Revert "Upstream multiple AOSP changes (#1221)" (#1225)

This reverts commit a9a7e6d91f70433701b7bd531245dc0fae377602.
diff --git a/android/src/main/java/org/conscrypt/Platform.java b/android/src/main/java/org/conscrypt/Platform.java
index b212d4d..64c9769 100644
--- a/android/src/main/java/org/conscrypt/Platform.java
+++ b/android/src/main/java/org/conscrypt/Platform.java
@@ -60,8 +60,8 @@
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.StandardConstants;
 import javax.net.ssl.X509TrustManager;
-import org.conscrypt.ct.LogStore;
-import org.conscrypt.ct.Policy;
+import org.conscrypt.ct.CTLogStore;
+import org.conscrypt.ct.CTPolicy;
 import org.conscrypt.metrics.CipherSuite;
 import org.conscrypt.metrics.ConscryptStatsLog;
 import org.conscrypt.metrics.Protocol;
@@ -884,11 +884,11 @@
         return null;
     }
 
-    static LogStore newDefaultLogStore() {
+    static CTLogStore newDefaultLogStore() {
         return null;
     }
 
-    static Policy newDefaultPolicy() {
+    static CTPolicy newDefaultPolicy(CTLogStore logStore) {
         return null;
     }
 
@@ -955,12 +955,4 @@
     public static boolean isTlsV1Deprecated() {
         return true;
     }
-
-    public static boolean isTlsV1Filtered() {
-        return false;
-    }
-
-    public static boolean isTlsV1Supported() {
-        return false;
-    }
 }
diff --git a/common/src/main/java/org/conscrypt/ArrayUtils.java b/common/src/main/java/org/conscrypt/ArrayUtils.java
index 5b4d68f..d946713 100644
--- a/common/src/main/java/org/conscrypt/ArrayUtils.java
+++ b/common/src/main/java/org/conscrypt/ArrayUtils.java
@@ -74,11 +74,4 @@
         }
         return result;
     }
-
-    /**
-     * Checks if given array is null or has zero elements.
-     */
-    public static <T> boolean isEmpty(T[] array) {
-        return array == null || array.length == 0;
-    }
 }
diff --git a/common/src/main/java/org/conscrypt/ByteArray.java b/common/src/main/java/org/conscrypt/ByteArray.java
index 3e97eb5..bfc544f 100644
--- a/common/src/main/java/org/conscrypt/ByteArray.java
+++ b/common/src/main/java/org/conscrypt/ByteArray.java
@@ -21,12 +21,11 @@
 /**
  * Byte array wrapper for hashtable use. Implements equals() and hashCode().
  */
-@Internal
-public final class ByteArray {
+final class ByteArray {
     private final byte[] bytes;
     private final int hashCode;
 
-    public ByteArray(byte[] bytes) {
+    ByteArray(byte[] bytes) {
         this.bytes = bytes;
         this.hashCode = Arrays.hashCode(bytes);
     }
@@ -38,9 +37,6 @@
 
     @Override
     public boolean equals(Object o) {
-        if (o == this) {
-            return true;
-        }
         if (!(o instanceof ByteArray)) {
             return false;
         }
diff --git a/common/src/main/java/org/conscrypt/NativeCrypto.java b/common/src/main/java/org/conscrypt/NativeCrypto.java
index 33f44fa..1f7005f 100644
--- a/common/src/main/java/org/conscrypt/NativeCrypto.java
+++ b/common/src/main/java/org/conscrypt/NativeCrypto.java
@@ -789,8 +789,8 @@
     // --- SSL handling --------------------------------------------------------
 
     static final String OBSOLETE_PROTOCOL_SSLV3 = "SSLv3";
-    static final String DEPRECATED_PROTOCOL_TLSV1 = "TLSv1";
-    static final String DEPRECATED_PROTOCOL_TLSV1_1 = "TLSv1.1";
+    private static final String DEPRECATED_PROTOCOL_TLSV1 = "TLSv1";
+    private static final String DEPRECATED_PROTOCOL_TLSV1_1 = "TLSv1.1";
     private static final String SUPPORTED_PROTOCOL_TLSV1_2 = "TLSv1.2";
     static final String SUPPORTED_PROTOCOL_TLSV1_3 = "TLSv1.3";
 
diff --git a/common/src/main/java/org/conscrypt/NativeSsl.java b/common/src/main/java/org/conscrypt/NativeSsl.java
index 7d260bc..79369ca 100644
--- a/common/src/main/java/org/conscrypt/NativeSsl.java
+++ b/common/src/main/java/org/conscrypt/NativeSsl.java
@@ -308,10 +308,8 @@
 
         if (parameters.getEnabledProtocols().length == 0 && parameters.isEnabledProtocolsFiltered) {
             throw new SSLHandshakeException("No enabled protocols; "
-                    + NativeCrypto.OBSOLETE_PROTOCOL_SSLV3 + ", "
-                    + NativeCrypto.DEPRECATED_PROTOCOL_TLSV1
-                    + " and " + NativeCrypto.DEPRECATED_PROTOCOL_TLSV1_1
-                    + " are no longer supported and were filtered from the list");
+                    + NativeCrypto.OBSOLETE_PROTOCOL_SSLV3
+                    + " is no longer supported and was filtered from the list");
         }
         NativeCrypto.setEnabledProtocols(ssl, this, parameters.enabledProtocols);
         NativeCrypto.setEnabledCipherSuites(
diff --git a/common/src/main/java/org/conscrypt/OpenSSLKey.java b/common/src/main/java/org/conscrypt/OpenSSLKey.java
index 4249b8e..e5e81f7 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLKey.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLKey.java
@@ -32,8 +32,7 @@
 /**
  * Represents a BoringSSL {@code EVP_PKEY}.
  */
-@Internal
-public final class OpenSSLKey {
+final class OpenSSLKey {
     private final NativeRef.EVP_PKEY ctx;
 
     private final boolean wrapped;
@@ -256,7 +255,7 @@
      *
      * @throws InvalidKeyException if parsing fails
      */
-    public static OpenSSLKey fromPublicKeyPemInputStream(InputStream is)
+    static OpenSSLKey fromPublicKeyPemInputStream(InputStream is)
             throws InvalidKeyException {
         OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
         try {
@@ -273,7 +272,7 @@
         }
     }
 
-    public PublicKey getPublicKey() throws NoSuchAlgorithmException {
+    PublicKey getPublicKey() throws NoSuchAlgorithmException {
         switch (NativeCrypto.EVP_PKEY_type(ctx)) {
             case NativeConstants.EVP_PKEY_RSA:
                 return new OpenSSLRSAPublicKey(this);
diff --git a/common/src/main/java/org/conscrypt/SSLParametersImpl.java b/common/src/main/java/org/conscrypt/SSLParametersImpl.java
index 58e1c08..c38da79 100644
--- a/common/src/main/java/org/conscrypt/SSLParametersImpl.java
+++ b/common/src/main/java/org/conscrypt/SSLParametersImpl.java
@@ -144,20 +144,8 @@
         }
 
         // initialize the list of cipher suites and protocols enabled by default
-        if (protocols == null) {
-          enabledProtocols = NativeCrypto.getDefaultProtocols().clone();
-        } else {
-            String[] filteredProtocols =
-                    filterFromProtocols(protocols, Arrays.asList(!Platform.isTlsV1Filtered()
-                        ? new String[0]
-                        : new String[] {
-                            NativeCrypto.OBSOLETE_PROTOCOL_SSLV3,
-                            NativeCrypto.DEPRECATED_PROTOCOL_TLSV1,
-                            NativeCrypto.DEPRECATED_PROTOCOL_TLSV1_1,
-                        }));
-            isEnabledProtocolsFiltered = protocols.length != filteredProtocols.length;
-            enabledProtocols = NativeCrypto.checkEnabledProtocols(filteredProtocols).clone();
-        }
+        enabledProtocols = NativeCrypto.checkEnabledProtocols(
+                protocols == null ? NativeCrypto.getDefaultProtocols() : protocols).clone();
         boolean x509CipherSuitesNeeded = (x509KeyManager != null) || (x509TrustManager != null);
         boolean pskCipherSuitesNeeded = pskKeyManager != null;
         enabledCipherSuites = getDefaultCipherSuites(
@@ -294,13 +282,7 @@
             throw new IllegalArgumentException("protocols == null");
         }
         String[] filteredProtocols =
-                filterFromProtocols(protocols, Arrays.asList(!Platform.isTlsV1Filtered()
-                    ? new String[0]
-                    : new String[] {
-                        NativeCrypto.OBSOLETE_PROTOCOL_SSLV3,
-                        NativeCrypto.DEPRECATED_PROTOCOL_TLSV1,
-                        NativeCrypto.DEPRECATED_PROTOCOL_TLSV1_1,
-                    }));
+                filterFromProtocols(protocols, NativeCrypto.OBSOLETE_PROTOCOL_SSLV3);
         isEnabledProtocolsFiltered = protocols.length != filteredProtocols.length;
         enabledProtocols = NativeCrypto.checkEnabledProtocols(filteredProtocols).clone();
     }
diff --git a/common/src/main/java/org/conscrypt/TrustManagerImpl.java b/common/src/main/java/org/conscrypt/TrustManagerImpl.java
index 400228d..1bacf7e 100644
--- a/common/src/main/java/org/conscrypt/TrustManagerImpl.java
+++ b/common/src/main/java/org/conscrypt/TrustManagerImpl.java
@@ -34,12 +34,6 @@
 
 package org.conscrypt;
 
-import org.conscrypt.ct.LogStore;
-import org.conscrypt.ct.Policy;
-import org.conscrypt.ct.PolicyCompliance;
-import org.conscrypt.ct.VerificationResult;
-import org.conscrypt.ct.Verifier;
-
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.Socket;
@@ -69,13 +63,16 @@
 import java.util.List;
 import java.util.Set;
 import java.util.logging.Logger;
-
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLParameters;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.X509ExtendedTrustManager;
+import org.conscrypt.ct.CTLogStore;
+import org.conscrypt.ct.CTPolicy;
+import org.conscrypt.ct.CTVerificationResult;
+import org.conscrypt.ct.CTVerifier;
 
 /**
  *
@@ -142,9 +139,8 @@
     private final Exception err;
     private final CertificateFactory factory;
     private final CertBlocklist blocklist;
-    private LogStore ctLogStore;
-    private Verifier ctVerifier;
-    private Policy ctPolicy;
+    private CTVerifier ctVerifier;
+    private CTPolicy ctPolicy;
 
     private ConscryptHostnameVerifier hostnameVerifier;
 
@@ -167,16 +163,18 @@
         this(keyStore, manager, certStore, null);
     }
 
-    public TrustManagerImpl(KeyStore keyStore, CertPinManager manager, ConscryptCertStore certStore,
-            CertBlocklist blocklist) {
+    public TrustManagerImpl(KeyStore keyStore, CertPinManager manager,
+            ConscryptCertStore certStore,
+                            CertBlocklist blocklist) {
         this(keyStore, manager, certStore, blocklist, null, null, null);
     }
 
     /**
      * For testing only.
      */
-    public TrustManagerImpl(KeyStore keyStore, CertPinManager manager, ConscryptCertStore certStore,
-            CertBlocklist blocklist, LogStore ctLogStore, Verifier ctVerifier, Policy ctPolicy) {
+    public TrustManagerImpl(KeyStore keyStore, CertPinManager manager,
+                            ConscryptCertStore certStore, CertBlocklist blocklist, CTLogStore ctLogStore,
+                            CTVerifier ctVerifier, CTPolicy ctPolicy) {
         CertPathValidator validatorLocal = null;
         CertificateFactory factoryLocal = null;
         KeyStore rootKeyStoreLocal = null;
@@ -216,7 +214,7 @@
         }
 
         if (ctPolicy == null) {
-            ctPolicy = Platform.newDefaultPolicy();
+            ctPolicy = Platform.newDefaultPolicy(ctLogStore);
         }
 
         this.pinManager = manager;
@@ -229,10 +227,8 @@
         this.acceptedIssuers = acceptedIssuersLocal;
         this.err = errLocal;
         this.blocklist = blocklist;
-        this.ctLogStore = ctLogStore;
-        this.ctVerifier = new Verifier(ctLogStore);
+        this.ctVerifier = new CTVerifier(ctLogStore);
         this.ctPolicy = ctPolicy;
-        ctLogStore.setPolicy(ctPolicy);
     }
 
     @SuppressWarnings("JdkObsolete")  // KeyStore#aliases is the only API available
@@ -684,7 +680,7 @@
             if (!clientAuth &&
                     (ctEnabledOverride || (host != null && Platform
                             .isCTVerificationRequired(host)))) {
-                checkCT(wholeChain, ocspData, tlsSctData);
+                checkCT(host, wholeChain, ocspData, tlsSctData);
             }
 
             if (untrustedChain.isEmpty()) {
@@ -730,23 +726,15 @@
         }
     }
 
-    private void checkCT(List<X509Certificate> chain, byte[] ocspData, byte[] tlsData)
+    private void checkCT(String host, List<X509Certificate> chain, byte[] ocspData, byte[] tlsData)
             throws CertificateException {
-        if (ctLogStore.getState() != LogStore.State.COMPLIANT) {
-            /* Fail open. For some reason, the LogStore is not usable. It could
-             * be because there is no log list available or that the log list
-             * is too old (according to the policy). */
-            return;
-        }
-        VerificationResult result =
+        CTVerificationResult result =
                 ctVerifier.verifySignedCertificateTimestamps(chain, tlsData, ocspData);
 
-        X509Certificate leaf = chain.get(0);
-        PolicyCompliance compliance = ctPolicy.doesResultConformToPolicy(result, leaf);
-        if (compliance != PolicyCompliance.COMPLY) {
+        if (!ctPolicy.doesResultConformToPolicy(result, host,
+                    chain.toArray(new X509Certificate[chain.size()]))) {
             throw new CertificateException(
-                    "Certificate chain does not conform to required transparency policy: "
-                    + compliance.name());
+                    "Certificate chain does not conform to required transparency policy.");
         }
     }
 
@@ -1037,12 +1025,12 @@
     }
 
     // Replace the CTVerifier. For testing only.
-    public void setCTVerifier(Verifier verifier) {
+    public void setCTVerifier(CTVerifier verifier) {
         this.ctVerifier = verifier;
     }
 
     // Replace the CTPolicy. For testing only.
-    public void setCTPolicy(Policy policy) {
+    public void setCTPolicy(CTPolicy policy) {
         this.ctPolicy = policy;
     }
 }
diff --git a/common/src/main/java/org/conscrypt/ct/Constants.java b/common/src/main/java/org/conscrypt/ct/CTConstants.java
similarity index 98%
rename from common/src/main/java/org/conscrypt/ct/Constants.java
rename to common/src/main/java/org/conscrypt/ct/CTConstants.java
index 71bcab2..76133d9 100644
--- a/common/src/main/java/org/conscrypt/ct/Constants.java
+++ b/common/src/main/java/org/conscrypt/ct/CTConstants.java
@@ -19,7 +19,7 @@
 import org.conscrypt.Internal;
 
 @Internal
-public class Constants {
+public class CTConstants {
     public static final String X509_SCT_LIST_OID = "1.3.6.1.4.1.11129.2.4.2";
     public static final String OCSP_SCT_LIST_OID = "1.3.6.1.4.1.11129.2.4.5";
 
@@ -41,3 +41,4 @@
 
     public static final int ISSUER_KEY_HASH_LENGTH = 32;
 }
+
diff --git a/common/src/main/java/org/conscrypt/ct/CTLogInfo.java b/common/src/main/java/org/conscrypt/ct/CTLogInfo.java
new file mode 100644
index 0000000..c2e312a
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/ct/CTLogInfo.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt.ct;
+
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.util.Arrays;
+import org.conscrypt.Internal;
+
+/**
+ * Properties about a Certificate Transparency Log.
+ * This object stores information about a CT log, its public key, description and URL.
+ * It allows verification of SCTs against the log's public key.
+ */
+@Internal
+public class CTLogInfo {
+    private final byte[] logId;
+    private final PublicKey publicKey;
+    private final String description;
+    private final String url;
+
+    public CTLogInfo(PublicKey publicKey, String description, String url) {
+        try {
+            this.logId = MessageDigest.getInstance("SHA-256")
+                .digest(publicKey.getEncoded());
+        } catch (NoSuchAlgorithmException e) {
+            // SHA-256 is guaranteed to be available
+            throw new RuntimeException(e);
+        }
+
+        this.publicKey = publicKey;
+        this.description = description;
+        this.url = url;
+    }
+
+    /**
+     * Get the log's ID, that is the SHA-256 hash of it's public key
+     */
+    public byte[] getID() {
+        return logId;
+    }
+
+    public PublicKey getPublicKey() {
+        return publicKey;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof CTLogInfo)) {
+            return false;
+        }
+
+        CTLogInfo that = (CTLogInfo)other;
+        return
+            this.publicKey.equals(that.publicKey) &&
+            this.description.equals(that.description) &&
+            this.url.equals(that.url);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 1;
+        hash = hash * 31 + publicKey.hashCode();
+        hash = hash * 31 + description.hashCode();
+        hash = hash * 31 + url.hashCode();
+
+        return hash;
+    }
+
+    /**
+     * Verify the signature of a signed certificate timestamp for the given certificate entry
+     * against the log's public key.
+     *
+     * @return the result of the verification
+     */
+    public VerifiedSCT.Status verifySingleSCT(SignedCertificateTimestamp sct,
+                                              CertificateEntry entry) {
+        if (!Arrays.equals(sct.getLogID(), getID())) {
+            return VerifiedSCT.Status.UNKNOWN_LOG;
+        }
+
+        byte[] toVerify;
+        try {
+            toVerify = sct.encodeTBS(entry);
+        } catch (SerializationException e) {
+            return VerifiedSCT.Status.INVALID_SCT;
+        }
+
+        Signature signature;
+        try {
+            String algorithm = sct.getSignature().getAlgorithm();
+            signature = Signature.getInstance(algorithm);
+        } catch (NoSuchAlgorithmException e) {
+            return VerifiedSCT.Status.INVALID_SCT;
+        }
+
+        try {
+            signature.initVerify(publicKey);
+        } catch (InvalidKeyException e) {
+            return VerifiedSCT.Status.INVALID_SCT;
+        }
+
+        try {
+            signature.update(toVerify);
+            if (!signature.verify(sct.getSignature().getSignature())) {
+                return VerifiedSCT.Status.INVALID_SIGNATURE;
+            }
+            return VerifiedSCT.Status.VALID;
+        } catch (SignatureException e) {
+            // This only happens if the signature is not initialized,
+            // but we call initVerify just before, so it should never do
+            throw new RuntimeException(e);
+        }
+    }
+}
+
diff --git a/common/src/main/java/org/conscrypt/ct/PolicyCompliance.java b/common/src/main/java/org/conscrypt/ct/CTLogStore.java
similarity index 81%
rename from common/src/main/java/org/conscrypt/ct/PolicyCompliance.java
rename to common/src/main/java/org/conscrypt/ct/CTLogStore.java
index d889ee7..bf30d66 100644
--- a/common/src/main/java/org/conscrypt/ct/PolicyCompliance.java
+++ b/common/src/main/java/org/conscrypt/ct/CTLogStore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,8 +19,7 @@
 import org.conscrypt.Internal;
 
 @Internal
-public enum PolicyCompliance {
-    COMPLY,
-    NOT_ENOUGH_SCTS,
-    NOT_ENOUGH_DIVERSE_SCTS
+public interface CTLogStore {
+    CTLogInfo getKnownLog(byte[] logId);
 }
+
diff --git a/common/src/main/java/org/conscrypt/ct/Policy.java b/common/src/main/java/org/conscrypt/ct/CTPolicy.java
similarity index 81%
rename from common/src/main/java/org/conscrypt/ct/Policy.java
rename to common/src/main/java/org/conscrypt/ct/CTPolicy.java
index 5b3d95a..455cabd 100644
--- a/common/src/main/java/org/conscrypt/ct/Policy.java
+++ b/common/src/main/java/org/conscrypt/ct/CTPolicy.java
@@ -20,7 +20,7 @@
 import org.conscrypt.Internal;
 
 @Internal
-public interface Policy {
-    boolean isLogStoreCompliant(LogStore store);
-    PolicyCompliance doesResultConformToPolicy(VerificationResult result, X509Certificate leaf);
+public interface CTPolicy {
+    boolean doesResultConformToPolicy(CTVerificationResult result, String hostname,
+            X509Certificate[] chain);
 }
diff --git a/common/src/main/java/org/conscrypt/ct/VerificationResult.java b/common/src/main/java/org/conscrypt/ct/CTVerificationResult.java
similarity index 75%
rename from common/src/main/java/org/conscrypt/ct/VerificationResult.java
rename to common/src/main/java/org/conscrypt/ct/CTVerificationResult.java
index 354b16a..b21e9ac 100644
--- a/common/src/main/java/org/conscrypt/ct/VerificationResult.java
+++ b/common/src/main/java/org/conscrypt/ct/CTVerificationResult.java
@@ -21,21 +21,13 @@
 import java.util.List;
 import org.conscrypt.Internal;
 
-/**
- * Container for verified SignedCertificateTimestamp.
- *
- * getValidSCTs returns SCTs which were found to match a known log and for
- * which the signature has been verified. There is no guarantee on the state of
- * the log (e.g., getLogInfo.getState() may return STATE_UNKNOWN). Further
- * verification on the compliance with the policy is performed in PolicyImpl.
- */
 @Internal
-public class VerificationResult {
+public class CTVerificationResult {
     private final ArrayList<VerifiedSCT> validSCTs = new ArrayList<VerifiedSCT>();
     private final ArrayList<VerifiedSCT> invalidSCTs = new ArrayList<VerifiedSCT>();
 
     public void add(VerifiedSCT result) {
-        if (result.isValid()) {
+        if (result.status == VerifiedSCT.Status.VALID) {
             validSCTs.add(result);
         } else {
             invalidSCTs.add(result);
@@ -50,3 +42,4 @@
         return Collections.unmodifiableList(invalidSCTs);
     }
 }
+
diff --git a/common/src/main/java/org/conscrypt/ct/Verifier.java b/common/src/main/java/org/conscrypt/ct/CTVerifier.java
similarity index 73%
rename from common/src/main/java/org/conscrypt/ct/Verifier.java
rename to common/src/main/java/org/conscrypt/ct/CTVerifier.java
index 79d90d9..2f1f79b 100644
--- a/common/src/main/java/org/conscrypt/ct/Verifier.java
+++ b/common/src/main/java/org/conscrypt/ct/CTVerifier.java
@@ -27,18 +27,18 @@
 import org.conscrypt.OpenSSLX509Certificate;
 
 @Internal
-public class Verifier {
-    private final LogStore store;
+public class CTVerifier {
+    private final CTLogStore store;
 
-    public Verifier(LogStore store) {
+    public CTVerifier(CTLogStore store) {
         this.store = store;
     }
 
-    public VerificationResult verifySignedCertificateTimestamps(List<X509Certificate> chain,
+    public CTVerificationResult verifySignedCertificateTimestamps(List<X509Certificate> chain,
             byte[] tlsData, byte[] ocspData) throws CertificateEncodingException {
         OpenSSLX509Certificate[] certs = new OpenSSLX509Certificate[chain.size()];
         int i = 0;
-        for (X509Certificate cert : chain) {
+        for(X509Certificate cert : chain) {
             certs[i++] = OpenSSLX509Certificate.fromCertificate(cert);
         }
         return verifySignedCertificateTimestamps(certs, tlsData, ocspData);
@@ -50,7 +50,7 @@
      * response, and verified against the list of known logs.
      * @throws IllegalArgumentException if the chain is empty
      */
-    public VerificationResult verifySignedCertificateTimestamps(OpenSSLX509Certificate[] chain,
+    public CTVerificationResult verifySignedCertificateTimestamps(OpenSSLX509Certificate[] chain,
             byte[] tlsData, byte[] ocspData) throws CertificateEncodingException {
         if (chain.length == 0) {
             throw new IllegalArgumentException("Chain of certificates mustn't be empty.");
@@ -58,7 +58,7 @@
 
         OpenSSLX509Certificate leaf = chain[0];
 
-        VerificationResult result = new VerificationResult();
+        CTVerificationResult result = new CTVerificationResult();
         List<SignedCertificateTimestamp> tlsScts = getSCTsFromTLSExtension(tlsData);
         verifyExternalSCTs(tlsScts, leaf, result);
 
@@ -75,7 +75,8 @@
      * The result of the verification for each sct is added to {@code result}.
      */
     private void verifyEmbeddedSCTs(List<SignedCertificateTimestamp> scts,
-            OpenSSLX509Certificate[] chain, VerificationResult result) {
+                                    OpenSSLX509Certificate[] chain,
+                                    CTVerificationResult result) {
         // Avoid creating the cert entry if we don't need it
         if (scts.isEmpty()) {
             return;
@@ -98,7 +99,10 @@
             return;
         }
 
-        verifySCTs(scts, precertEntry, result);
+        for (SignedCertificateTimestamp sct: scts) {
+            VerifiedSCT.Status status = verifySingleSCT(sct, precertEntry);
+            result.add(new VerifiedSCT(sct, status));
+        }
     }
 
     /**
@@ -107,7 +111,8 @@
      * The result of the verification for each sct is added to {@code result}.
      */
     private void verifyExternalSCTs(List<SignedCertificateTimestamp> scts,
-            OpenSSLX509Certificate leaf, VerificationResult result) {
+                                    OpenSSLX509Certificate leaf,
+                                    CTVerificationResult result) {
         // Avoid creating the cert entry if we don't need it
         if (scts.isEmpty()) {
             return;
@@ -121,38 +126,32 @@
             return;
         }
 
-        verifySCTs(scts, x509Entry, result);
+        for (SignedCertificateTimestamp sct: scts) {
+            VerifiedSCT.Status status = verifySingleSCT(sct, x509Entry);
+            result.add(new VerifiedSCT(sct, status));
+        }
     }
 
     /**
-     * Verify a list of SCTs.
+     * Verify a single SCT for the given Certificate Entry
      */
-    private void verifySCTs(List<SignedCertificateTimestamp> scts, CertificateEntry certEntry,
-            VerificationResult result) {
-        for (SignedCertificateTimestamp sct : scts) {
-            VerifiedSCT.Builder builder = new VerifiedSCT.Builder(sct);
-            LogInfo log = store.getKnownLog(sct.getLogID());
-            if (log == null) {
-                builder.setStatus(VerifiedSCT.Status.UNKNOWN_LOG);
-            } else {
-                VerifiedSCT.Status status = log.verifySingleSCT(sct, certEntry);
-                builder.setStatus(status);
-                if (status == VerifiedSCT.Status.VALID) {
-                    builder.setLogInfo(log);
-                }
-            }
-            result.add(builder.build());
+    private VerifiedSCT.Status verifySingleSCT(SignedCertificateTimestamp sct,
+                                                         CertificateEntry certEntry) {
+        CTLogInfo log = store.getKnownLog(sct.getLogID());
+        if (log == null) {
+            return VerifiedSCT.Status.UNKNOWN_LOG;
         }
+
+        return log.verifySingleSCT(sct, certEntry);
     }
 
     /**
      * Add every SCT in {@code scts} to {@code result} with INVALID_SCT as status
      */
-    private void markSCTsAsInvalid(
-            List<SignedCertificateTimestamp> scts, VerificationResult result) {
-        for (SignedCertificateTimestamp sct : scts) {
-            VerifiedSCT.Builder builder = new VerifiedSCT.Builder(sct);
-            result.add(builder.setStatus(VerifiedSCT.Status.INVALID_SCT).build());
+    private void markSCTsAsInvalid(List<SignedCertificateTimestamp> scts,
+                                   CTVerificationResult result) {
+        for (SignedCertificateTimestamp sct: scts) {
+            result.add(new VerifiedSCT(sct, VerifiedSCT.Status.INVALID_SCT));
         }
     }
 
@@ -164,25 +163,24 @@
      * @param origin used to create the SignedCertificateTimestamp instances.
      */
     @SuppressWarnings("MixedMutabilityReturnType")
-    private static List<SignedCertificateTimestamp> getSCTsFromSCTList(
-            byte[] data, SignedCertificateTimestamp.Origin origin) {
+    private static List<SignedCertificateTimestamp> getSCTsFromSCTList(byte[] data,
+            SignedCertificateTimestamp.Origin origin) {
         if (data == null) {
             return Collections.emptyList();
         }
 
         byte[][] sctList;
         try {
-            sctList = Serialization.readList(
-                    data, Constants.SCT_LIST_LENGTH_BYTES, Constants.SERIALIZED_SCT_LENGTH_BYTES);
+            sctList = Serialization.readList(data, CTConstants.SCT_LIST_LENGTH_BYTES,
+                                             CTConstants.SERIALIZED_SCT_LENGTH_BYTES);
         } catch (SerializationException e) {
             return Collections.emptyList();
         }
 
         List<SignedCertificateTimestamp> scts = new ArrayList<SignedCertificateTimestamp>();
-        for (byte[] encodedSCT : sctList) {
-            try {
-                SignedCertificateTimestamp sct =
-                        SignedCertificateTimestamp.decode(encodedSCT, origin);
+        for (byte[] encodedSCT: sctList) {
+            try  {
+                SignedCertificateTimestamp sct = SignedCertificateTimestamp.decode(encodedSCT, origin);
                 scts.add(sct);
             } catch (SerializationException e) {
                 // Ignore errors
@@ -212,21 +210,23 @@
      *              issuer in order to identify the relevant SingleResponse from the OCSP response,
      *              or an empty list is returned
      */
-    private List<SignedCertificateTimestamp> getSCTsFromOCSPResponse(
-            byte[] data, OpenSSLX509Certificate[] chain) {
+    private List<SignedCertificateTimestamp> getSCTsFromOCSPResponse(byte[] data,
+            OpenSSLX509Certificate[] chain) {
         if (data == null || chain.length < 2) {
             return Collections.emptyList();
         }
 
-        byte[] extData = NativeCrypto.get_ocsp_single_extension(data, Constants.OCSP_SCT_LIST_OID,
-                chain[0].getContext(), chain[0], chain[1].getContext(), chain[1]);
+        byte[] extData = NativeCrypto.get_ocsp_single_extension(data, CTConstants.OCSP_SCT_LIST_OID,
+                chain[0].getContext(), chain[0],
+                chain[1].getContext(), chain[1]);
         if (extData == null) {
             return Collections.emptyList();
         }
 
         try {
             return getSCTsFromSCTList(
-                    Serialization.readDEROctetString(Serialization.readDEROctetString(extData)),
+                    Serialization.readDEROctetString(
+                      Serialization.readDEROctetString(extData)),
                     SignedCertificateTimestamp.Origin.OCSP_RESPONSE);
         } catch (SerializationException e) {
             return Collections.emptyList();
@@ -240,17 +240,19 @@
      * to be parsed, an empty list is returned. Individual SCTs which fail to be parsed are ignored.
      */
     private List<SignedCertificateTimestamp> getSCTsFromX509Extension(OpenSSLX509Certificate leaf) {
-        byte[] extData = leaf.getExtensionValue(Constants.X509_SCT_LIST_OID);
+        byte[] extData = leaf.getExtensionValue(CTConstants.X509_SCT_LIST_OID);
         if (extData == null) {
             return Collections.emptyList();
         }
 
         try {
             return getSCTsFromSCTList(
-                    Serialization.readDEROctetString(Serialization.readDEROctetString(extData)),
+                    Serialization.readDEROctetString(
+                      Serialization.readDEROctetString(extData)),
                     SignedCertificateTimestamp.Origin.EMBEDDED);
         } catch (SerializationException e) {
             return Collections.emptyList();
         }
     }
 }
+
diff --git a/common/src/main/java/org/conscrypt/ct/CertificateEntry.java b/common/src/main/java/org/conscrypt/ct/CertificateEntry.java
index 137ded1..72ed530 100644
--- a/common/src/main/java/org/conscrypt/ct/CertificateEntry.java
+++ b/common/src/main/java/org/conscrypt/ct/CertificateEntry.java
@@ -61,8 +61,8 @@
         } else if (entryType == LogEntryType.X509_ENTRY && issuerKeyHash != null) {
             throw new IllegalArgumentException("unexpected issuerKeyHash for X509 entry.");
         }
-
-        if (issuerKeyHash != null && issuerKeyHash.length != Constants.ISSUER_KEY_HASH_LENGTH) {
+        
+        if (issuerKeyHash != null && issuerKeyHash.length != CTConstants.ISSUER_KEY_HASH_LENGTH) {
             throw new IllegalArgumentException("issuerKeyHash must be 32 bytes long");
         }
 
@@ -83,11 +83,11 @@
     public static CertificateEntry createForPrecertificate(OpenSSLX509Certificate leaf,
             OpenSSLX509Certificate issuer) throws CertificateException {
         try {
-            if (!leaf.getNonCriticalExtensionOIDs().contains(Constants.X509_SCT_LIST_OID)) {
+            if (!leaf.getNonCriticalExtensionOIDs().contains(CTConstants.X509_SCT_LIST_OID)) {
                 throw new CertificateException("Certificate does not contain embedded signed timestamps");
             }
 
-            byte[] tbs = leaf.getTBSCertificateWithoutExtension(Constants.X509_SCT_LIST_OID);
+            byte[] tbs = leaf.getTBSCertificateWithoutExtension(CTConstants.X509_SCT_LIST_OID);
 
             byte[] issuerKey = issuer.getPublicKey().getEncoded();
             MessageDigest md = MessageDigest.getInstance("SHA-256");
@@ -124,11 +124,11 @@
      * TLS encode the CertificateEntry structure.
      */
     public void encode(OutputStream output) throws SerializationException {
-        Serialization.writeNumber(output, entryType.ordinal(), Constants.LOG_ENTRY_TYPE_LENGTH);
+        Serialization.writeNumber(output, entryType.ordinal(), CTConstants.LOG_ENTRY_TYPE_LENGTH);
         if (entryType == LogEntryType.PRECERT_ENTRY) {
             Serialization.writeFixedBytes(output, issuerKeyHash);
         }
-        Serialization.writeVariableBytes(output, certificate, Constants.CERTIFICATE_LENGTH_BYTES);
+        Serialization.writeVariableBytes(output, certificate, CTConstants.CERTIFICATE_LENGTH_BYTES);
     }
 }
 
diff --git a/common/src/main/java/org/conscrypt/ct/DigitallySigned.java b/common/src/main/java/org/conscrypt/ct/DigitallySigned.java
index 15720d9..b5f4478 100644
--- a/common/src/main/java/org/conscrypt/ct/DigitallySigned.java
+++ b/common/src/main/java/org/conscrypt/ct/DigitallySigned.java
@@ -107,9 +107,10 @@
         throws SerializationException {
         try {
             return new DigitallySigned(
-                    Serialization.readNumber(input, Constants.HASH_ALGORITHM_LENGTH),
-                    Serialization.readNumber(input, Constants.SIGNATURE_ALGORITHM_LENGTH),
-                    Serialization.readVariableBytes(input, Constants.SIGNATURE_LENGTH_BYTES));
+                Serialization.readNumber(input, CTConstants.HASH_ALGORITHM_LENGTH),
+                Serialization.readNumber(input, CTConstants.SIGNATURE_ALGORITHM_LENGTH),
+                Serialization.readVariableBytes(input, CTConstants.SIGNATURE_LENGTH_BYTES)
+            );
         } catch (IllegalArgumentException e) {
             throw new SerializationException(e);
         }
diff --git a/common/src/main/java/org/conscrypt/ct/LogInfo.java b/common/src/main/java/org/conscrypt/ct/LogInfo.java
deleted file mode 100644
index 99c8139..0000000
--- a/common/src/main/java/org/conscrypt/ct/LogInfo.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.conscrypt.ct;
-
-import java.security.InvalidKeyException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.util.Arrays;
-import java.util.Objects;
-import org.conscrypt.Internal;
-
-/**
- * Properties about a Certificate Transparency Log.
- * This object stores information about a CT log, its public key, description and URL.
- * It allows verification of SCTs against the log's public key.
- */
-@Internal
-public class LogInfo {
-    public static final int STATE_UNKNOWN = 0;
-    public static final int STATE_PENDING = 1;
-    public static final int STATE_QUALIFIED = 2;
-    public static final int STATE_USABLE = 3;
-    public static final int STATE_READONLY = 4;
-    public static final int STATE_RETIRED = 5;
-    public static final int STATE_REJECTED = 6;
-
-    private final byte[] logId;
-    private final PublicKey publicKey;
-    private final int state;
-    private final long stateTimestamp;
-    private final String description;
-    private final String url;
-    private final String operator;
-
-    private LogInfo(Builder builder) {
-        /* Based on the required fields for the log list schema v3. Notably,
-         * the state may be absent. The logId must match the public key, this
-         * is validated in the builder. */
-        Objects.requireNonNull(builder.logId);
-        Objects.requireNonNull(builder.publicKey);
-        Objects.requireNonNull(builder.url);
-        Objects.requireNonNull(builder.operator);
-
-        this.logId = builder.logId;
-        this.publicKey = builder.publicKey;
-        this.state = builder.state;
-        this.stateTimestamp = builder.stateTimestamp;
-        this.description = builder.description;
-        this.url = builder.url;
-        this.operator = builder.operator;
-    }
-
-    public static class Builder {
-        private byte[] logId;
-        private PublicKey publicKey;
-        private int state;
-        private long stateTimestamp;
-        private String description;
-        private String url;
-        private String operator;
-
-        public Builder setPublicKey(PublicKey publicKey) {
-            Objects.requireNonNull(publicKey);
-            this.publicKey = publicKey;
-            try {
-                this.logId = MessageDigest.getInstance("SHA-256").digest(publicKey.getEncoded());
-            } catch (NoSuchAlgorithmException e) {
-                // SHA-256 is guaranteed to be available
-                throw new RuntimeException(e);
-            }
-            return this;
-        }
-
-        public Builder setState(int state, long timestamp) {
-            if (state < 0 || state > STATE_REJECTED) {
-                throw new IllegalArgumentException("invalid state value");
-            }
-            this.state = state;
-            this.stateTimestamp = timestamp;
-            return this;
-        }
-
-        public Builder setDescription(String description) {
-            Objects.requireNonNull(description);
-            this.description = description;
-            return this;
-        }
-
-        public Builder setUrl(String url) {
-            Objects.requireNonNull(url);
-            this.url = url;
-            return this;
-        }
-
-        public Builder setOperator(String operator) {
-            Objects.requireNonNull(operator);
-            this.operator = operator;
-            return this;
-        }
-
-        public LogInfo build() {
-            return new LogInfo(this);
-        }
-    }
-
-    /**
-     * Get the log's ID, that is the SHA-256 hash of it's public key
-     */
-    public byte[] getID() {
-        return logId;
-    }
-
-    public PublicKey getPublicKey() {
-        return publicKey;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public String getUrl() {
-        return url;
-    }
-
-    public int getState() {
-        return state;
-    }
-
-    public int getStateAt(long when) {
-        if (when >= this.stateTimestamp) {
-            return state;
-        }
-        return STATE_UNKNOWN;
-    }
-
-    public long getStateTimestamp() {
-        return stateTimestamp;
-    }
-
-    public String getOperator() {
-        return operator;
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        if (this == other) {
-            return true;
-        }
-        if (!(other instanceof LogInfo)) {
-            return false;
-        }
-
-        LogInfo that = (LogInfo) other;
-        return this.state == that.state && this.description.equals(that.description)
-                && this.url.equals(that.url) && this.operator.equals(that.operator)
-                && this.stateTimestamp == that.stateTimestamp
-                && Arrays.equals(this.logId, that.logId);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(
-                Arrays.hashCode(logId), description, url, state, stateTimestamp, operator);
-    }
-
-    /**
-     * Verify the signature of a signed certificate timestamp for the given certificate entry
-     * against the log's public key.
-     *
-     * @return the result of the verification
-     */
-    public VerifiedSCT.Status verifySingleSCT(
-            SignedCertificateTimestamp sct, CertificateEntry entry) {
-        if (!Arrays.equals(sct.getLogID(), getID())) {
-            return VerifiedSCT.Status.UNKNOWN_LOG;
-        }
-
-        byte[] toVerify;
-        try {
-            toVerify = sct.encodeTBS(entry);
-        } catch (SerializationException e) {
-            return VerifiedSCT.Status.INVALID_SCT;
-        }
-
-        Signature signature;
-        try {
-            String algorithm = sct.getSignature().getAlgorithm();
-            signature = Signature.getInstance(algorithm);
-        } catch (NoSuchAlgorithmException e) {
-            return VerifiedSCT.Status.INVALID_SCT;
-        }
-
-        try {
-            signature.initVerify(publicKey);
-        } catch (InvalidKeyException e) {
-            return VerifiedSCT.Status.INVALID_SCT;
-        }
-
-        try {
-            signature.update(toVerify);
-            if (!signature.verify(sct.getSignature().getSignature())) {
-                return VerifiedSCT.Status.INVALID_SIGNATURE;
-            }
-            return VerifiedSCT.Status.VALID;
-        } catch (SignatureException e) {
-            // This only happens if the signature is not initialized,
-            // but we call initVerify just before, so it should never do
-            throw new RuntimeException(e);
-        }
-    }
-}
diff --git a/common/src/main/java/org/conscrypt/ct/LogStore.java b/common/src/main/java/org/conscrypt/ct/LogStore.java
deleted file mode 100644
index 10e099c..0000000
--- a/common/src/main/java/org/conscrypt/ct/LogStore.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.conscrypt.ct;
-
-import org.conscrypt.Internal;
-
-@Internal
-public interface LogStore {
-    public enum State {
-        UNINITIALIZED,
-        NOT_FOUND,
-        MALFORMED,
-        LOADED,
-        COMPLIANT,
-        NON_COMPLIANT,
-    }
-
-    void setPolicy(Policy policy);
-
-    State getState();
-
-    long getTimestamp();
-
-    LogInfo getKnownLog(byte[] logId);
-}
diff --git a/common/src/main/java/org/conscrypt/ct/SignedCertificateTimestamp.java b/common/src/main/java/org/conscrypt/ct/SignedCertificateTimestamp.java
index 8ad3788..d23f9ed 100644
--- a/common/src/main/java/org/conscrypt/ct/SignedCertificateTimestamp.java
+++ b/common/src/main/java/org/conscrypt/ct/SignedCertificateTimestamp.java
@@ -87,16 +87,19 @@
      */
     public static SignedCertificateTimestamp decode(InputStream input, Origin origin)
             throws SerializationException {
-        int version = Serialization.readNumber(input, Constants.VERSION_LENGTH);
+        int version = Serialization.readNumber(input, CTConstants.VERSION_LENGTH);
         if (version != Version.V1.ordinal()) {
             throw new SerializationException("Unsupported SCT version " + version);
         }
 
-        return new SignedCertificateTimestamp(Version.V1,
-                Serialization.readFixedBytes(input, Constants.LOGID_LENGTH),
-                Serialization.readLong(input, Constants.TIMESTAMP_LENGTH),
-                Serialization.readVariableBytes(input, Constants.EXTENSIONS_LENGTH_BYTES),
-                DigitallySigned.decode(input), origin);
+        return new SignedCertificateTimestamp(
+            Version.V1,
+            Serialization.readFixedBytes(input, CTConstants.LOGID_LENGTH),
+            Serialization.readLong(input, CTConstants.TIMESTAMP_LENGTH),
+            Serialization.readVariableBytes(input, CTConstants.EXTENSIONS_LENGTH_BYTES),
+            DigitallySigned.decode(input),
+            origin
+        );
     }
 
     /**
@@ -112,12 +115,12 @@
      */
     public void encodeTBS(OutputStream output, CertificateEntry certEntry)
             throws SerializationException {
-        Serialization.writeNumber(output, version.ordinal(), Constants.VERSION_LENGTH);
+        Serialization.writeNumber(output, version.ordinal(), CTConstants.VERSION_LENGTH);
         Serialization.writeNumber(output, SignatureType.CERTIFICATE_TIMESTAMP.ordinal(),
-                Constants.SIGNATURE_TYPE_LENGTH);
-        Serialization.writeNumber(output, timestamp, Constants.TIMESTAMP_LENGTH);
+                                          CTConstants.SIGNATURE_TYPE_LENGTH);
+        Serialization.writeNumber(output, timestamp, CTConstants.TIMESTAMP_LENGTH);
         certEntry.encode(output);
-        Serialization.writeVariableBytes(output, extensions, Constants.EXTENSIONS_LENGTH_BYTES);
+        Serialization.writeVariableBytes(output, extensions, CTConstants.EXTENSIONS_LENGTH_BYTES);
     }
 
     /**
diff --git a/common/src/main/java/org/conscrypt/ct/VerifiedSCT.java b/common/src/main/java/org/conscrypt/ct/VerifiedSCT.java
index 6c9c008..7eaf45d 100644
--- a/common/src/main/java/org/conscrypt/ct/VerifiedSCT.java
+++ b/common/src/main/java/org/conscrypt/ct/VerifiedSCT.java
@@ -16,7 +16,6 @@
 
 package org.conscrypt.ct;
 
-import java.util.Objects;
 import org.conscrypt.Internal;
 
 /**
@@ -31,61 +30,12 @@
         INVALID_SCT
     }
 
-    private final SignedCertificateTimestamp sct;
-    private final Status status;
-    private final LogInfo logInfo;
+    public final SignedCertificateTimestamp sct;
+    public final Status status;
 
-    private VerifiedSCT(Builder builder) {
-        Objects.requireNonNull(builder.sct);
-        Objects.requireNonNull(builder.status);
-        if (builder.status == Status.VALID) {
-            Objects.requireNonNull(builder.logInfo);
-        }
-
-        this.sct = builder.sct;
-        this.status = builder.status;
-        this.logInfo = builder.logInfo;
-    }
-
-    public SignedCertificateTimestamp getSct() {
-        return sct;
-    }
-
-    public Status getStatus() {
-        return status;
-    }
-
-    public boolean isValid() {
-        return status == Status.VALID;
-    }
-
-    public LogInfo getLogInfo() {
-        return logInfo;
-    }
-
-    public static class Builder {
-        private SignedCertificateTimestamp sct;
-        private Status status;
-        private LogInfo logInfo;
-
-        public Builder(SignedCertificateTimestamp sct) {
-            this.sct = sct;
-        }
-
-        public Builder setStatus(Status status) {
-            this.status = status;
-            return this;
-        }
-
-        public Builder setLogInfo(LogInfo logInfo) {
-            Objects.requireNonNull(logInfo);
-            this.logInfo = logInfo;
-            return this;
-        }
-
-        public VerifiedSCT build() {
-            return new VerifiedSCT(this);
-        }
+    public VerifiedSCT(SignedCertificateTimestamp sct, Status status) {
+        this.sct = sct;
+        this.status = status;
     }
 }
 
diff --git a/common/src/test/java/org/conscrypt/ct/VerifierTest.java b/common/src/test/java/org/conscrypt/ct/CTVerifierTest.java
similarity index 64%
rename from common/src/test/java/org/conscrypt/ct/VerifierTest.java
rename to common/src/test/java/org/conscrypt/ct/CTVerifierTest.java
index e7b94ba..9aaf8db 100644
--- a/common/src/test/java/org/conscrypt/ct/VerifierTest.java
+++ b/common/src/test/java/org/conscrypt/ct/CTVerifierTest.java
@@ -30,44 +30,25 @@
 import org.junit.runners.JUnit4;
 
 @RunWith(JUnit4.class)
-public class VerifierTest {
+public class CTVerifierTest {
     private OpenSSLX509Certificate ca;
     private OpenSSLX509Certificate cert;
     private OpenSSLX509Certificate certEmbedded;
-    private Verifier ctVerifier;
+    private CTVerifier ctVerifier;
 
     @Before
     public void setUp() throws Exception {
         ca = OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("ca-cert.pem"));
         cert = OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("cert.pem"));
-        certEmbedded =
-                OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("cert-ct-embedded.pem"));
+        certEmbedded = OpenSSLX509Certificate.fromX509PemInputStream(
+                openTestFile("cert-ct-embedded.pem"));
 
         PublicKey key = TestUtils.readPublicKeyPemFile("ct-server-key-public.pem");
 
-        final LogInfo log = new LogInfo.Builder()
-                                    .setPublicKey(key)
-                                    .setDescription("Test Log")
-                                    .setUrl("http://example.com")
-                                    .setOperator("LogOperator")
-                                    .setState(LogInfo.STATE_USABLE, 1643709600000L)
-                                    .build();
-        LogStore store = new LogStore() {
+        final CTLogInfo log = new CTLogInfo(key, "Test Log", "foo");
+        CTLogStore store = new CTLogStore() {
             @Override
-            public void setPolicy(Policy policy) {}
-
-            @Override
-            public State getState() {
-                return LogStore.State.COMPLIANT;
-            }
-
-            @Override
-            public long getTimestamp() {
-                return 0;
-            }
-
-            @Override
-            public LogInfo getKnownLog(byte[] logId) {
+            public CTLogInfo getKnownLog(byte[] logId) {
                 if (Arrays.equals(logId, log.getID())) {
                     return log;
                 } else {
@@ -76,116 +57,120 @@
             }
         };
 
-        ctVerifier = new Verifier(store);
+        ctVerifier = new CTVerifier(store);
     }
 
     @Test
     public void test_verifySignedCertificateTimestamps_withOCSPResponse() throws Exception {
-        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca};
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
 
         byte[] ocspResponse = readTestFile("ocsp-response.der");
-        VerificationResult result =
-                ctVerifier.verifySignedCertificateTimestamps(chain, null, ocspResponse);
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, null, ocspResponse);
         assertEquals(1, result.getValidSCTs().size());
         assertEquals(0, result.getInvalidSCTs().size());
     }
 
     @Test
     public void test_verifySignedCertificateTimestamps_withTLSExtension() throws Exception {
-        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca};
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
 
         byte[] tlsExtension = readTestFile("ct-signed-timestamp-list");
-        VerificationResult result =
-                ctVerifier.verifySignedCertificateTimestamps(chain, tlsExtension, null);
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, tlsExtension, null);
         assertEquals(1, result.getValidSCTs().size());
         assertEquals(0, result.getInvalidSCTs().size());
     }
 
     @Test
     public void test_verifySignedCertificateTimestamps_withEmbeddedExtension() throws Exception {
-        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {certEmbedded, ca};
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { certEmbedded, ca };
 
-        VerificationResult result = ctVerifier.verifySignedCertificateTimestamps(chain, null, null);
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, null, null);
         assertEquals(1, result.getValidSCTs().size());
         assertEquals(0, result.getInvalidSCTs().size());
     }
 
     @Test
     public void test_verifySignedCertificateTimestamps_withoutTimestamp() throws Exception {
-        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca};
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
 
-        VerificationResult result = ctVerifier.verifySignedCertificateTimestamps(chain, null, null);
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, null, null);
         assertEquals(0, result.getValidSCTs().size());
         assertEquals(0, result.getInvalidSCTs().size());
     }
 
     @Test
     public void test_verifySignedCertificateTimestamps_withInvalidSignature() throws Exception {
-        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca};
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
 
         byte[] tlsExtension = readTestFile("ct-signed-timestamp-list-invalid");
 
-        VerificationResult result =
-                ctVerifier.verifySignedCertificateTimestamps(chain, tlsExtension, null);
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, tlsExtension, null);
         assertEquals(0, result.getValidSCTs().size());
         assertEquals(1, result.getInvalidSCTs().size());
-        assertEquals(
-                VerifiedSCT.Status.INVALID_SIGNATURE, result.getInvalidSCTs().get(0).getStatus());
+        assertEquals(VerifiedSCT.Status.INVALID_SIGNATURE,
+                     result.getInvalidSCTs().get(0).status);
     }
 
     @Test
     public void test_verifySignedCertificateTimestamps_withUnknownLog() throws Exception {
-        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca};
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
 
         byte[] tlsExtension = readTestFile("ct-signed-timestamp-list-unknown");
 
-        VerificationResult result =
-                ctVerifier.verifySignedCertificateTimestamps(chain, tlsExtension, null);
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, tlsExtension, null);
         assertEquals(0, result.getValidSCTs().size());
         assertEquals(1, result.getInvalidSCTs().size());
-        assertEquals(VerifiedSCT.Status.UNKNOWN_LOG, result.getInvalidSCTs().get(0).getStatus());
+        assertEquals(VerifiedSCT.Status.UNKNOWN_LOG,
+                     result.getInvalidSCTs().get(0).status);
     }
 
     @Test
     public void test_verifySignedCertificateTimestamps_withInvalidEncoding() throws Exception {
-        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca};
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
 
         // Just some garbage data which will fail to deserialize
-        byte[] tlsExtension = new byte[] {1, 2, 3, 4};
+        byte[] tlsExtension = new byte[] { 1, 2, 3, 4 };
 
-        VerificationResult result =
-                ctVerifier.verifySignedCertificateTimestamps(chain, tlsExtension, null);
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, tlsExtension, null);
         assertEquals(0, result.getValidSCTs().size());
         assertEquals(0, result.getInvalidSCTs().size());
     }
 
     @Test
     public void test_verifySignedCertificateTimestamps_withInvalidOCSPResponse() throws Exception {
-        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca};
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
 
         // Just some garbage data which will fail to deserialize
-        byte[] ocspResponse = new byte[] {1, 2, 3, 4};
+        byte[] ocspResponse = new byte[] { 1, 2, 3, 4 };
 
-        VerificationResult result =
-                ctVerifier.verifySignedCertificateTimestamps(chain, null, ocspResponse);
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, null, ocspResponse);
         assertEquals(0, result.getValidSCTs().size());
         assertEquals(0, result.getInvalidSCTs().size());
     }
 
     @Test
     public void test_verifySignedCertificateTimestamps_withMultipleTimestamps() throws Exception {
-        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca};
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
 
         byte[] tlsExtension = readTestFile("ct-signed-timestamp-list-invalid");
         byte[] ocspResponse = readTestFile("ocsp-response.der");
 
-        VerificationResult result =
-                ctVerifier.verifySignedCertificateTimestamps(chain, tlsExtension, ocspResponse);
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, tlsExtension, ocspResponse);
         assertEquals(1, result.getValidSCTs().size());
         assertEquals(1, result.getInvalidSCTs().size());
         assertEquals(SignedCertificateTimestamp.Origin.OCSP_RESPONSE,
-                result.getValidSCTs().get(0).getSct().getOrigin());
+                     result.getValidSCTs().get(0).sct.getOrigin());
         assertEquals(SignedCertificateTimestamp.Origin.TLS_EXTENSION,
-                result.getInvalidSCTs().get(0).getSct().getOrigin());
+                     result.getInvalidSCTs().get(0).sct.getOrigin());
     }
 }
+
diff --git a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLContextTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLContextTest.java
index f24d864..40acd1b 100644
--- a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLContextTest.java
+++ b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLContextTest.java
@@ -119,16 +119,6 @@
     }
 
     @Test
-    public void test_SSLContext_allProtocols() throws Exception {
-        SSLConfigurationAsserts.assertSSLContextDefaultConfiguration(SSLContext.getDefault());
-
-        for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS_ALL) {
-            SSLContext sslContext = SSLContext.getInstance(protocol);
-            sslContext.init(null, null, null);
-        }
-    }
-
-    @Test
     public void test_SSLContext_pskOnlyConfiguration_defaultProviderOnly() throws Exception {
         // Test the scenario where only a PSKKeyManager is provided and no TrustManagers are
         // provided.
diff --git a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java
index 24277ad..5ce4d5f 100644
--- a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java
+++ b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java
@@ -16,16 +16,10 @@
 
 package org.conscrypt.javax.net.ssl;
 
-import libcore.junit.util.SwitchTargetSdkVersionRule;
-import libcore.junit.util.SwitchTargetSdkVersionRule.TargetSdkVersion;
-
 import static org.conscrypt.TestUtils.osName;
 import static org.conscrypt.TestUtils.isOsx;
 import static org.conscrypt.TestUtils.isLinux;
 import static org.conscrypt.TestUtils.isWindows;
-import static org.conscrypt.TestUtils.isTlsV1Deprecated;
-import static org.conscrypt.TestUtils.isTlsV1Filtered;
-import static org.conscrypt.TestUtils.isTlsV1Supported;
 import static org.conscrypt.TestUtils.UTF_8;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -115,9 +109,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import tests.net.DelegatingSSLSocketFactory;
@@ -131,9 +123,6 @@
 @RunWith(Parameterized.class)
 public class SSLSocketVersionCompatibilityTest {
 
-    @Rule
-    public TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance();
-
     @Parameterized.Parameters(name = "{index}: {0} client, {1} server")
     public static Iterable<Object[]> data() {
         return Arrays.asList(new Object[][] {
@@ -1701,7 +1690,7 @@
     @Test
     public void test_SSLSocket_ClientHello_ALPN() throws Exception {
         final String[] protocolList = new String[] { "h2", "http/1.1" };
-
+        
         ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
             @Override
             public void run(SSLSocketFactory sslSocketFactory) throws Exception {
@@ -1937,35 +1926,7 @@
     }
 
     @Test
-    public void test_SSLSocket_TLSv1Supported() throws Exception {
-        assumeTrue(isTlsV1Supported());
-        TestSSLContext context = new TestSSLContext.Builder()
-                .clientProtocol(clientVersion)
-                .serverProtocol(serverVersion)
-                .build();
-        final SSLSocket client =
-                (SSLSocket) context.clientContext.getSocketFactory().createSocket();
-        client.setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1"});
-        assertEquals(2, client.getEnabledProtocols().length);
-    }
-
-    @TargetSdkVersion(35)
-    @Test
-    public void test_SSLSocket_SSLv3Unsupported_35() throws Exception {
-        assumeFalse(isTlsV1Filtered());
-        TestSSLContext context = new TestSSLContext.Builder()
-                .clientProtocol(clientVersion)
-                .serverProtocol(serverVersion)
-                .build();
-        final SSLSocket client =
-                (SSLSocket) context.clientContext.getSocketFactory().createSocket();
-        assertThrows(IllegalArgumentException.class, () -> client.setEnabledProtocols(new String[] {"SSLv3"}));
-        assertThrows(IllegalArgumentException.class, () -> client.setEnabledProtocols(new String[] {"SSL"}));
-    }
-
-    @TargetSdkVersion(34)
-    @Test
-    public void test_SSLSocket_SSLv3Unsupported_34() throws Exception {
+    public void test_SSLSocket_SSLv3Unsupported() throws Exception {
         TestSSLContext context = new TestSSLContext.Builder()
                 .clientProtocol(clientVersion)
                 .serverProtocol(serverVersion)
@@ -1983,40 +1944,6 @@
         }
     }
 
-    @TargetSdkVersion(34)
-    @Test
-    public void test_TLSv1Filtered_34() throws Exception {
-        TestSSLContext context = new TestSSLContext.Builder()
-                .clientProtocol(clientVersion)
-                .serverProtocol(serverVersion)
-                .build();
-        final SSLSocket client =
-                (SSLSocket) context.clientContext.getSocketFactory().createSocket();
-        client.setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"});
-        assertEquals(1, client.getEnabledProtocols().length);
-        assertEquals("TLSv1.2", client.getEnabledProtocols()[0]);
-    }
-
-    @TargetSdkVersion(35)
-    @Test
-    public void test_TLSv1Filtered_35() throws Exception {
-        assumeFalse(isTlsV1Filtered());
-        TestSSLContext context = new TestSSLContext.Builder()
-                .clientProtocol(clientVersion)
-                .serverProtocol(serverVersion)
-                .build();
-        final SSLSocket client =
-                (SSLSocket) context.clientContext.getSocketFactory().createSocket();
-        assertThrows(IllegalArgumentException.class, () ->
-            client.setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"}));
-    }
-
-    @Test
-    public void test_TLSv1Unsupported_notEnabled() throws Exception {
-        assumeTrue(!isTlsV1Supported());
-        assertTrue(isTlsV1Deprecated());
-    }
-
     // Under some circumstances, the file descriptor socket may get finalized but still
     // be reused by the JDK's built-in HTTP connection reuse code.  Ensure that a
     // SocketException is thrown if that happens.
diff --git a/libcore-stub/src/main/java/libcore/net/NetworkSecurityPolicy.java b/libcore-stub/src/main/java/libcore/net/NetworkSecurityPolicy.java
deleted file mode 100644
index e7ca0f1..0000000
--- a/libcore-stub/src/main/java/libcore/net/NetworkSecurityPolicy.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.net;
-
-/**
- * Network security policy for this process/application.
- *
- * <p>Network stacks/components are expected to honor this policy. Components which can use the
- * Android framework API should be accessing this policy via the framework's
- * {@code android.security.NetworkSecurityPolicy} instead of via this class.
- *
- * <p>The policy currently consists of a single flag: whether cleartext network traffic is
- * permitted. See {@link #isCleartextTrafficPermitted()}.
- */
-public abstract class NetworkSecurityPolicy {
-    private static volatile NetworkSecurityPolicy instance = new DefaultNetworkSecurityPolicy();
-
-    public static NetworkSecurityPolicy getInstance() {
-        return instance;
-    }
-
-    public static void setInstance(NetworkSecurityPolicy policy) {
-        if (policy == null) {
-            throw new NullPointerException("policy == null");
-        }
-        instance = policy;
-    }
-
-    /**
-     * Returns {@code true} if cleartext network traffic (e.g. HTTP, FTP, XMPP, IMAP, SMTP --
-     * without TLS or STARTTLS) is permitted for all network communications of this process.
-     *
-     * <p>{@link #isCleartextTrafficPermitted(String)} should be used to determine if cleartext
-     * traffic is permitted for a specific host.
-     *
-     * <p>When cleartext network traffic is not permitted, the platform's components (e.g. HTTP
-     * stacks, {@code WebView}, {@code MediaPlayer}) will refuse this process's requests to use
-     * cleartext traffic. Third-party libraries are encouraged to do the same.
-     *
-     * <p>This flag is honored on a best effort basis because it's impossible to prevent all
-     * cleartext traffic from an application given the level of access provided to applications on
-     * Android. For example, there's no expectation that {@link java.net.Socket} API will honor this
-     * flag. Luckily, most network traffic from apps is handled by higher-level network stacks which
-     * can be made to honor this flag. Platform-provided network stacks (e.g. HTTP and FTP) honor
-     * this flag from day one, and well-established third-party network stacks will eventually
-     * honor it.
-     */
-    public abstract boolean isCleartextTrafficPermitted();
-
-    /**
-     * Returns {@code true} if cleartext network traffic (e.g. HTTP, FTP, XMPP, IMAP, SMTP --
-     * without TLS or STARTTLS) is permitted for communicating with {@code hostname} for this
-     * process.
-     *
-     * <p>See {@link #isCleartextTrafficPermitted} for more details.
-     */
-    public abstract boolean isCleartextTrafficPermitted(String hostname);
-
-    /**
-     * Returns {@code true} if Certificate Transparency information is required to be presented by
-     * the server and verified by the client in TLS connections to {@code hostname}.
-     *
-     * <p>See RFC6962 section 3.3 for more details.
-     */
-    public abstract boolean isCertificateTransparencyVerificationRequired(String hostname);
-
-    public static final class DefaultNetworkSecurityPolicy extends NetworkSecurityPolicy {
-        @Override
-        public boolean isCleartextTrafficPermitted() {
-            return true;
-        }
-
-        @Override
-        public boolean isCleartextTrafficPermitted(String hostname) {
-            return isCleartextTrafficPermitted();
-        }
-
-        @Override
-        public boolean isCertificateTransparencyVerificationRequired(String hostname) {
-            return false;
-        }
-    }
-}
diff --git a/openjdk/src/main/java/org/conscrypt/Platform.java b/openjdk/src/main/java/org/conscrypt/Platform.java
index 78e2236..f5770ca 100644
--- a/openjdk/src/main/java/org/conscrypt/Platform.java
+++ b/openjdk/src/main/java/org/conscrypt/Platform.java
@@ -78,9 +78,8 @@
 import javax.net.ssl.TrustManagerFactory;
 import javax.net.ssl.X509ExtendedTrustManager;
 import javax.net.ssl.X509TrustManager;
-import org.conscrypt.ct.LogStore;
-import org.conscrypt.ct.Policy;
-import sun.security.x509.AlgorithmId;
+import org.conscrypt.ct.CTLogStore;
+import org.conscrypt.ct.CTPolicy;
 
 /**
  * Platform-specific methods for OpenJDK.
@@ -719,11 +718,11 @@
         return null;
     }
 
-    static LogStore newDefaultLogStore() {
+    static CTLogStore newDefaultLogStore() {
         return null;
     }
 
-    static Policy newDefaultPolicy() {
+    static CTPolicy newDefaultPolicy(CTLogStore logStore) {
         return null;
     }
 
@@ -814,12 +813,4 @@
     public static boolean isTlsV1Deprecated() {
         return true;
     }
-
-    public static boolean isTlsV1Filtered() {
-        return false;
-    }
-
-    public static boolean isTlsV1Supported() {
-        return false;
-    }
 }
diff --git a/openjdk/src/test/java/org/conscrypt/ConscryptOpenJdkSuite.java b/openjdk/src/test/java/org/conscrypt/ConscryptOpenJdkSuite.java
index f5ae14b..d5401e3 100644
--- a/openjdk/src/test/java/org/conscrypt/ConscryptOpenJdkSuite.java
+++ b/openjdk/src/test/java/org/conscrypt/ConscryptOpenJdkSuite.java
@@ -18,8 +18,8 @@
 
 import static org.conscrypt.TestUtils.installConscryptAsDefaultProvider;
 
+import org.conscrypt.ct.CTVerifierTest;
 import org.conscrypt.ct.SerializationTest;
-import org.conscrypt.ct.VerifierTest;
 import org.conscrypt.java.security.AlgorithmParameterGeneratorTestDH;
 import org.conscrypt.java.security.AlgorithmParameterGeneratorTestDSA;
 import org.conscrypt.java.security.AlgorithmParametersPSSTest;
@@ -111,7 +111,7 @@
         TestSessionBuilderTest.class,
         TrustManagerImplTest.class,
         // org.conscrypt.ct tests
-        VerifierTest.class,
+        CTVerifierTest.class,
         SerializationTest.class,
         // java.security tests
         CertificateFactoryTest.class,
diff --git a/openjdk/src/test/resources/test_blocklist_ca2.pem b/openjdk/src/test/resources/test_blocklist_ca2.pem
deleted file mode 100644
index ca33594..0000000
--- a/openjdk/src/test/resources/test_blocklist_ca2.pem
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDHTCCAgWgAwIBAgIUY4GMRArmOpdsPFvMChsC64re26kwDQYJKoZIhvcNAQEL
-BQAwHjEcMBoGA1UEAwwTYmxhY2tsaXN0IHRlc3QgQ0EgMjAeFw0yNDA2MDcwNTAx
-NTJaFw0zNDA2MDUwNTAxNTJaMB4xHDAaBgNVBAMME2JsYWNrbGlzdCB0ZXN0IENB
-IDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZiCGg9O8L4drk5R0J
-AwFvnm4aS2NZXnD0xUwE4iK0Nhw0Fb5A/yd4aW1RL8gCqcYXQywHrQAZq+PBUnG9
-ArtRoOTugkSD7gRFPFi7xcq6tdjVinkG33BUqSoyZb584sAwLfuKmG4WgGF6Bm2g
-vTh4kzvvqzJmWiDMHAxJCUiE+OQ3kBKoGUqplyn97Td3ZKs6mi1lAYV5xAu5M3EK
-fFC6g4Ckvy/5ZMLrROwz2bTtMQEsIRAeFRWWQJlDKF4RbceFmhDavwMSHCZu9ZGM
-HPAapXg+3LdQM+uqiQoguItY9dv1SJbWlcsxl5NzqDkPLfZeIhMTEYk/PtMKIsLk
-7aUvAgMBAAGjUzBRMB0GA1UdDgQWBBQyrTPvjxNE7FvnFRmD50u9H+o5nTAfBgNV
-HSMEGDAWgBQyrTPvjxNE7FvnFRmD50u9H+o5nTAPBgNVHRMBAf8EBTADAQH/MA0G
-CSqGSIb3DQEBCwUAA4IBAQBOAdrI1ycTfwL/PMUCXAlwbosKFyDESgGgbfE0blHc
-MkT0AY4ODxU2+oEM+UTwFJ/3ZC88hlJdhqL4spwC1lTCP8DpydxzR+KN8iEUDQT6
-MeXjmll7qUzk5f+/xF3BCp+5ZqympYNPT3vwf25k308aT6/LNrWCvQYxPJJue1aS
-kPQU6y3ktgsdXmSL696fGi/VcuNmJKjDJn+po7NS+F07Addnf5t1v9iwNMI2OwiG
-LzZ/3wiQNU03KlM6fiy++e0VIxrxShg1cYMlKQanzFo3cFkigT5JNDHTA2gVV0Kz
-7bEMyIVMLL59ky3bVdxVCJdi5brhgqV1C8Wly0bgm555
------END CERTIFICATE-----
diff --git a/openjdk/src/test/resources/test_blocklist_ca2_key.pem b/openjdk/src/test/resources/test_blocklist_ca2_key.pem
deleted file mode 100644
index f8a4bb5..0000000
--- a/openjdk/src/test/resources/test_blocklist_ca2_key.pem
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZiCGg9O8L4drk
-5R0JAwFvnm4aS2NZXnD0xUwE4iK0Nhw0Fb5A/yd4aW1RL8gCqcYXQywHrQAZq+PB
-UnG9ArtRoOTugkSD7gRFPFi7xcq6tdjVinkG33BUqSoyZb584sAwLfuKmG4WgGF6
-Bm2gvTh4kzvvqzJmWiDMHAxJCUiE+OQ3kBKoGUqplyn97Td3ZKs6mi1lAYV5xAu5
-M3EKfFC6g4Ckvy/5ZMLrROwz2bTtMQEsIRAeFRWWQJlDKF4RbceFmhDavwMSHCZu
-9ZGMHPAapXg+3LdQM+uqiQoguItY9dv1SJbWlcsxl5NzqDkPLfZeIhMTEYk/PtMK
-IsLk7aUvAgMBAAECggEAP2Jww8MrJ4QseyBNvukzQBIv0YI7N2uihaMokcGMY0sN
-lME/RRUyBee8nm50DAlsQzFTra2SI4cP5cG0PDyy+e3LZd55C+CJec4Csi7j1fZ6
-WRqsgZZgiUs3pQvVOzjf8GQje6IXnQmOdLLPsrM766eZcIaErbXa4XlY5xRCkMaO
-RavPwvbAFKKKCjndJs5OZpwZwR/UcHMlYLR3Dg+ozzlG48dMr0ao/3bimGPsc8/t
-eXzlVq2vi8xdRm53MdzarH9VWTYg3AalKEedLxxfvyGaZrpviCZdftHvH+sRruCy
-D280CSlmwLFc5xVGQTN9zNolEWarxU4fcusgmyfXrQKBgQDVqbrSOBDKF1WlyT/J
-vsCCRywoTBVDxvB0BeoC3ICHh5KISbtfjqy/CR4qLGnuov0ZXVR5UmGHs3gl21qm
-HjX1IRWo+8bfaoxBqK1zcFd4XtkPNPtEAcQvH0MFjnBXd0WcC/7qzvUUIC2RGuWY
-YmxCj4uFdSTLwLwiTDwXdAy7HQKBgQC39C8Ygu7MlTO9EpyQdoOSbQC+VgaPPABO
-LgQLoGH5K7sNOjutHo3Bt3iKFsdPkTHkZc3alBhH+61zgbsmGFrNJI+XRgkeGpLR
-/6Bbi7ivswsJcHmodOKOr62RKMJArwqOqN2MhNlZTBDjgIy4owN2B0YHwd9GTQWu
-JOjfwHwjuwKBgQCpxjtPjQMyQcZpfHc2LF81Za5dus7u0yX/Wy+t5F4w0vYJW2UK
-sgjrpygT5MSrvVEVlYZo/J/Ivz+J/TmTY9AGHqriYmWM41HdXlWss6idWehp3/SD
-/k9QDiwoPx1fMsPaEeIV3Cr7OfJbKZ8kLZjObtczTXjWeihDrIXXMPxotQKBgQC3
-mO1QZ43zfo7PDL5aqQ6UnFp7ndyaJOahIOhEumROjsj4YMCi/rW5PGcAW8+9qErF
-jJ4ypFC/t3/cowSo9vHZgb4W233KH/edxKbF9+Py6J4BY9LowRBGHSz8jlOiv5Gn
-5P6KeyV7LKJGjkzlEz4nFQdeQq+XuNQMhSYv/CtqdQKBgEQKDQRiKgl4DjbDWNjV
-3EUFH/LBvwPUXnrfeECXuyiTrTHrUsTtahcrjTQs/ByX15YSmyVhbUSsYQ6dkafZ
-aVNl9zNLQYYa2y3VbBs0q5xEW+iCmc4w/LsW+mnUiFmKlYVokwGVEDkqMyDkk3oB
-58f2hIryzy4MDkh0Iqixr6o1
------END PRIVATE KEY-----
diff --git a/platform/src/main/java/org/conscrypt/CertBlocklistImpl.java b/platform/src/main/java/org/conscrypt/CertBlocklistImpl.java
index 305b74b..2428d4c 100644
--- a/platform/src/main/java/org/conscrypt/CertBlocklistImpl.java
+++ b/platform/src/main/java/org/conscrypt/CertBlocklistImpl.java
@@ -24,16 +24,14 @@
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.math.BigInteger;
+import java.security.GeneralSecurityException;
 import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
 import java.security.PublicKey;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -43,35 +41,14 @@
     private static final Logger logger = Logger.getLogger(CertBlocklistImpl.class.getName());
 
     private final Set<BigInteger> serialBlocklist;
-    private final Set<ByteArray> sha1PubkeyBlocklist;
-    private final Set<ByteArray> sha256PubkeyBlocklist;
-    private Map<ByteArray, Boolean> cache;
-
-    /**
-     * Number of entries in the cache. The cache contains public keys which are
-     * at most 4096 bits (512 bytes) for RSA. For a cache size of 64, that is
-     * at most 512 * 64 = 32,768 bytes.
-     */
-    private static final int CACHE_SIZE = 64;
+    private final Set<ByteString> pubkeyBlocklist;
 
     /**
      * public for testing only.
      */
-    public CertBlocklistImpl(Set<BigInteger> serialBlocklist, Set<ByteArray> sha1PubkeyBlocklist) {
-        this(serialBlocklist, sha1PubkeyBlocklist, Collections.emptySet());
-    }
-
-    public CertBlocklistImpl(Set<BigInteger> serialBlocklist, Set<ByteArray> sha1PubkeyBlocklist,
-            Set<ByteArray> sha256PubkeyBlocklist) {
-        this.cache = Collections.synchronizedMap(new LinkedHashMap<ByteArray, Boolean>() {
-            @Override
-            protected boolean removeEldestEntry(Map.Entry<ByteArray, Boolean> eldest) {
-                return size() > CACHE_SIZE;
-            }
-        });
+    public CertBlocklistImpl(Set<BigInteger> serialBlocklist, Set<ByteString> pubkeyBlocklist) {
         this.serialBlocklist = serialBlocklist;
-        this.sha1PubkeyBlocklist = sha1PubkeyBlocklist;
-        this.sha256PubkeyBlocklist = sha256PubkeyBlocklist;
+        this.pubkeyBlocklist = pubkeyBlocklist;
     }
 
     public static CertBlocklist getDefault() {
@@ -79,14 +56,10 @@
         String blocklistRoot = androidData + "/misc/keychain/";
         String defaultPubkeyBlocklistPath = blocklistRoot + "pubkey_blacklist.txt";
         String defaultSerialBlocklistPath = blocklistRoot + "serial_blacklist.txt";
-        String defaultPubkeySha256BlocklistPath = blocklistRoot + "pubkey_sha256_blocklist.txt";
 
-        Set<ByteArray> sha1PubkeyBlocklist =
-                readPublicKeyBlockList(defaultPubkeyBlocklistPath, "SHA-1");
-        Set<ByteArray> sha256PubkeyBlocklist =
-                readPublicKeyBlockList(defaultPubkeySha256BlocklistPath, "SHA-256");
+        Set<ByteString> pubkeyBlocklist = readPublicKeyBlockList(defaultPubkeyBlocklistPath);
         Set<BigInteger> serialBlocklist = readSerialBlockList(defaultSerialBlocklistPath);
-        return new CertBlocklistImpl(serialBlocklist, sha1PubkeyBlocklist, sha256PubkeyBlocklist);
+        return new CertBlocklistImpl(serialBlocklist, pubkeyBlocklist);
     }
 
     private static boolean isHex(String value) {
@@ -99,8 +72,8 @@
         }
     }
 
-    private static boolean isPubkeyHash(String value, int expectedHashLength) {
-        if (value.length() != expectedHashLength) {
+    private static boolean isPubkeyHash(String value) {
+        if (value.length() != 40) {
             logger.log(Level.WARNING, "Invalid pubkey hash length: " + value.length());
             return false;
         }
@@ -156,12 +129,32 @@
     }
 
     private static Set<BigInteger> readSerialBlockList(String path) {
-        /*
-         * Deprecated. Serials may inadvertently match a certificate that was
-         * issued not in compliance with the Baseline Requirements. Prefer
-         * using the certificate public key.
+
+        /* Start out with a base set of known bad values.
+         *
+         * WARNING: Do not add short serials to this list!
+         *
+         * Since this currently doesn't compare the serial + issuer, you
+         * should only add serials that have enough entropy here. Short
+         * serials may inadvertently match a certificate that was issued
+         * not in compliance with the Baseline Requirements.
          */
-        Set<BigInteger> bl = new HashSet<BigInteger>();
+        Set<BigInteger> bl = new HashSet<BigInteger>(Arrays.asList(
+            // From http://src.chromium.org/viewvc/chrome/trunk/src/net/base/x509_certificate.cc?revision=78748&view=markup
+            // Not a real certificate. For testing only.
+            new BigInteger("077a59bcd53459601ca6907267a6dd1c", 16),
+            new BigInteger("047ecbe9fca55f7bd09eae36e10cae1e", 16),
+            new BigInteger("d8f35f4eb7872b2dab0692e315382fb0", 16),
+            new BigInteger("b0b7133ed096f9b56fae91c874bd3ac0", 16),
+            new BigInteger("9239d5348f40d1695a745470e1f23f43", 16),
+            new BigInteger("e9028b9578e415dc1a710a2b88154447", 16),
+            new BigInteger("d7558fdaf5f1105bb213282b707729a3", 16),
+            new BigInteger("f5c86af36162f13a64f54f6dc9587c06", 16),
+            new BigInteger("392a434f0e07df1f8aa305de34e0c229", 16),
+            new BigInteger("3e75ced46b693021218830ae86a82a71", 16)
+        ));
+
+        // attempt to augment it with values taken from gservices
         String serialBlocklist = readBlocklist(path);
         if (!serialBlocklist.equals("")) {
             for (String value : serialBlocklist.split(",", -1)) {
@@ -177,13 +170,15 @@
         return Collections.unmodifiableSet(bl);
     }
 
-    static final byte[][] SHA1_BUILTINS = {
+    private static Set<ByteString> readPublicKeyBlockList(String path) {
+
+        // start out with a base set of known bad values
+        Set<ByteString> bl = new HashSet<ByteString>(toByteStrings(
             // Blocklist test cert for CTS. The cert and key can be found in
             // src/test/resources/blocklist_test_ca.pem and
             // src/test/resources/blocklist_test_ca_key.pem.
             "bae78e6bed65a2bf60ddedde7fd91e825865e93d".getBytes(UTF_8),
-            // From
-            // http://src.chromium.org/viewvc/chrome/branches/782/src/net/base/x509_certificate.cc?r1=98750&r2=98749&pathrev=98750
+            // From http://src.chromium.org/viewvc/chrome/branches/782/src/net/base/x509_certificate.cc?r1=98750&r2=98749&pathrev=98750
             // C=NL, O=DigiNotar, CN=DigiNotar Root CA/emailAddress=info@diginotar.nl
             "410f36363258f30b347d12ce4863e433437806a8".getBytes(UTF_8),
             // Subject: CN=DigiNotar Cyber CA
@@ -210,49 +205,16 @@
             "783333c9687df63377efceddd82efa9101913e8e".getBytes(UTF_8),
             // Subject: Subject: C=FR, O=DG Tr\xC3\xA9sor, CN=AC DG Tr\xC3\xA9sor SSL
             // Issuer: C=FR, O=DGTPE, CN=AC DGTPE Signature Authentification
-            "3ecf4bbbe46096d514bb539bb913d77aa4ef31bf".getBytes(UTF_8),
-    };
-
-    static final byte[][] SHA256_BUILTINS = {
-            // Blocklist test cert for CTS. The cert and key can be found in
-            // src/test/resources/blocklist_test_ca2.pem and
-            // src/test/resources/blocklist_test_ca2_key.pem.
-            "809964b15e9bd312993d9984045551f503f2cf8e68f39188921ba30fe623f9fd".getBytes(UTF_8),
-    };
-
-    private static Set<ByteArray> readPublicKeyBlockList(String path, String hashType) {
-        Set<ByteArray> bl;
-
-        switch (hashType) {
-            case "SHA-1":
-                bl = new HashSet<ByteArray>(toByteArrays(SHA1_BUILTINS));
-                break;
-            case "SHA-256":
-                bl = new HashSet<ByteArray>(toByteArrays(SHA256_BUILTINS));
-                break;
-            default:
-                throw new RuntimeException(
-                        "Unknown hashType: " + hashType + ". Expected SHA-1 or SHA-256");
-        }
-
-        MessageDigest md;
-        try {
-            md = MessageDigest.getInstance(hashType);
-        } catch (NoSuchAlgorithmException e) {
-            logger.log(Level.SEVERE, "Unable to get " + hashType + " MessageDigest", e);
-            return bl;
-        }
-        // The hashes are encoded with hexadecimal values. There should be
-        // twice as many characters as the digest length in bytes.
-        int hashLength = md.getDigestLength() * 2;
+            "3ecf4bbbe46096d514bb539bb913d77aa4ef31bf".getBytes(UTF_8)
+        ));
 
         // attempt to augment it with values taken from gservices
         String pubkeyBlocklist = readBlocklist(path);
         if (!pubkeyBlocklist.equals("")) {
             for (String value : pubkeyBlocklist.split(",", -1)) {
                 value = value.trim();
-                if (isPubkeyHash(value, hashLength)) {
-                    bl.add(new ByteArray(value.getBytes(UTF_8)));
+                if (isPubkeyHash(value)) {
+                    bl.add(new ByteString(value.getBytes(UTF_8)));
                 } else {
                     logger.log(Level.WARNING, "Tried to blocklist invalid pubkey " + value);
                 }
@@ -262,46 +224,22 @@
         return bl;
     }
 
-    private static boolean isPublicKeyBlockListed(
-            byte[] encodedPublicKey, Set<ByteArray> blocklist, String hashType) {
-        MessageDigest md;
-        try {
-            md = MessageDigest.getInstance(hashType);
-        } catch (NoSuchAlgorithmException e) {
-            logger.log(Level.SEVERE, "Unable to get " + hashType + " MessageDigest", e);
-            return false;
-        }
-        ByteArray out = new ByteArray(toHex(md.digest(encodedPublicKey)));
-        if (blocklist.contains(out)) {
-            return true;
-        }
-        return false;
-    }
-
     @Override
     public boolean isPublicKeyBlockListed(PublicKey publicKey) {
-        byte[] encodedPublicKey = publicKey.getEncoded();
-        // cacheKey is a view on encodedPublicKey. Because it is used as a key
-        // for a Map, its underlying array (encodedPublicKey) should not be
-        // modified.
-        ByteArray cacheKey = new ByteArray(encodedPublicKey);
-        Boolean cachedResult = cache.get(cacheKey);
-        if (cachedResult != null) {
-            return cachedResult.booleanValue();
+        byte[] encoded = publicKey.getEncoded();
+        MessageDigest md;
+        try {
+            md = MessageDigest.getInstance("SHA1");
+        } catch (GeneralSecurityException e) {
+            logger.log(Level.SEVERE, "Unable to get SHA1 MessageDigest", e);
+            return false;
         }
-        if (!sha1PubkeyBlocklist.isEmpty()) {
-            if (isPublicKeyBlockListed(encodedPublicKey, sha1PubkeyBlocklist, "SHA-1")) {
-                cache.put(cacheKey, true);
+        byte[] out = toHex(md.digest(encoded));
+        for (ByteString blocklisted : pubkeyBlocklist) {
+            if (Arrays.equals(blocklisted.bytes, out)) {
                 return true;
             }
         }
-        if (!sha256PubkeyBlocklist.isEmpty()) {
-            if (isPublicKeyBlockListed(encodedPublicKey, sha256PubkeyBlocklist, "SHA-256")) {
-                cache.put(cacheKey, true);
-                return true;
-            }
-        }
-        cache.put(cacheKey, false);
         return false;
     }
 
@@ -325,11 +263,37 @@
         return serialBlocklist.contains(serial);
     }
 
-    private static List<ByteArray> toByteArrays(byte[]... allBytes) {
-        List<ByteArray> byteArrays = new ArrayList<>(allBytes.length + 1);
+    private static List<ByteString> toByteStrings(byte[]... allBytes) {
+        List<ByteString> byteStrings = new ArrayList<>(allBytes.length + 1);
         for (byte[] bytes : allBytes) {
-            byteArrays.add(new ByteArray(bytes));
+            byteStrings.add(new ByteString(bytes));
         }
-        return byteArrays;
+        return byteStrings;
+    }
+
+    private static class ByteString {
+        final byte[] bytes;
+
+        public ByteString(byte[] bytes) {
+            this.bytes = bytes;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (!(o instanceof ByteString)) {
+                return false;
+            }
+
+            ByteString other = (ByteString) o;
+            return Arrays.equals(bytes, other.bytes);
+        }
+
+        @Override
+        public int hashCode() {
+            return Arrays.hashCode(bytes);
+        }
     }
 }
diff --git a/platform/src/main/java/org/conscrypt/InternalUtil.java b/platform/src/main/java/org/conscrypt/InternalUtil.java
new file mode 100644
index 0000000..39558c4
--- /dev/null
+++ b/platform/src/main/java/org/conscrypt/InternalUtil.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import java.io.InputStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
+
+/**
+ * Helper to initialize the JNI libraries. This version runs when compiled
+ * as part of the platform.
+ */
+@Internal
+public final class InternalUtil {
+    public static PublicKey logKeyToPublicKey(byte[] logKey)
+            throws NoSuchAlgorithmException {
+        try {
+            return new OpenSSLKey(NativeCrypto.EVP_parse_public_key(logKey)).getPublicKey();
+        } catch (ParsingException e) {
+            throw new NoSuchAlgorithmException(e);
+        }
+    }
+
+    public static PublicKey readPublicKeyPem(InputStream pem) throws InvalidKeyException, NoSuchAlgorithmException {
+        return OpenSSLKey.fromPublicKeyPemInputStream(pem).getPublicKey();
+    }
+
+    private InternalUtil() {
+    }
+}
diff --git a/platform/src/main/java/org/conscrypt/Platform.java b/platform/src/main/java/org/conscrypt/Platform.java
index 85bd6da..36bc5bc 100644
--- a/platform/src/main/java/org/conscrypt/Platform.java
+++ b/platform/src/main/java/org/conscrypt/Platform.java
@@ -25,7 +25,6 @@
 import android.system.StructTimeval;
 import dalvik.system.BlockGuard;
 import dalvik.system.CloseGuard;
-import dalvik.system.VMRuntime;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.lang.System;
@@ -62,14 +61,12 @@
 import javax.net.ssl.StandardConstants;
 import javax.net.ssl.X509ExtendedTrustManager;
 import javax.net.ssl.X509TrustManager;
-import libcore.net.NetworkSecurityPolicy;
-import org.conscrypt.ct.LogStore;
-import org.conscrypt.ct.LogStoreImpl;
-import org.conscrypt.ct.Policy;
-import org.conscrypt.ct.PolicyImpl;
+import org.conscrypt.ct.CTLogStore;
+import org.conscrypt.ct.CTLogStoreImpl;
+import org.conscrypt.ct.CTPolicy;
+import org.conscrypt.ct.CTPolicyImpl;
 import org.conscrypt.metrics.CipherSuite;
 import org.conscrypt.metrics.ConscryptStatsLog;
-import org.conscrypt.metrics.OptionalMethod;
 import org.conscrypt.metrics.Protocol;
 import sun.security.x509.AlgorithmId;
 
@@ -464,10 +461,6 @@
     }
 
     static boolean isCTVerificationRequired(String hostname) {
-        if (Flags.certificateTransparencyPlatform()) {
-            return NetworkSecurityPolicy.getInstance()
-                    .isCertificateTransparencyVerificationRequired(hostname);
-        }
         return false;
     }
 
@@ -493,12 +486,12 @@
         return CertBlocklistImpl.getDefault();
     }
 
-    static LogStore newDefaultLogStore() {
-        return new LogStoreImpl();
+    static CTLogStore newDefaultLogStore() {
+        return new CTLogStoreImpl();
     }
 
-    static Policy newDefaultPolicy() {
-        return new PolicyImpl();
+    static CTPolicy newDefaultPolicy(CTLogStore logStore) {
+        return new CTPolicyImpl(logStore, 2);
     }
 
     static boolean serverNamePermitted(SSLParametersImpl parameters, String serverName) {
@@ -544,34 +537,6 @@
     }
 
     public static boolean isTlsV1Deprecated() {
-        return true;
-    }
-
-    public static boolean isTlsV1Filtered() {
-        Object targetSdkVersion = getTargetSdkVersion();
-        if ((targetSdkVersion != null) && ((int) targetSdkVersion > 34))
-            return false;
-        return true;
-    }
-
-    public static boolean isTlsV1Supported() {
         return false;
     }
-
-    static Object getTargetSdkVersion() {
-        try {
-            Class<?> vmRuntime = Class.forName("dalvik.system.VMRuntime");
-            if (vmRuntime == null) {
-                return null;
-            }
-            OptionalMethod getSdkVersion =
-                    new OptionalMethod(vmRuntime,
-                                        "getTargetSdkVersion");
-            return getSdkVersion.invokeStatic();
-        } catch (ClassNotFoundException e) {
-            return null;
-        } catch (NullPointerException e) {
-            return null;
-        }
-    }
 }
diff --git a/platform/src/main/java/org/conscrypt/TrustedCertificateStore.java b/platform/src/main/java/org/conscrypt/TrustedCertificateStore.java
index bcf7816..14df376 100644
--- a/platform/src/main/java/org/conscrypt/TrustedCertificateStore.java
+++ b/platform/src/main/java/org/conscrypt/TrustedCertificateStore.java
@@ -35,7 +35,6 @@
 import java.util.List;
 import java.util.Set;
 import javax.security.auth.x500.X500Principal;
-import org.conscrypt.ArrayUtils;
 import org.conscrypt.io.IoUtils;
 import org.conscrypt.metrics.OptionalMethod;
 
@@ -117,7 +116,7 @@
             if ((System.getProperty("system.certs.enabled") != null)
                     && (System.getProperty("system.certs.enabled")).equals("true"))
                 return false;
-            if (updatableDir.exists() && !(ArrayUtils.isEmpty(updatableDir.list())))
+            if (updatableDir.exists() && !(updatableDir.list().length == 0))
                 return true;
             return false;
         }
diff --git a/platform/src/main/java/org/conscrypt/ct/CTLogStoreImpl.java b/platform/src/main/java/org/conscrypt/ct/CTLogStoreImpl.java
new file mode 100644
index 0000000..0f37a8d
--- /dev/null
+++ b/platform/src/main/java/org/conscrypt/ct/CTLogStoreImpl.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt.ct;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Scanner;
+import java.util.Set;
+import org.conscrypt.Internal;
+import org.conscrypt.InternalUtil;
+
+@Internal
+public class CTLogStoreImpl implements CTLogStore {
+    private static final Charset US_ASCII = StandardCharsets.US_ASCII;
+
+    /**
+     * Thrown when parsing of a log file fails.
+     */
+    public static class InvalidLogFileException extends Exception {
+        public InvalidLogFileException() {
+        }
+
+        public InvalidLogFileException(String message) {
+            super(message);
+        }
+
+        public InvalidLogFileException(String message, Throwable cause) {
+            super(message, cause);
+        }
+
+        public InvalidLogFileException(Throwable cause) {
+            super(cause);
+        }
+    }
+
+    private static final File defaultUserLogDir;
+    private static final File defaultSystemLogDir;
+    // Lazy loaded by CTLogStoreImpl()
+    private static volatile CTLogInfo[] defaultFallbackLogs = null;
+    static {
+        String ANDROID_DATA = System.getenv("ANDROID_DATA");
+        String ANDROID_ROOT = System.getenv("ANDROID_ROOT");
+        defaultUserLogDir = new File(ANDROID_DATA + "/misc/keychain/trusted_ct_logs/current/");
+        defaultSystemLogDir = new File(ANDROID_ROOT + "/etc/security/ct_known_logs/");
+    }
+
+    private final File userLogDir;
+    private final File systemLogDir;
+    private final CTLogInfo[] fallbackLogs;
+
+    private final HashMap<ByteBuffer, CTLogInfo> logCache = new HashMap<>();
+    private final Set<ByteBuffer> missingLogCache
+            = Collections.synchronizedSet(new HashSet<ByteBuffer>());
+
+    public CTLogStoreImpl() {
+        this(defaultUserLogDir,
+             defaultSystemLogDir,
+             getDefaultFallbackLogs());
+    }
+
+    public CTLogStoreImpl(File userLogDir, File systemLogDir, CTLogInfo[] fallbackLogs) {
+        this.userLogDir = userLogDir;
+        this.systemLogDir = systemLogDir;
+        this.fallbackLogs = fallbackLogs;
+    }
+
+    @Override
+    public CTLogInfo getKnownLog(byte[] logId) {
+        ByteBuffer buf = ByteBuffer.wrap(logId);
+        CTLogInfo log = logCache.get(buf);
+        if (log != null) {
+            return log;
+        }
+        if (missingLogCache.contains(buf)) {
+            return null;
+        }
+
+        log = findKnownLog(logId);
+        if (log != null) {
+            logCache.put(buf, log);
+        } else {
+            missingLogCache.add(buf);
+        }
+
+        return log;
+    }
+
+    private CTLogInfo findKnownLog(byte[] logId) {
+        String filename = hexEncode(logId);
+        try {
+            return loadLog(new File(userLogDir, filename));
+        } catch (InvalidLogFileException e) {
+            return null;
+        } catch (FileNotFoundException e) {
+            // Ignored
+        }
+
+        try {
+            return loadLog(new File(systemLogDir, filename));
+        } catch (InvalidLogFileException e) {
+            return null;
+        } catch (FileNotFoundException e) {
+            // Ignored
+        }
+
+        // If the updateable logs dont exist then use the fallback logs.
+        if (!userLogDir.exists()) {
+            for (CTLogInfo log: fallbackLogs) {
+                if (Arrays.equals(logId, log.getID())) {
+                    return log;
+                }
+            }
+        }
+        return null;
+    }
+
+    public static CTLogInfo[] getDefaultFallbackLogs() {
+        CTLogInfo[] result = defaultFallbackLogs;
+        if (result == null) {
+            // single-check idiom
+            defaultFallbackLogs = result = createDefaultFallbackLogs();
+        }
+        return result;
+    }
+
+    private static CTLogInfo[] createDefaultFallbackLogs() {
+        CTLogInfo[] logs = new CTLogInfo[KnownLogs.LOG_COUNT];
+        for (int i = 0; i < KnownLogs.LOG_COUNT; i++) {
+            try {
+                PublicKey key = InternalUtil.logKeyToPublicKey(KnownLogs.LOG_KEYS[i]);
+
+                logs[i] = new CTLogInfo(key,
+                                        KnownLogs.LOG_DESCRIPTIONS[i],
+                                        KnownLogs.LOG_URLS[i]);
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        defaultFallbackLogs = logs;
+        return logs;
+    }
+
+    /**
+     * Load a CTLogInfo from a file.
+     * @throws FileNotFoundException if the file does not exist
+     * @throws InvalidLogFileException if the file could not be parsed properly
+     * @return a CTLogInfo or null if the file is empty
+     */
+    public static CTLogInfo loadLog(File file) throws FileNotFoundException,
+                                                      InvalidLogFileException {
+        return loadLog(new FileInputStream(file));
+    }
+
+    /**
+     * Load a CTLogInfo from a textual representation. Closes {@code input} upon completion
+     * of loading.
+     *
+     * @throws InvalidLogFileException if the input could not be parsed properly
+     * @return a CTLogInfo or null if the input is empty
+     */
+    public static CTLogInfo loadLog(InputStream input) throws InvalidLogFileException {
+        final Scanner scan = new Scanner(input, "UTF-8");
+        scan.useDelimiter("\n");
+
+        String description = null;
+        String url = null;
+        String key = null;
+        try {
+            // If the scanner can't even read one token then the file must be empty/blank
+            if (!scan.hasNext()) {
+                return null;
+            }
+
+            while (scan.hasNext()) {
+                String[] parts = scan.next().split(":", 2);
+                if (parts.length < 2) {
+                    continue;
+                }
+
+                String name = parts[0];
+                String value = parts[1];
+                switch (name) {
+                    case "description":
+                        description = value;
+                        break;
+                    case "url":
+                        url = value;
+                        break;
+                    case "key":
+                        key = value;
+                        break;
+                }
+            }
+        } finally {
+            scan.close();
+        }
+
+        if (description == null || url == null || key == null) {
+            throw new InvalidLogFileException("Missing one of 'description', 'url' or 'key'");
+        }
+
+        PublicKey pubkey;
+        try {
+            pubkey = InternalUtil.readPublicKeyPem(new ByteArrayInputStream(
+                    ("-----BEGIN PUBLIC KEY-----\n" +
+                        key + "\n" +
+                        "-----END PUBLIC KEY-----").getBytes(US_ASCII)));
+        } catch (InvalidKeyException e) {
+            throw new InvalidLogFileException(e);
+        } catch (NoSuchAlgorithmException e) {
+            throw new InvalidLogFileException(e);
+        }
+
+        return new CTLogInfo(pubkey, description, url);
+    }
+
+    private final static char[] HEX_DIGITS = new char[] {
+        '0', '1', '2', '3', '4', '5', '6', '7',
+        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+    };
+
+    private static String hexEncode(byte[] data) {
+        StringBuilder sb = new StringBuilder(data.length * 2);
+        for (byte b: data) {
+            sb.append(HEX_DIGITS[(b >> 4) & 0x0f]);
+            sb.append(HEX_DIGITS[b & 0x0f]);
+        }
+        return sb.toString();
+    }
+}
diff --git a/platform/src/main/java/org/conscrypt/ct/CTPolicyImpl.java b/platform/src/main/java/org/conscrypt/ct/CTPolicyImpl.java
new file mode 100644
index 0000000..3faca6f
--- /dev/null
+++ b/platform/src/main/java/org/conscrypt/ct/CTPolicyImpl.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt.ct;
+
+import java.security.cert.X509Certificate;
+import java.util.HashSet;
+import java.util.Set;
+import org.conscrypt.Internal;
+
+@Internal
+public class CTPolicyImpl implements CTPolicy {
+    private final CTLogStore logStore;
+    private final int minimumLogCount;
+
+    public CTPolicyImpl(CTLogStore logStore, int minimumLogCount) {
+        this.logStore = logStore;
+        this.minimumLogCount = minimumLogCount;
+    }
+
+    @Override
+    public boolean doesResultConformToPolicy(CTVerificationResult result, String hostname,
+                                             X509Certificate[] chain) {
+        Set<CTLogInfo> logSet = new HashSet<>();
+        for (VerifiedSCT verifiedSCT: result.getValidSCTs()) {
+            CTLogInfo log = logStore.getKnownLog(verifiedSCT.sct.getLogID());
+            if (log != null) {
+                logSet.add(log);
+            }
+        }
+
+        return logSet.size() >= minimumLogCount;
+    }
+}
diff --git a/platform/src/main/java/org/conscrypt/ct/KnownLogs.java b/platform/src/main/java/org/conscrypt/ct/KnownLogs.java
new file mode 100644
index 0000000..dba00cb
--- /dev/null
+++ b/platform/src/main/java/org/conscrypt/ct/KnownLogs.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file is generated by print_log_list.py
+ * https://github.com/google/certificate-transparency/blob/master/python/utilities/log_list/print_log_list.py */
+
+package org.conscrypt.ct;
+
+import org.conscrypt.Internal;
+
+@Internal
+public final class KnownLogs {
+    public static final int LOG_COUNT = 8;
+    public static final String[] LOG_DESCRIPTIONS = new String[] {
+        "Google 'Pilot' log",
+        "Google 'Aviator' log",
+        "DigiCert Log Server",
+        "Google 'Rocketeer' log",
+        "Certly.IO log",
+        "Izenpe log",
+        "Symantec log",
+        "Venafi log",
+    };
+    public static final String[] LOG_URLS = new String[] {
+        "ct.googleapis.com/pilot",
+        "ct.googleapis.com/aviator",
+        "ct1.digicert-ct.com/log",
+        "ct.googleapis.com/rocketeer",
+        "log.certly.io",
+        "ct.izenpe.com",
+        "ct.ws.symantec.com",
+        "ctlog.api.venafi.com",
+    };
+    public static final byte[][] LOG_KEYS = new byte[][] {
+        // Google 'Pilot' log
+        new byte[] {
+            48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72,
+            -50, 61, 3, 1, 7, 3, 66, 0, 4, 125, -88, 75, 18, 41, -128, -93, 61, -83,
+            -45, 90, 119, -72, -52, -30, -120, -77, -91, -3, -15, -45, 12, -51, 24,
+            12, -24, 65, 70, -24, -127, 1, 27, 21, -31, 75, -15, 27, 98, -35, 54, 10,
+            8, 24, -70, -19, 11, 53, -124, -48, -98, 64, 60, 45, -98, -101, -126,
+            101, -67, 31, 4, 16, 65, 76, -96
+        },
+        // Google 'Aviator' log
+        new byte[] {
+            48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72,
+            -50, 61, 3, 1, 7, 3, 66, 0, 4, -41, -12, -52, 105, -78, -28, 14, -112,
+            -93, -118, -22, 90, 112, 9, 79, -17, 19, 98, -48, -115, 73, 96, -1, 27,
+            64, 80, 7, 12, 109, 113, -122, -38, 37, 73, -115, 101, -31, 8, 13, 71,
+            52, 107, -67, 39, -68, -106, 33, 62, 52, -11, -121, 118, 49, -79, 127,
+            29, -55, -123, 59, 13, -9, 31, 63, -23
+        },
+        // DigiCert Log Server
+        new byte[] {
+            48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72,
+            -50, 61, 3, 1, 7, 3, 66, 0, 4, 2, 70, -59, -66, 27, -69, -126, 64, 22,
+            -24, -63, -46, -84, 25, 105, 19, 89, -8, -8, 112, -123, 70, 64, -71, 56,
+            -80, 35, -126, -88, 100, 76, 127, -65, -69, 52, -97, 74, 95, 40, -118,
+            -49, 25, -60, 0, -10, 54, 6, -109, 101, -19, 76, -11, -87, 33, 98, 90,
+            -40, -111, -21, 56, 36, 64, -84, -24
+        },
+        // Google 'Rocketeer' log
+        new byte[] {
+            48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72,
+            -50, 61, 3, 1, 7, 3, 66, 0, 4, 32, 91, 24, -56, 60, -63, -117, -77, 49,
+            8, 0, -65, -96, -112, 87, 43, -73, 71, -116, 111, -75, 104, -80, -114,
+            -112, 120, -23, -96, 115, -22, 79, 40, 33, 46, -100, -64, -12, 22, 27,
+            -86, -7, -43, -41, -87, -128, -61, 78, 47, 82, 60, -104, 1, 37, 70, 36,
+            37, 40, 35, 119, 45, 5, -62, 64, 122
+        },
+        // Certly.IO log
+        new byte[] {
+            48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72,
+            -50, 61, 3, 1, 7, 3, 66, 0, 4, 11, 35, -53, -123, 98, -104, 97, 72, 4,
+            115, -21, 84, 93, -13, -48, 7, -116, 45, 25, 45, -116, 54, -11, -21,
+            -113, 1, 66, 10, 124, -104, 38, 39, -63, -75, -35, -110, -109, -80, -82,
+            -8, -101, 61, 12, -40, 76, 78, 29, -7, 21, -5, 71, 104, 123, -70, 102,
+            -73, 37, -100, -48, 74, -62, 102, -37, 72
+        },
+        // Izenpe log
+        new byte[] {
+            48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72,
+            -50, 61, 3, 1, 7, 3, 66, 0, 4, 39, 100, 57, 12, 45, -36, 80, 24, -8, 33,
+            0, -94, 14, -19, 44, -22, 62, 117, -70, -97, -109, 100, 9, 0, 17, -60,
+            17, 23, -85, 92, -49, 15, 116, -84, -75, -105, -112, -109, 0, 91, -72,
+            -21, -9, 39, 61, -39, -78, 10, -127, 95, 47, 13, 117, 56, -108, 55, -103,
+            30, -10, 7, 118, -32, -18, -66
+        },
+        // Symantec log
+        new byte[] {
+            48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72,
+            -50, 61, 3, 1, 7, 3, 66, 0, 4, -106, -22, -84, 28, 70, 12, 27, 85, -36,
+            13, -4, -75, -108, 39, 70, 87, 66, 112, 58, 105, 24, -30, -65, 59, -60,
+            -37, -85, -96, -12, -74, 108, -64, 83, 63, 77, 66, 16, 51, -16, 88, -105,
+            -113, 107, -66, 114, -12, 42, -20, 28, 66, -86, 3, 47, 26, 126, 40, 53,
+            118, -103, 8, 61, 33, 20, -122
+        },
+        // Venafi log
+        new byte[] {
+            48, -126, 1, 34, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0,
+            3, -126, 1, 15, 0, 48, -126, 1, 10, 2, -126, 1, 1, 0, -94, 90, 72, 31,
+            23, 82, -107, 53, -53, -93, 91, 58, 31, 83, -126, 118, -108, -93, -1,
+            -128, -14, 28, 55, 60, -64, -79, -67, -63, 89, -117, -85, 45, 101, -109,
+            -41, -13, -32, 4, -43, -102, 111, -65, -42, 35, 118, 54, 79, 35, -103,
+            -53, 84, 40, -83, -116, 21, 75, 101, 89, 118, 65, 74, -100, -90, -9, -77,
+            59, 126, -79, -91, 73, -92, 23, 81, 108, -128, -36, 42, -112, 80, 75,
+            -120, 36, -23, -91, 18, 50, -109, 4, 72, -112, 2, -6, 95, 14, 48, -121,
+            -114, 85, 118, 5, -18, 42, 76, -50, -93, 106, 105, 9, 110, 37, -83, -126,
+            118, 15, -124, -110, -6, 56, -42, -122, 78, 36, -113, -101, -80, 114,
+            -53, -98, -30, 107, 63, -31, 109, -55, 37, 117, 35, -120, -95, 24, 88, 6,
+            35, 51, 120, -38, 0, -48, 56, -111, 103, -46, -90, 125, 39, -105, 103,
+            90, -63, -13, 47, 23, -26, -22, -46, 91, -24, -127, -51, -3, -110, 104,
+            -25, -13, 6, -16, -23, 114, -124, -18, 1, -91, -79, -40, 51, -38, -50,
+            -125, -91, -37, -57, -49, -42, 22, 126, -112, 117, 24, -65, 22, -36, 50,
+            59, 109, -115, -85, -126, 23, 31, -119, 32, -115, 29, -102, -26, 77, 35,
+            8, -33, 120, 111, -58, 5, -65, 95, -82, -108, -105, -37, 95, 100, -44,
+            -18, 22, -117, -93, -124, 108, 113, 43, -15, -85, 127, 93, 13, 50, -18,
+            4, -30, -112, -20, 65, -97, -5, 57, -63, 2, 3, 1, 0, 1
+        },
+    };
+}
diff --git a/platform/src/main/java/org/conscrypt/ct/LogStoreImpl.java b/platform/src/main/java/org/conscrypt/ct/LogStoreImpl.java
deleted file mode 100644
index b7141d4..0000000
--- a/platform/src/main/java/org/conscrypt/ct/LogStoreImpl.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.conscrypt.ct;
-
-import static java.nio.charset.StandardCharsets.US_ASCII;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import org.conscrypt.ByteArray;
-import org.conscrypt.Internal;
-import org.conscrypt.OpenSSLKey;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Base64;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-@Internal
-public class LogStoreImpl implements LogStore {
-    private static final Logger logger = Logger.getLogger(LogStoreImpl.class.getName());
-    public static final String V3_PATH = "/misc/keychain/ct/v3/log_list.json";
-    private static final Path defaultLogList;
-
-    static {
-        String ANDROID_DATA = System.getenv("ANDROID_DATA");
-        defaultLogList = Paths.get(ANDROID_DATA, V3_PATH);
-    }
-
-    private final Path logList;
-    private State state;
-    private Policy policy;
-    private String version;
-    private long timestamp;
-    private Map<ByteArray, LogInfo> logs;
-
-    public LogStoreImpl() {
-        this(defaultLogList);
-    }
-
-    public LogStoreImpl(Path logList) {
-        this.state = State.UNINITIALIZED;
-        this.logList = logList;
-    }
-
-    @Override
-    public State getState() {
-        ensureLogListIsLoaded();
-        return state;
-    }
-
-    @Override
-    public long getTimestamp() {
-        return timestamp;
-    }
-
-    @Override
-    public void setPolicy(Policy policy) {
-        this.policy = policy;
-    }
-
-    @Override
-    public LogInfo getKnownLog(byte[] logId) {
-        if (logId == null) {
-            return null;
-        }
-        if (!ensureLogListIsLoaded()) {
-            return null;
-        }
-        ByteArray buf = new ByteArray(logId);
-        LogInfo log = logs.get(buf);
-        if (log != null) {
-            return log;
-        }
-        return null;
-    }
-
-    /* Ensures the log list is loaded.
-     * Returns true if the log list is usable.
-     */
-    private boolean ensureLogListIsLoaded() {
-        synchronized (this) {
-            if (state == State.UNINITIALIZED) {
-                state = loadLogList();
-            }
-            if (state == State.LOADED && policy != null) {
-                state = policy.isLogStoreCompliant(this) ? State.COMPLIANT : State.NON_COMPLIANT;
-            }
-            return state == State.COMPLIANT;
-        }
-    }
-
-    private State loadLogList() {
-        byte[] content;
-        try {
-            content = Files.readAllBytes(logList);
-        } catch (IOException e) {
-            return State.NOT_FOUND;
-        }
-        if (content == null) {
-            return State.NOT_FOUND;
-        }
-        JSONObject json;
-        try {
-            json = new JSONObject(new String(content, UTF_8));
-        } catch (JSONException e) {
-            logger.log(Level.WARNING, "Unable to parse log list", e);
-            return State.MALFORMED;
-        }
-        HashMap<ByteArray, LogInfo> logsMap = new HashMap<>();
-        try {
-            version = json.getString("version");
-            timestamp = parseTimestamp(json.getString("log_list_timestamp"));
-            JSONArray operators = json.getJSONArray("operators");
-            for (int i = 0; i < operators.length(); i++) {
-                JSONObject operator = operators.getJSONObject(i);
-                String operatorName = operator.getString("name");
-                JSONArray logs = operator.getJSONArray("logs");
-                for (int j = 0; j < logs.length(); j++) {
-                    JSONObject log = logs.getJSONObject(j);
-
-                    LogInfo.Builder builder =
-                            new LogInfo.Builder()
-                                    .setDescription(log.getString("description"))
-                                    .setPublicKey(parsePubKey(log.getString("key")))
-                                    .setUrl(log.getString("url"))
-                                    .setOperator(operatorName);
-
-                    JSONObject stateObject = log.optJSONObject("state");
-                    if (stateObject != null) {
-                        String state = stateObject.keys().next();
-                        String stateTimestamp =
-                                stateObject.getJSONObject(state).getString("timestamp");
-                        builder.setState(parseState(state), parseTimestamp(stateTimestamp));
-                    }
-
-                    LogInfo logInfo = builder.build();
-                    byte[] logId = Base64.getDecoder().decode(log.getString("log_id"));
-
-                    // The logId computed using the public key should match the log_id field.
-                    if (!Arrays.equals(logInfo.getID(), logId)) {
-                        throw new IllegalArgumentException("logId does not match publicKey");
-                    }
-
-                    logsMap.put(new ByteArray(logId), logInfo);
-                }
-            }
-        } catch (JSONException | IllegalArgumentException e) {
-            logger.log(Level.WARNING, "Unable to parse log list", e);
-            return State.MALFORMED;
-        }
-        this.logs = Collections.unmodifiableMap(logsMap);
-        return State.LOADED;
-    }
-
-    private static int parseState(String state) {
-        switch (state) {
-            case "pending":
-                return LogInfo.STATE_PENDING;
-            case "qualified":
-                return LogInfo.STATE_QUALIFIED;
-            case "usable":
-                return LogInfo.STATE_USABLE;
-            case "readonly":
-                return LogInfo.STATE_READONLY;
-            case "retired":
-                return LogInfo.STATE_RETIRED;
-            case "rejected":
-                return LogInfo.STATE_REJECTED;
-            default:
-                throw new IllegalArgumentException("Unknown log state: " + state);
-        }
-    }
-
-    // ISO 8601
-    private static DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
-
-    @SuppressWarnings("JavaUtilDate")
-    private static long parseTimestamp(String timestamp) {
-        try {
-            Date date = dateFormatter.parse(timestamp);
-            return date.getTime();
-        } catch (ParseException e) {
-            throw new IllegalArgumentException(e);
-        }
-    }
-
-    private static PublicKey parsePubKey(String key) {
-        byte[] pem = ("-----BEGIN PUBLIC KEY-----\n" + key + "\n-----END PUBLIC KEY-----")
-                             .getBytes(US_ASCII);
-        PublicKey pubkey;
-        try {
-            pubkey = OpenSSLKey.fromPublicKeyPemInputStream(new ByteArrayInputStream(pem))
-                             .getPublicKey();
-        } catch (InvalidKeyException | NoSuchAlgorithmException e) {
-            throw new IllegalArgumentException(e);
-        }
-        return pubkey;
-    }
-}
diff --git a/platform/src/main/java/org/conscrypt/ct/PolicyImpl.java b/platform/src/main/java/org/conscrypt/ct/PolicyImpl.java
deleted file mode 100644
index 8bcd463..0000000
--- a/platform/src/main/java/org/conscrypt/ct/PolicyImpl.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.conscrypt.ct;
-
-import org.conscrypt.Internal;
-
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-@Internal
-public class PolicyImpl implements Policy {
-    @Override
-    public boolean isLogStoreCompliant(LogStore store) {
-        long now = System.currentTimeMillis();
-        return isLogStoreCompliantAt(store, now);
-    }
-
-    public boolean isLogStoreCompliantAt(LogStore store, long atTime) {
-        long storeTimestamp = store.getTimestamp();
-        long seventyDaysInMs = 70L * 24 * 60 * 60 * 1000;
-        if (storeTimestamp + seventyDaysInMs < atTime) {
-            // Expired log list.
-            return false;
-        } else if (storeTimestamp > atTime) {
-            // Log list from the future. It is likely that the device has an
-            // incorrect time.
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public PolicyCompliance doesResultConformToPolicy(
-            VerificationResult result, X509Certificate leaf) {
-        long now = System.currentTimeMillis();
-        return doesResultConformToPolicyAt(result, leaf, now);
-    }
-
-    public PolicyCompliance doesResultConformToPolicyAt(
-            VerificationResult result, X509Certificate leaf, long atTime) {
-        List<VerifiedSCT> validSCTs = new ArrayList<VerifiedSCT>(result.getValidSCTs());
-        /* While the log list supports logs without a state, these entries are
-         * not supported by the log policy. Filter them out. */
-        filterOutUnknown(validSCTs);
-        /* Filter out any SCT issued after a log was retired */
-        filterOutAfterRetired(validSCTs);
-
-        Set<VerifiedSCT> embeddedValidSCTs = new HashSet<>();
-        Set<VerifiedSCT> ocspOrTLSValidSCTs = new HashSet<>();
-        for (VerifiedSCT vsct : validSCTs) {
-            if (vsct.getSct().getOrigin() == SignedCertificateTimestamp.Origin.EMBEDDED) {
-                embeddedValidSCTs.add(vsct);
-            } else {
-                ocspOrTLSValidSCTs.add(vsct);
-            }
-        }
-        if (embeddedValidSCTs.size() > 0) {
-            return conformEmbeddedSCTs(embeddedValidSCTs, leaf, atTime);
-        }
-        return PolicyCompliance.NOT_ENOUGH_SCTS;
-    }
-
-    private void filterOutUnknown(List<VerifiedSCT> scts) {
-        Iterator<VerifiedSCT> it = scts.iterator();
-        while (it.hasNext()) {
-            VerifiedSCT vsct = it.next();
-            if (vsct.getLogInfo().getState() == LogInfo.STATE_UNKNOWN) {
-                it.remove();
-            }
-        }
-    }
-
-    private void filterOutAfterRetired(List<VerifiedSCT> scts) {
-        /* From the policy:
-         *
-         * In order to contribute to a certificate’s CT Compliance, an SCT must
-         * have been issued before the Log’s Retired timestamp, if one exists.
-         * Chrome uses the earliest SCT among all SCTs presented to evaluate CT
-         * compliance against CT Log Retired timestamps. This accounts for edge
-         * cases in which a CT Log becomes Retired during the process of
-         * submitting certificate logging requests.
-         */
-
-        if (scts.size() < 1) {
-            return;
-        }
-        long minTimestamp = scts.get(0).getSct().getTimestamp();
-        for (VerifiedSCT vsct : scts) {
-            long ts = vsct.getSct().getTimestamp();
-            if (ts < minTimestamp) {
-                minTimestamp = ts;
-            }
-        }
-        Iterator<VerifiedSCT> it = scts.iterator();
-        while (it.hasNext()) {
-            VerifiedSCT vsct = it.next();
-            if (vsct.getLogInfo().getState() == LogInfo.STATE_RETIRED
-                    && minTimestamp > vsct.getLogInfo().getStateTimestamp()) {
-                it.remove();
-            }
-        }
-    }
-
-    private PolicyCompliance conformEmbeddedSCTs(
-            Set<VerifiedSCT> embeddedValidSCTs, X509Certificate leaf, long atTime) {
-        /* 1. At least one Embedded SCT from a CT Log that was Qualified,
-         *    Usable, or ReadOnly at the time of check;
-         */
-        boolean found = false;
-        for (VerifiedSCT vsct : embeddedValidSCTs) {
-            LogInfo log = vsct.getLogInfo();
-            switch (log.getStateAt(atTime)) {
-                case LogInfo.STATE_QUALIFIED:
-                case LogInfo.STATE_USABLE:
-                case LogInfo.STATE_READONLY:
-                    found = true;
-            }
-        }
-        if (!found) {
-            return PolicyCompliance.NOT_ENOUGH_SCTS;
-        }
-
-        /* 2. There are Embedded SCTs from at least N distinct CT Logs that
-         *    were Qualified, Usable, ReadOnly, or Retired at the time of check,
-         *    where N is defined in the following table;
-         *
-         *    Certificate Lifetime    Number of SCTs from distinct CT Logs
-         *         <= 180 days                        2
-         *          > 180 days                        3
-         */
-        Set<LogInfo> validLogs = new HashSet<>();
-        int numberSCTsRequired;
-        long certLifetimeMs = leaf.getNotAfter().getTime() - leaf.getNotBefore().getTime();
-        long certLifetimeDays = TimeUnit.DAYS.convert(certLifetimeMs, TimeUnit.MILLISECONDS);
-        if (certLifetimeDays <= 180) {
-            numberSCTsRequired = 2;
-        } else {
-            numberSCTsRequired = 3;
-        }
-        for (VerifiedSCT vsct : embeddedValidSCTs) {
-            LogInfo log = vsct.getLogInfo();
-            switch (log.getStateAt(atTime)) {
-                case LogInfo.STATE_QUALIFIED:
-                case LogInfo.STATE_USABLE:
-                case LogInfo.STATE_READONLY:
-                case LogInfo.STATE_RETIRED:
-                    validLogs.add(log);
-            }
-        }
-        if (validLogs.size() < numberSCTsRequired) {
-            return PolicyCompliance.NOT_ENOUGH_SCTS;
-        }
-
-        /* 3. Among the SCTs satisfying requirements 1 and 2, at least two SCTs
-         *    must be issued from distinct CT Log Operators as recognized by
-         *    Chrome.
-         */
-        Set<String> operators = new HashSet<>();
-        for (LogInfo logInfo : validLogs) {
-            operators.add(logInfo.getOperator());
-        }
-        if (operators.size() < 2) {
-            return PolicyCompliance.NOT_ENOUGH_DIVERSE_SCTS;
-        }
-
-        return PolicyCompliance.COMPLY;
-    }
-}
diff --git a/platform/src/test/java/org/conscrypt/CertBlocklistTest.java b/platform/src/test/java/org/conscrypt/CertBlocklistTest.java
index 4d9a5c1..39561e5 100644
--- a/platform/src/test/java/org/conscrypt/CertBlocklistTest.java
+++ b/platform/src/test/java/org/conscrypt/CertBlocklistTest.java
@@ -29,7 +29,6 @@
 public class CertBlocklistTest extends TestCase {
 
     private static final String BLOCKLIST_CA = "test_blocklist_ca.pem";
-    private static final String BLOCKLIST_CA2 = "test_blocklist_ca2.pem";
     private static final String BLOCKLISTED_CHAIN = "blocklist_test_chain.pem";
     private static final String BLOCKLIST_FALLBACK_VALID_CA = "blocklist_test_valid_ca.pem";
     private static final String BLOCKLISTED_VALID_CHAIN = "blocklist_test_valid_chain.pem";
@@ -44,15 +43,6 @@
     }
 
     /**
-     * Ensure that the test blocklisted CA 2 is actually blocklisted by default.
-     */
-    public void testBlocklistedPublicKeySHA256() throws Exception {
-        X509Certificate blocklistedCa = loadCertificate(BLOCKLIST_CA2);
-        CertBlocklist blocklist = CertBlocklistImpl.getDefault();
-        assertTrue(blocklist.isPublicKeyBlockListed(blocklistedCa.getPublicKey()));
-    }
-
-    /**
      * Check that the blocklisted CA is rejected even if it used as a root of trust
      */
     public void testBlocklistedCaUntrusted() throws Exception {
diff --git a/platform/src/test/java/org/conscrypt/ct/CTLogStoreImplTest.java b/platform/src/test/java/org/conscrypt/ct/CTLogStoreImplTest.java
new file mode 100644
index 0000000..f95a3e6
--- /dev/null
+++ b/platform/src/test/java/org/conscrypt/ct/CTLogStoreImplTest.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt.ct;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.security.PublicKey;
+import junit.framework.TestCase;
+import org.conscrypt.InternalUtil;
+
+public class CTLogStoreImplTest extends TestCase {
+    private static final String[] LOG_KEYS = new String[] {
+        "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmXg8sUUzwBYaWrRb+V0IopzQ6o3U" +
+        "yEJ04r5ZrRXGdpYM8K+hB0pXrGRLI0eeWz+3skXrS0IO83AhA3GpRL6s6w==",
+
+        "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAErEULmlBnX9L/+AK20hLYzPMFozYx" +
+        "pP0Wm1ylqGkPEwuDKn9DSpNSOym49SN77BLGuAXu9twOW/qT+ddIYVBEIw==",
+
+        "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEP6PGcXmjlyCBz2ZFUuUjrgbZLaEF" +
+        "gfLUkt2cEqlSbb4vTuB6WWmgC9h0L6PN6JF0CPcajpBKGlTI15242a8d4g==",
+
+        "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAER3qB0NADsP1szXxe4EagrD/ryPVh" +
+        "Y/azWbKyXcK12zhXnO8WH2U4QROVUMctFXLflIzw0EivdRN9t7UH1Od30w==",
+
+        "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY0ww9JqeJvzVtKNTPVb3JZa7s0ZV" +
+        "duH3PpshpMS5XVoPRSjSQCph6f3HjUcM3c4N2hpa8OFbrFFy37ttUrgD+A=="
+    };
+    private static final String[] LOG_FILENAMES = new String[] {
+        "df1c2ec11500945247a96168325ddc5c7959e8f7c6d388fc002e0bbd3f74d764",
+        "84f8ae3f613b13407a75fa2893b93ab03b18d86c455fe7c241ae020033216446",
+        "89baa01a445100009d8f9a238947115b30702275aafee675a7d94b6b09287619",
+        "57456bffe268e49a190dce4318456034c2b4958f3c0201bed5a366737d1e74ca",
+        "896c898ced4b8e6547fa351266caae4ca304f1c1ec2b623c2ee259c5452147b0"
+    };
+
+    private static final CTLogInfo[] LOGS;
+    private static final String[] LOGS_SERIALIZED;
+
+    static {
+        try {
+            int logCount = LOG_KEYS.length;
+            LOGS = new CTLogInfo[logCount];
+            LOGS_SERIALIZED = new String[logCount];
+            for (int i = 0; i < logCount; i++) {
+                PublicKey key = InternalUtil.readPublicKeyPem(new ByteArrayInputStream(
+                    ("-----BEGIN PUBLIC KEY-----\n" +
+                     LOG_KEYS[i] + "\n" +
+                     "-----END PUBLIC KEY-----\n").getBytes(StandardCharsets.US_ASCII)));
+                String description = String.format("Test Log %d", i);
+                String url = String.format("log%d.example.com", i);
+                LOGS[i] = new CTLogInfo(key, description, url);
+                LOGS_SERIALIZED[i] = String.format("description:%s\nurl:%s\nkey:%s",
+                    description, url, LOG_KEYS[i]);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /* CTLogStoreImpl loads the list of logs lazily when they are first needed
+     * to avoid any overhead when CT is disabled.
+     * This test simply forces the logs to be loaded to make sure it doesn't
+     * fail, as all of the other tests use a different log store.
+     */
+    public void test_getDefaultFallbackLogs() {
+        CTLogInfo[] knownLogs = CTLogStoreImpl.getDefaultFallbackLogs();
+        assertEquals(KnownLogs.LOG_COUNT, knownLogs.length);
+    }
+
+    public void test_loadLog() throws Exception {
+        CTLogInfo log = CTLogStoreImpl.loadLog(
+                new ByteArrayInputStream(LOGS_SERIALIZED[0].getBytes(StandardCharsets.US_ASCII)));
+        assertEquals(LOGS[0], log);
+
+        File testFile = writeFile(LOGS_SERIALIZED[0]);
+        log = CTLogStoreImpl.loadLog(testFile);
+        assertEquals(LOGS[0], log);
+
+        // Empty log file, used to mask fallback logs
+        assertEquals(null, CTLogStoreImpl.loadLog(new ByteArrayInputStream(new byte[0])));
+        try {
+            CTLogStoreImpl.loadLog(new ByteArrayInputStream(
+                    "randomgarbage".getBytes(StandardCharsets.US_ASCII)));
+            fail("InvalidLogFileException not thrown");
+        } catch (CTLogStoreImpl.InvalidLogFileException e) {}
+
+        try {
+            CTLogStoreImpl.loadLog(new File("/nonexistent"));
+            fail("FileNotFoundException not thrown");
+        } catch (FileNotFoundException e) {}
+    }
+
+    public void test_getKnownLog() throws Exception {
+        File userDir = createTempDirectory();
+        userDir.deleteOnExit();
+
+        File systemDir = createTempDirectory();
+        systemDir.deleteOnExit();
+
+        CTLogInfo[] fallback = new CTLogInfo[] { LOGS[2], LOGS[3] };
+
+        CTLogStore store = new CTLogStoreImpl(userDir, systemDir, fallback);
+
+        /* Add logs 0 and 1 to the user and system directories respectively
+         * Log 2 & 3 are part of the fallbacks
+         * But mask log 3 with an empty file in the user directory.
+         * Log 4 is not in the store
+         */
+        File log0File = new File(userDir, LOG_FILENAMES[0]);
+        File log1File = new File(systemDir, LOG_FILENAMES[1]);
+        File log3File = new File(userDir, LOG_FILENAMES[3]);
+        File log4File = new File(userDir, LOG_FILENAMES[4]);
+
+        writeFile(log0File, LOGS_SERIALIZED[0]);
+        writeFile(log1File, LOGS_SERIALIZED[1]);
+        writeFile(log3File, "");
+
+        // Logs 01 are present, log 2 is in the fallback and unused, log 3 is present but masked,
+        // log 4 is missing
+        assertEquals(LOGS[0], store.getKnownLog(LOGS[0].getID()));
+        assertEquals(LOGS[1], store.getKnownLog(LOGS[1].getID()));
+        // Fallback logs are not used if the userDir is present.
+        assertEquals(null, store.getKnownLog(LOGS[2].getID()));
+        assertEquals(null, store.getKnownLog(LOGS[3].getID()));
+        assertEquals(null, store.getKnownLog(LOGS[4].getID()));
+
+        /* Test whether CTLogStoreImpl caches properly
+         * Modify the files on the disk, the result of the store should not change
+         * Delete log 0, mask log 1, add log 4
+         */
+        log0File.delete();
+        writeFile(log1File, "");
+        writeFile(log4File, LOGS_SERIALIZED[4]);
+
+        assertEquals(LOGS[0], store.getKnownLog(LOGS[0].getID()));
+        assertEquals(LOGS[1], store.getKnownLog(LOGS[1].getID()));
+        assertEquals(null, store.getKnownLog(LOGS[4].getID()));
+
+        // Test that fallback logs are used when the userDir doesn't exist.
+        File doesntExist = new File("/doesnt/exist/");
+        store = new CTLogStoreImpl(doesntExist, doesntExist, fallback);
+        assertEquals(LOGS[2], store.getKnownLog(LOGS[2].getID()));
+        assertEquals(LOGS[3], store.getKnownLog(LOGS[3].getID()));
+    }
+
+    /**
+     * Create a temporary file and write to it.
+     * The file will be deleted on exit.
+     * @param contents The data to be written to the file
+     * @return A reference to the temporary file
+     */
+    private File writeFile(String contents) throws IOException {
+        File file = File.createTempFile("test", null);
+        file.deleteOnExit();
+        writeFile(file, contents);
+        return file;
+    }
+
+    private static void writeFile(File file, String contents) throws FileNotFoundException {
+        PrintWriter writer = new PrintWriter(
+                new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), UTF_8)),
+                false);
+        try {
+            writer.write(contents);
+        } finally {
+            writer.close();
+        }
+    }
+
+    /*
+     * This is NOT safe, as another process could create a file between delete() and mkdir()
+     * It should be fine for tests though
+     */
+    private static File createTempDirectory() throws IOException {
+        File folder = File.createTempFile("test", "");
+        folder.delete();
+        folder.mkdir();
+        return folder;
+    }
+}
+
diff --git a/platform/src/test/java/org/conscrypt/ct/LogStoreImplTest.java b/platform/src/test/java/org/conscrypt/ct/LogStoreImplTest.java
deleted file mode 100644
index 81b6cd6..0000000
--- a/platform/src/test/java/org/conscrypt/ct/LogStoreImplTest.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.conscrypt.ct;
-
-import static java.nio.charset.StandardCharsets.US_ASCII;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.security.PublicKey;
-import java.util.Base64;
-import junit.framework.TestCase;
-import org.conscrypt.OpenSSLKey;
-
-public class LogStoreImplTest extends TestCase {
-    public void test_loadLogList() throws Exception {
-        // clang-format off
-        String content = "" +
-"{" +
-"  \"version\": \"1.1\"," +
-"  \"log_list_timestamp\": \"2024-01-01T11:55:12Z\"," +
-"  \"operators\": [" +
-"    {" +
-"      \"name\": \"Operator 1\"," +
-"      \"email\": [\"ct@operator1.com\"]," +
-"      \"logs\": [" +
-"        {" +
-"          \"description\": \"Operator 1 'Test2024' log\"," +
-"          \"log_id\": \"7s3QZNXbGs7FXLedtM0TojKHRny87N7DUUhZRnEftZs=\"," +
-"          \"key\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHblsqctplMVc5ramA7vSuNxUQxcomQwGAVAdnWTAWUYr3MgDHQW0LagJ95lB7QT75Ve6JgT2EVLOFGU7L3YrwA==\"," +
-"          \"url\": \"https://operator1.example.com/logs/test2024/\"," +
-"          \"mmd\": 86400," +
-"          \"state\": {" +
-"            \"usable\": {" +
-"              \"timestamp\": \"2022-11-01T18:54:00Z\"" +
-"            }" +
-"          }," +
-"          \"temporal_interval\": {" +
-"            \"start_inclusive\": \"2024-01-01T00:00:00Z\"," +
-"            \"end_exclusive\": \"2025-01-01T00:00:00Z\"" +
-"          }" +
-"        }," +
-"        {" +
-"          \"description\": \"Operator 1 'Test2025' log\"," +
-"          \"log_id\": \"TnWjJ1yaEMM4W2zU3z9S6x3w4I4bjWnAsfpksWKaOd8=\"," +
-"          \"key\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIIKh+WdoqOTblJji4WiH5AltIDUzODyvFKrXCBjw/Rab0/98J4LUh7dOJEY7+66+yCNSICuqRAX+VPnV8R1Fmg==\"," +
-"          \"url\": \"https://operator1.example.com/logs/test2025/\"," +
-"          \"mmd\": 86400," +
-"          \"state\": {" +
-"            \"usable\": {" +
-"              \"timestamp\": \"2023-11-26T12:00:00Z\"" +
-"            }" +
-"          }," +
-"          \"temporal_interval\": {" +
-"            \"start_inclusive\": \"2025-01-01T00:00:00Z\"," +
-"            \"end_exclusive\": \"2025-07-01T00:00:00Z\"" +
-"          }" +
-"        }" +
-"      ]" +
-"    }," +
-"    {" +
-"      \"name\": \"Operator 2\"," +
-"      \"email\": [\"ct@operator2.com\"]," +
-"      \"logs\": [" +
-"        {" +
-"          \"description\": \"Operator 2 'Test2024' Log\"," +
-"          \"log_id\": \"2ra/az+1tiKfm8K7XGvocJFxbLtRhIU0vaQ9MEjX+6s=\"," +
-"          \"key\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEd7Gbe4/mizX+OpIpLayKjVGKJfyTttegiyk3cR0zyswz6ii5H+Ksw6ld3Ze+9p6UJd02gdHrXSnDK0TxW8oVSA==\"," +
-"          \"url\": \"https://operator2.example.com/logs/test2024/\"," +
-"          \"mmd\": 86400," +
-"          \"state\": {" +
-"            \"usable\": {" +
-"              \"timestamp\": \"2022-11-30T17:00:00Z\"" +
-"            }" +
-"          }," +
-"          \"temporal_interval\": {" +
-"            \"start_inclusive\": \"2024-01-01T00:00:00Z\"," +
-"            \"end_exclusive\": \"2025-01-01T00:00:00Z\"" +
-"          }" +
-"        }" +
-"      ]" +
-"    }" +
-"  ]" +
-"}";
-        // clang-format on
-
-        File logList = writeFile(content);
-        LogStore store = new LogStoreImpl(logList.toPath());
-        store.setPolicy(new PolicyImpl() {
-            @Override
-            public boolean isLogStoreCompliant(LogStore store) {
-                return true;
-            }
-        });
-
-        assertNull("A null logId should return null", store.getKnownLog(null));
-
-        byte[] pem = ("-----BEGIN PUBLIC KEY-----\n"
-                + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHblsqctplMVc5ramA7vSuNxUQxcomQwGAVAdnWTAWUYr"
-                + "3MgDHQW0LagJ95lB7QT75Ve6JgT2EVLOFGU7L3YrwA=="
-                + "\n-----END PUBLIC KEY-----\n")
-                             .getBytes(US_ASCII);
-        ByteArrayInputStream is = new ByteArrayInputStream(pem);
-
-        LogInfo log1 =
-                new LogInfo.Builder()
-                        .setPublicKey(OpenSSLKey.fromPublicKeyPemInputStream(is).getPublicKey())
-                        .setDescription("Operator 1 'Test2024' log")
-                        .setUrl("https://operator1.example.com/logs/test2024/")
-                        .setState(LogInfo.STATE_USABLE, 1667328840000L)
-                        .setOperator("Operator 1")
-                        .build();
-        byte[] log1Id = Base64.getDecoder().decode("7s3QZNXbGs7FXLedtM0TojKHRny87N7DUUhZRnEftZs=");
-        assertEquals("An existing logId should be returned", log1, store.getKnownLog(log1Id));
-    }
-
-    private File writeFile(String content) throws IOException {
-        File file = File.createTempFile("test", null);
-        file.deleteOnExit();
-        try (FileWriter fw = new FileWriter(file)) {
-            fw.write(content);
-        }
-        return file;
-    }
-}
diff --git a/platform/src/test/java/org/conscrypt/ct/PolicyImplTest.java b/platform/src/test/java/org/conscrypt/ct/PolicyImplTest.java
deleted file mode 100644
index c4b70f9..0000000
--- a/platform/src/test/java/org/conscrypt/ct/PolicyImplTest.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.conscrypt.ct;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import org.conscrypt.java.security.cert.FakeX509Certificate;
-import org.junit.Assume;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.security.PublicKey;
-import java.security.cert.X509Certificate;
-
-@RunWith(JUnit4.class)
-public class PolicyImplTest {
-    private static final String OPERATOR1 = "operator 1";
-    private static final String OPERATOR2 = "operator 2";
-    private static LogInfo usableOp1Log1;
-    private static LogInfo usableOp1Log2;
-    private static LogInfo retiredOp1LogOld;
-    private static LogInfo retiredOp1LogNew;
-    private static LogInfo usableOp2Log;
-    private static LogInfo retiredOp2Log;
-    private static SignedCertificateTimestamp embeddedSCT;
-
-    /* Some test dates. By default:
-     *  - The verification is occurring in January 2024;
-     *  - The log list was created in December 2023;
-     *  - The SCTs were generated in January 2023; and
-     *  - The logs got into their state in January 2022.
-     * Other dates are used to exercise edge cases.
-     */
-    private static final long JAN2025 = 1735725600000L;
-    private static final long JAN2024 = 1704103200000L;
-    private static final long DEC2023 = 1701424800000L;
-    private static final long JUN2023 = 1672999200000L;
-    private static final long JAN2023 = 1672567200000L;
-    private static final long JAN2022 = 1641031200000L;
-
-    private static class FakePublicKey implements PublicKey {
-        static final long serialVersionUID = 1;
-        final byte[] key;
-
-        FakePublicKey(byte[] key) {
-            this.key = key;
-        }
-
-        @Override
-        public byte[] getEncoded() {
-            return this.key;
-        }
-
-        @Override
-        public String getAlgorithm() {
-            return "";
-        }
-
-        @Override
-        public String getFormat() {
-            return "";
-        }
-    }
-
-    @BeforeClass
-    public static void setUp() {
-        /* Defines LogInfo for the tests. Only a subset of the attributes are
-         * expected to be used, namely the LogID (based on the public key), the
-         * operator name and the log state.
-         */
-        usableOp1Log1 = new LogInfo.Builder()
-                                .setPublicKey(new FakePublicKey(new byte[] {0x01}))
-                                .setUrl("")
-                                .setOperator(OPERATOR1)
-                                .setState(LogInfo.STATE_USABLE, JAN2022)
-                                .build();
-        usableOp1Log2 = new LogInfo.Builder()
-                                .setPublicKey(new FakePublicKey(new byte[] {0x02}))
-                                .setUrl("")
-                                .setOperator(OPERATOR1)
-                                .setState(LogInfo.STATE_USABLE, JAN2022)
-                                .build();
-        retiredOp1LogOld = new LogInfo.Builder()
-                                   .setPublicKey(new FakePublicKey(new byte[] {0x03}))
-                                   .setUrl("")
-                                   .setOperator(OPERATOR1)
-                                   .setState(LogInfo.STATE_RETIRED, JAN2022)
-                                   .build();
-        retiredOp1LogNew = new LogInfo.Builder()
-                                   .setPublicKey(new FakePublicKey(new byte[] {0x06}))
-                                   .setUrl("")
-                                   .setOperator(OPERATOR1)
-                                   .setState(LogInfo.STATE_RETIRED, JUN2023)
-                                   .build();
-        usableOp2Log = new LogInfo.Builder()
-                               .setPublicKey(new FakePublicKey(new byte[] {0x04}))
-                               .setUrl("")
-                               .setOperator(OPERATOR2)
-                               .setState(LogInfo.STATE_USABLE, JAN2022)
-                               .build();
-        retiredOp2Log = new LogInfo.Builder()
-                                .setPublicKey(new FakePublicKey(new byte[] {0x05}))
-                                .setUrl("")
-                                .setOperator(OPERATOR2)
-                                .setState(LogInfo.STATE_RETIRED, JAN2022)
-                                .build();
-        /* The origin of the SCT and its timestamp are used during the
-         * evaluation for policy compliance. The signature is validated at the
-         * previous step (see the Verifier class).
-         */
-        embeddedSCT = new SignedCertificateTimestamp(SignedCertificateTimestamp.Version.V1, null,
-                JAN2023, null, null, SignedCertificateTimestamp.Origin.EMBEDDED);
-    }
-
-    @Test
-    public void emptyVerificationResult() throws Exception {
-        PolicyImpl p = new PolicyImpl();
-        VerificationResult result = new VerificationResult();
-
-        X509Certificate leaf = new FakeX509Certificate();
-        assertEquals("An empty VerificationResult", PolicyCompliance.NOT_ENOUGH_SCTS,
-                p.doesResultConformToPolicyAt(result, leaf, JAN2024));
-    }
-
-    @Test
-    public void validVerificationResult() throws Exception {
-        PolicyImpl p = new PolicyImpl();
-
-        VerifiedSCT vsct1 = new VerifiedSCT.Builder(embeddedSCT)
-                                    .setStatus(VerifiedSCT.Status.VALID)
-                                    .setLogInfo(usableOp1Log1)
-                                    .build();
-
-        VerifiedSCT vsct2 = new VerifiedSCT.Builder(embeddedSCT)
-                                    .setStatus(VerifiedSCT.Status.VALID)
-                                    .setLogInfo(usableOp2Log)
-                                    .build();
-
-        VerificationResult result = new VerificationResult();
-        result.add(vsct1);
-        result.add(vsct2);
-
-        X509Certificate leaf = new FakeX509Certificate();
-        assertEquals("Two valid SCTs from different operators", PolicyCompliance.COMPLY,
-                p.doesResultConformToPolicyAt(result, leaf, JAN2024));
-    }
-
-    @Test
-    public void validWithRetiredVerificationResult() throws Exception {
-        PolicyImpl p = new PolicyImpl();
-
-        VerifiedSCT vsct1 = new VerifiedSCT.Builder(embeddedSCT)
-                                    .setStatus(VerifiedSCT.Status.VALID)
-                                    .setLogInfo(retiredOp1LogNew)
-                                    .build();
-
-        VerifiedSCT vsct2 = new VerifiedSCT.Builder(embeddedSCT)
-                                    .setStatus(VerifiedSCT.Status.VALID)
-                                    .setLogInfo(usableOp2Log)
-                                    .build();
-
-        VerificationResult result = new VerificationResult();
-        result.add(vsct1);
-        result.add(vsct2);
-
-        X509Certificate leaf = new FakeX509Certificate();
-        assertEquals("One valid, one retired SCTs from different operators",
-                PolicyCompliance.COMPLY, p.doesResultConformToPolicyAt(result, leaf, JAN2024));
-    }
-
-    @Test
-    public void invalidWithRetiredVerificationResult() throws Exception {
-        PolicyImpl p = new PolicyImpl();
-
-        VerifiedSCT vsct1 = new VerifiedSCT.Builder(embeddedSCT)
-                                    .setStatus(VerifiedSCT.Status.VALID)
-                                    .setLogInfo(retiredOp1LogOld)
-                                    .build();
-
-        VerifiedSCT vsct2 = new VerifiedSCT.Builder(embeddedSCT)
-                                    .setStatus(VerifiedSCT.Status.VALID)
-                                    .setLogInfo(usableOp2Log)
-                                    .build();
-
-        VerificationResult result = new VerificationResult();
-        result.add(vsct1);
-        result.add(vsct2);
-
-        X509Certificate leaf = new FakeX509Certificate();
-        assertEquals("One valid, one retired (before SCT timestamp) SCTs from different operators",
-                PolicyCompliance.NOT_ENOUGH_SCTS,
-                p.doesResultConformToPolicyAt(result, leaf, JAN2024));
-    }
-
-    @Test
-    public void invalidOneSctVerificationResult() throws Exception {
-        PolicyImpl p = new PolicyImpl();
-
-        VerifiedSCT vsct1 = new VerifiedSCT.Builder(embeddedSCT)
-                                    .setStatus(VerifiedSCT.Status.VALID)
-                                    .setLogInfo(usableOp1Log1)
-                                    .build();
-
-        VerificationResult result = new VerificationResult();
-        result.add(vsct1);
-
-        X509Certificate leaf = new FakeX509Certificate();
-        assertEquals("One valid SCT", PolicyCompliance.NOT_ENOUGH_SCTS,
-                p.doesResultConformToPolicyAt(result, leaf, JAN2024));
-    }
-
-    @Test
-    public void invalidTwoSctsVerificationResult() throws Exception {
-        PolicyImpl p = new PolicyImpl();
-
-        VerifiedSCT vsct1 = new VerifiedSCT.Builder(embeddedSCT)
-                                    .setStatus(VerifiedSCT.Status.VALID)
-                                    .setLogInfo(retiredOp1LogNew)
-                                    .build();
-
-        VerifiedSCT vsct2 = new VerifiedSCT.Builder(embeddedSCT)
-                                    .setStatus(VerifiedSCT.Status.VALID)
-                                    .setLogInfo(retiredOp2Log)
-                                    .build();
-
-        VerificationResult result = new VerificationResult();
-        result.add(vsct1);
-        result.add(vsct2);
-
-        X509Certificate leaf = new FakeX509Certificate();
-        assertEquals("Two retired SCTs from different operators", PolicyCompliance.NOT_ENOUGH_SCTS,
-                p.doesResultConformToPolicyAt(result, leaf, JAN2024));
-    }
-
-    @Test
-    public void invalidTwoSctsSameOperatorVerificationResult() throws Exception {
-        PolicyImpl p = new PolicyImpl();
-
-        VerifiedSCT vsct1 = new VerifiedSCT.Builder(embeddedSCT)
-                                    .setStatus(VerifiedSCT.Status.VALID)
-                                    .setLogInfo(usableOp1Log1)
-                                    .build();
-
-        VerifiedSCT vsct2 = new VerifiedSCT.Builder(embeddedSCT)
-                                    .setStatus(VerifiedSCT.Status.VALID)
-                                    .setLogInfo(usableOp1Log2)
-                                    .build();
-
-        VerificationResult result = new VerificationResult();
-        result.add(vsct1);
-        result.add(vsct2);
-
-        X509Certificate leaf = new FakeX509Certificate();
-        assertEquals("Two SCTs from the same operator", PolicyCompliance.NOT_ENOUGH_DIVERSE_SCTS,
-                p.doesResultConformToPolicyAt(result, leaf, JAN2024));
-    }
-
-    @Test
-    @NonCts(reason = NonCtsReasons.INTERNAL_APIS)
-    public void validRecentLogStore() throws Exception {
-        PolicyImpl p = new PolicyImpl();
-
-        LogStore store = new LogStoreImpl() {
-            @Override
-            public long getTimestamp() {
-                return DEC2023;
-            }
-        };
-        assertTrue("A recent log list is compliant", p.isLogStoreCompliantAt(store, JAN2024));
-    }
-
-    @Test
-    @NonCts(reason = NonCtsReasons.INTERNAL_APIS)
-    public void invalidFutureLogStore() throws Exception {
-        PolicyImpl p = new PolicyImpl();
-
-        LogStore store = new LogStoreImpl() {
-            @Override
-            public long getTimestamp() {
-                return JAN2025;
-            }
-        };
-        assertFalse("A future log list is non-compliant", p.isLogStoreCompliantAt(store, JAN2024));
-    }
-
-    @Test
-    @NonCts(reason = NonCtsReasons.INTERNAL_APIS)
-    public void invalidOldLogStore() throws Exception {
-        PolicyImpl p = new PolicyImpl();
-
-        LogStore store = new LogStoreImpl() {
-            @Override
-            public long getTimestamp() {
-                return JAN2023;
-            }
-        };
-        assertFalse("A expired log list is non-compliant", p.isLogStoreCompliantAt(store, JAN2024));
-    }
-}
diff --git a/testing/src/main/java/org/conscrypt/ChannelType.java b/testing/src/main/java/org/conscrypt/ChannelType.java
index 23e09a0..09dd582 100644
--- a/testing/src/main/java/org/conscrypt/ChannelType.java
+++ b/testing/src/main/java/org/conscrypt/ChannelType.java
@@ -38,24 +38,24 @@
 public enum ChannelType {
     NONE {
         @Override
-        public SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
+        SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
                 throws IOException {
             return clientMode(factory.createSocket(address, port));
         }
 
         @Override
-        public ServerSocket newServerSocket(SSLServerSocketFactory factory) throws IOException {
+        ServerSocket newServerSocket(SSLServerSocketFactory factory) throws IOException {
             return factory.createServerSocket(0, 50, InetAddress.getLoopbackAddress());
         }
 
         @Override
-        public SSLSocket accept(ServerSocket socket, SSLSocketFactory unused) throws IOException {
+        SSLSocket accept(ServerSocket socket, SSLSocketFactory unused) throws IOException {
             return serverMode(socket.accept());
         }
     },
     NO_CHANNEL {
         @Override
-        public SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
+        SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
                 throws IOException {
             Socket wrapped = new Socket(address, port);
             assertNull(wrapped.getChannel());
@@ -64,13 +64,13 @@
         }
 
         @Override
-        public ServerSocket newServerSocket(SSLServerSocketFactory unused) throws IOException {
+        ServerSocket newServerSocket(SSLServerSocketFactory unused) throws IOException {
             return ServerSocketFactory.getDefault().createServerSocket(
                     0, 50, InetAddress.getLoopbackAddress());
         }
 
         @Override
-        public SSLSocket accept(ServerSocket serverSocket, SSLSocketFactory factory) throws IOException {
+        SSLSocket accept(ServerSocket serverSocket, SSLSocketFactory factory) throws IOException {
             assertFalse(serverSocket instanceof SSLServerSocket);
             Socket wrapped = serverSocket.accept();
             assertNull(wrapped.getChannel());
@@ -81,21 +81,21 @@
     },
     CHANNEL {
         @Override
-        public SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
+        SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
                 throws IOException {
             Socket wrapped = SocketChannel.open(new InetSocketAddress(address, port)).socket();
             return clientMode(factory.createSocket(wrapped, address.getHostName(), port, true));
         }
 
         @Override
-        public ServerSocket newServerSocket(SSLServerSocketFactory unused) throws IOException {
+        ServerSocket newServerSocket(SSLServerSocketFactory unused) throws IOException {
             return ServerSocketChannel.open()
                     .bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0))
                     .socket();
         }
 
         @Override
-        public SSLSocket accept(ServerSocket serverSocket, SSLSocketFactory factory) throws IOException {
+        SSLSocket accept(ServerSocket serverSocket, SSLSocketFactory factory) throws IOException {
             assertFalse(serverSocket instanceof SSLServerSocket);
             ServerSocketChannel serverChannel = serverSocket.getChannel();
 
@@ -111,10 +111,10 @@
         }
     };
 
-    public abstract SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
+    abstract SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
             throws IOException;
-    public abstract ServerSocket newServerSocket(SSLServerSocketFactory factory) throws IOException;
-    public abstract SSLSocket accept(ServerSocket socket, SSLSocketFactory factory) throws IOException;
+    abstract ServerSocket newServerSocket(SSLServerSocketFactory factory) throws IOException;
+    abstract SSLSocket accept(ServerSocket socket, SSLSocketFactory factory) throws IOException;
 
     private static SSLSocket clientMode(Socket socket) {
         SSLSocket sslSocket = (SSLSocket) socket;
diff --git a/testing/src/main/java/org/conscrypt/TestUtils.java b/testing/src/main/java/org/conscrypt/TestUtils.java
index b6ee45b..503102d 100644
--- a/testing/src/main/java/org/conscrypt/TestUtils.java
+++ b/testing/src/main/java/org/conscrypt/TestUtils.java
@@ -355,7 +355,7 @@
         }
     }
 
-    public static SSLSocketFactory setUseEngineSocket(
+    static SSLSocketFactory setUseEngineSocket(
             SSLSocketFactory conscryptFactory, boolean useEngineSocket) {
         try {
             Class<?> clazz = conscryptClass("Conscrypt");
@@ -368,7 +368,7 @@
         }
     }
 
-    public static SSLServerSocketFactory setUseEngineSocket(
+    static SSLServerSocketFactory setUseEngineSocket(
             SSLServerSocketFactory conscryptFactory, boolean useEngineSocket) {
         try {
             Class<?> clazz = conscryptClass("Conscrypt");
@@ -513,12 +513,12 @@
         return msg;
     }
 
-    public static SSLContext newClientSslContext(Provider provider) {
+    static SSLContext newClientSslContext(Provider provider) {
         SSLContext context = newContext(provider);
         return initClientSslContext(context);
     }
 
-    public static SSLContext newServerSslContext(Provider provider) {
+    static SSLContext newServerSslContext(Provider provider) {
         SSLContext context = newContext(provider);
         return initServerSslContext(context);
     }
@@ -871,18 +871,4 @@
             throw new RuntimeException(e);
         }
     }
-
-    // Find base method via reflection due to possible version skew on Android
-    // and visibility issues when building with Gradle.
-    public static boolean isTlsV1Filtered() {
-        try {
-            return (Boolean) conscryptClass("Platform")
-                    .getDeclaredMethod("isTlsV1Filtered")
-                    .invoke(null);
-        } catch (NoSuchMethodException e) {
-            return true;
-        } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException e) {
-            throw new IllegalStateException("Reflection failure", e);
-        }
-    }
 }
diff --git a/testing/src/main/java/org/conscrypt/java/security/StandardNames.java b/testing/src/main/java/org/conscrypt/java/security/StandardNames.java
index 6d4e871..54a26d0 100644
--- a/testing/src/main/java/org/conscrypt/java/security/StandardNames.java
+++ b/testing/src/main/java/org/conscrypt/java/security/StandardNames.java
@@ -161,8 +161,6 @@
     }
 
     public static final String SSL_CONTEXT_PROTOCOLS_DEFAULT = "Default";
-    public static final Set<String> SSL_CONTEXT_PROTOCOLS_ALL =
-            new HashSet<String>(Arrays.asList("TLS", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"));
     public static final Set<String> SSL_CONTEXT_PROTOCOLS = new HashSet<String>(
             Arrays.asList(SSL_CONTEXT_PROTOCOLS_DEFAULT, "TLS", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"));
     public static final Set<String> SSL_CONTEXT_PROTOCOLS_WITH_DEFAULT_CONFIG = new HashSet<String>(
diff --git a/testing/src/main/java/org/conscrypt/java/security/cert/FakeX509Certificate.java b/testing/src/main/java/org/conscrypt/java/security/cert/FakeX509Certificate.java
deleted file mode 100644
index ed61cc4..0000000
--- a/testing/src/main/java/org/conscrypt/java/security/cert/FakeX509Certificate.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.conscrypt.java.security.cert;
-
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.Principal;
-import java.security.PublicKey;
-import java.security.SignatureException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.X509Certificate;
-import java.util.Date;
-import java.util.Set;
-
-public class FakeX509Certificate extends X509Certificate {
-    @Override
-    public void checkValidity()
-            throws CertificateExpiredException, CertificateNotYetValidException {}
-
-    @Override
-    public void checkValidity(Date date)
-            throws CertificateExpiredException, CertificateNotYetValidException {}
-
-    @Override
-    public int getBasicConstraints() {
-        return 0;
-    }
-
-    @Override
-    public Principal getIssuerDN() {
-        return new MockPrincipal();
-    }
-
-    @Override
-    public boolean[] getIssuerUniqueID() {
-        return null;
-    }
-
-    @Override
-    public boolean[] getKeyUsage() {
-        return null;
-    }
-
-    @Override
-    public Date getNotAfter() {
-        return new Date(System.currentTimeMillis());
-    }
-
-    @Override
-    public Date getNotBefore() {
-        return new Date(System.currentTimeMillis() - 1000);
-    }
-
-    @Override
-    public BigInteger getSerialNumber() {
-        return null;
-    }
-
-    @Override
-    public String getSigAlgName() {
-        return null;
-    }
-
-    @Override
-    public String getSigAlgOID() {
-        return null;
-    }
-
-    @Override
-    public byte[] getSigAlgParams() {
-        return null;
-    }
-
-    @Override
-    public byte[] getSignature() {
-        return null;
-    }
-
-    @Override
-    public Principal getSubjectDN() {
-        return new MockPrincipal();
-    }
-
-    class MockPrincipal implements Principal {
-        public String getName() {
-            return null;
-        }
-    }
-    @Override
-    public boolean[] getSubjectUniqueID() {
-        return null;
-    }
-
-    @Override
-    public byte[] getTBSCertificate() throws CertificateEncodingException {
-        return null;
-    }
-
-    @Override
-    public int getVersion() {
-        return 0;
-    }
-
-    @Override
-    public byte[] getEncoded() throws CertificateEncodingException {
-        return null;
-    }
-
-    @Override
-    public PublicKey getPublicKey() {
-        return null;
-    }
-
-    @Override
-    public String toString() {
-        return null;
-    }
-
-    @Override
-    public void verify(PublicKey key)
-            throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
-                   NoSuchProviderException, SignatureException {}
-
-    @Override
-    public void verify(PublicKey key, String sigProvider)
-            throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
-                   NoSuchProviderException, SignatureException {}
-
-    @Override
-    public Set<String> getCriticalExtensionOIDs() {
-        return null;
-    }
-
-    @Override
-    public byte[] getExtensionValue(String oid) {
-        return null;
-    }
-
-    @Override
-    public Set<String> getNonCriticalExtensionOIDs() {
-        return null;
-    }
-
-    @Override
-    public boolean hasUnsupportedCriticalExtension() {
-        return false;
-    }
-}