Merge "Assert finite default timeout for TLS/SSL sessions."
diff --git a/luni/src/main/java/java/text/SimpleDateFormat.java b/luni/src/main/java/java/text/SimpleDateFormat.java
index 8f83ff7..4e7a950 100644
--- a/luni/src/main/java/java/text/SimpleDateFormat.java
+++ b/luni/src/main/java/java/text/SimpleDateFormat.java
@@ -91,7 +91,16 @@
  * <tr> <td>{@code ''}</td> <td>single quote</td>           <td>(Literal)</td>     <td>{@code 'o''clock'}:o'clock</td> </tr>
  * </table>
  *
- * <p>Fractional seconds are handled specially: they're zero-padded on the <i>right</i>.
+ * <p>Note that {@code 'S'} represents fractional seconds and not millisecond values.
+ * They will be padded on the left or on the right or both depending on the number of
+ * {@code 'S'} in the pattern. For example, the number of fractional seconds in a
+ * {@code Date} where {@code Date.getTime() == 1000006} are {@code 0.006} or
+ * {@code (6 / 1000)}. This leads to the following formatting:
+ * <ul>
+ *     <li> {@code "S" => "0"} </li>
+ *     <li> {@code "SSS" => "006" } </li>
+ *     <li> {@code "SSSSSS" => "006000" }</li>
+ * </ul>
  *
  * <p>The two pattern characters {@code L} and {@code c} are ICU-compatible extensions, not
  * available in the RI or in Android before Android 2.3 (Gingerbread, API level 9). These
diff --git a/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java b/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
index 013bf17..e513cf2 100644
--- a/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
+++ b/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
@@ -38,6 +38,7 @@
     private static final int ALT_DNS_NAME = 2;
     private static final int ALT_IPA_NAME = 7;
 
+    @Override
     public final boolean verify(String host, SSLSession session) {
         try {
             Certificate[] certificates = session.getPeerCertificates();
@@ -47,7 +48,7 @@
         }
     }
 
-    public boolean verify(String host, X509Certificate certificate) {
+    private boolean verify(String host, X509Certificate certificate) {
         return InetAddress.isNumeric(host)
                 ? verifyIpAddress(host, certificate)
                 : verifyHostName(host, certificate);
@@ -126,7 +127,7 @@
      * @param cn certificate host name. May include wildcards like
      *     {@code *.android.com}.
      */
-    public boolean verifyHostName(String hostName, String cn) {
+    private boolean verifyHostName(String hostName, String cn) {
         if (hostName == null || hostName.isEmpty() || cn == null || cn.isEmpty()) {
             return false;
         }
diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
index 714df2c..3db7c8f 100644
--- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
@@ -74,7 +74,6 @@
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.X509TrustManager;
-import libcore.java.security.StandardNames;
 import libcore.java.security.TestKeyStore;
 import libcore.java.util.AbstractResourceLeakageDetectorTestCase;
 import libcore.javax.net.ssl.TestSSLContext;
@@ -2189,50 +2188,107 @@
         urlConnection.getInputStream();
     }
 
-    public void testSslFallback() throws Exception {
+    public void testSslFallback_allSupportedProtocols() throws Exception {
         TestSSLContext testSSLContext = TestSSLContext.create();
 
-        // Android now disables SSLv3 by default. To test fallback we re-enable it for the server.
-        // This can be removed once OkHttp is updated to support other fallback protocols.
+        String[] allSupportedProtocols = { "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3" };
         SSLSocketFactory serverSocketFactory =
                 new LimitedProtocolsSocketFactory(
                         testSSLContext.serverContext.getSocketFactory(),
-                        "TLSv1", "SSLv3");
-
+                        allSupportedProtocols);
         server.useHttps(serverSocketFactory, false);
         server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
-        server.enqueue(new MockResponse().setBody("This required a 2nd handshake"));
+        server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+        server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+        server.enqueue(new MockResponse().setBody("This required fallbacks"));
         server.play();
 
         HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
         // Keeps track of the client sockets created so that we can interrogate them.
         final boolean disableFallbackScsv = true;
         FallbackTestClientSocketFactory clientSocketFactory = new FallbackTestClientSocketFactory(
-                testSSLContext.clientContext.getSocketFactory(), disableFallbackScsv);
+                new LimitedProtocolsSocketFactory(
+                        testSSLContext.clientContext.getSocketFactory(), allSupportedProtocols),
+                disableFallbackScsv);
         connection.setSSLSocketFactory(clientSocketFactory);
-        assertEquals("This required a 2nd handshake",
+        assertEquals("This required fallbacks",
                 readAscii(connection.getInputStream(), Integer.MAX_VALUE));
 
+        // Confirm the server accepted a single connection.
         RecordedRequest retry = server.takeRequest();
         assertEquals(0, retry.getSequenceNumber());
         assertEquals("SSLv3", retry.getSslProtocol());
 
         // Confirm the client fallback looks ok.
         List<SSLSocket> createdSockets = clientSocketFactory.getCreatedSockets();
-        assertTrue(createdSockets.size() > 1);
+        assertEquals(4, createdSockets.size());
         TlsFallbackDisabledScsvSSLSocket clientSocket1 =
                 (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(0);
-        List<String> clientSocket1EnabledProtocols = Arrays.asList(
-                clientSocket1.getEnabledProtocols());
-        assertContains(clientSocket1EnabledProtocols, "TLSv1.2");
-        assertFalse(clientSocket1.wasTlsFallbackScsvSet());
+        assertSslSocket(clientSocket1,
+                false /* expectedWasFallbackScsvSet */, "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3");
 
         TlsFallbackDisabledScsvSSLSocket clientSocket2 =
                 (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(1);
-        List<String> clientSocket2EnabledProtocols =
-                Arrays.asList(clientSocket2.getEnabledProtocols());
-        assertContainsNoneMatching(clientSocket2EnabledProtocols, "TLSv1.2");
-        assertTrue(clientSocket2.wasTlsFallbackScsvSet());
+        assertSslSocket(clientSocket2,
+                true /* expectedWasFallbackScsvSet */, "TLSv1.1", "TLSv1", "SSLv3");
+
+        TlsFallbackDisabledScsvSSLSocket clientSocket3 =
+                (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(2);
+        assertSslSocket(clientSocket3, true /* expectedWasFallbackScsvSet */, "TLSv1", "SSLv3");
+
+        TlsFallbackDisabledScsvSSLSocket clientSocket4 =
+                (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(3);
+        assertSslSocket(clientSocket4, true /* expectedWasFallbackScsvSet */, "SSLv3");
+    }
+
+    public void testSslFallback_defaultProtocols() throws Exception {
+        TestSSLContext testSSLContext = TestSSLContext.create();
+
+        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
+        server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+        server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+        server.enqueue(new MockResponse().setBody("This required fallbacks"));
+        server.play();
+
+        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
+        // Keeps track of the client sockets created so that we can interrogate them.
+        final boolean disableFallbackScsv = true;
+        FallbackTestClientSocketFactory clientSocketFactory = new FallbackTestClientSocketFactory(
+                testSSLContext.clientContext.getSocketFactory(),
+                disableFallbackScsv);
+        connection.setSSLSocketFactory(clientSocketFactory);
+        assertEquals("This required fallbacks",
+                readAscii(connection.getInputStream(), Integer.MAX_VALUE));
+
+        // Confirm the server accepted a single connection.
+        RecordedRequest retry = server.takeRequest();
+        assertEquals(0, retry.getSequenceNumber());
+        assertEquals("TLSv1", retry.getSslProtocol());
+
+        // Confirm the client fallback looks ok.
+        List<SSLSocket> createdSockets = clientSocketFactory.getCreatedSockets();
+        assertEquals(3, createdSockets.size());
+        TlsFallbackDisabledScsvSSLSocket clientSocket1 =
+                (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(0);
+        assertSslSocket(clientSocket1,
+                false /* expectedWasFallbackScsvSet */, "TLSv1.2", "TLSv1.1", "TLSv1");
+
+        TlsFallbackDisabledScsvSSLSocket clientSocket2 =
+                (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(1);
+        assertSslSocket(clientSocket2, true /* expectedWasFallbackScsvSet */, "TLSv1.1", "TLSv1");
+
+        TlsFallbackDisabledScsvSSLSocket clientSocket3 =
+                (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(2);
+        assertSslSocket(clientSocket3, true /* expectedWasFallbackScsvSet */, "TLSv1");
+    }
+
+    private static void assertSslSocket(TlsFallbackDisabledScsvSSLSocket socket,
+            boolean expectedWasFallbackScsvSet, String... expectedEnabledProtocols) {
+        Set<String> enabledProtocols =
+                new HashSet<String>(Arrays.asList(socket.getEnabledProtocols()));
+        Set<String> expectedProtocolsSet = new HashSet<String>(Arrays.asList(expectedEnabledProtocols));
+        assertEquals(enabledProtocols, expectedProtocolsSet);
+        assertEquals(expectedWasFallbackScsvSet, socket.wasTlsFallbackScsvSet());
     }
 
     public void testInspectSslBeforeConnect() throws Exception {
diff --git a/luni/src/test/java/libcore/java/text/NumberFormatTest.java b/luni/src/test/java/libcore/java/text/NumberFormatTest.java
index 4ff063b..0678e96 100644
--- a/luni/src/test/java/libcore/java/text/NumberFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/NumberFormatTest.java
@@ -22,6 +22,7 @@
 import java.text.FieldPosition;
 import java.text.NumberFormat;
 import java.text.ParsePosition;
+import java.util.Currency;
 import java.util.Locale;
 
 public class NumberFormatTest extends junit.framework.TestCase {
@@ -80,6 +81,7 @@
 
     // Formatting percentages is confusing but deliberate.
     // Ensure we don't accidentally "fix" this.
+    // https://code.google.com/p/android/issues/detail?id=10333
     public void test_10333() throws Exception {
         NumberFormat nf = NumberFormat.getPercentInstance(Locale.US);
         assertEquals("15%", nf.format(0.15));
@@ -91,6 +93,7 @@
         }
     }
 
+    // https://code.google.com/p/android/issues/detail?id=62269
     public void test_62269() throws Exception {
         NumberFormat nf = NumberFormat.getNumberInstance(Locale.US);
         try {
@@ -126,4 +129,28 @@
             fail();
         } catch (NullPointerException expected) {}
     }
+
+    // https://code.google.com/p/android/issues/detail?id=79925
+    public void test_setCurrency() throws Exception {
+        NumberFormat nf = NumberFormat.getCurrencyInstance(Locale.US);
+        nf.setCurrency(Currency.getInstance("AMD"));
+        assertEquals("AMD50.00", nf.format(50.0));
+
+        DecimalFormatSymbols decimalFormatSymbols = ((DecimalFormat) nf).getDecimalFormatSymbols();
+        decimalFormatSymbols.setCurrencySymbol("");
+        ((DecimalFormat) nf).setDecimalFormatSymbols(decimalFormatSymbols);
+        assertEquals("50.00", nf.format(50.0));
+
+        nf.setCurrency(Currency.getInstance("AMD"));
+        assertEquals("AMD50.00", nf.format(50.0));
+
+        nf.setCurrency(Currency.getInstance("AMD"));
+        assertEquals("AMD50.00", nf.format(50.0));
+
+        nf.setCurrency(Currency.getInstance("USD"));
+        assertEquals("$50.00", nf.format(50.0));
+
+        nf.setCurrency(Currency.getInstance("AMD"));
+        assertEquals("AMD50.00", nf.format(50.0));
+    }
 }
diff --git a/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java b/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
index e890355..9e4b804 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
@@ -22,6 +22,7 @@
 import java.nio.charset.StandardCharsets;
 import java.security.Principal;
 import java.security.PublicKey;
+import java.security.cert.Certificate;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
@@ -30,21 +31,32 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
-import javax.net.ssl.DefaultHostnameVerifier;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
 import javax.security.auth.x500.X500Principal;
 import junit.framework.TestCase;
 
+/**
+ * Tests for the platform-default {@link HostnameVerifier} as provided by
+ * {@link HttpsURLConnection#getDefaultHostnameVerifier()}.
+ */
 public final class DefaultHostnameVerifierTest extends TestCase {
     private static final int ALT_UNKNOWN = 0;
     private static final int ALT_DNS_NAME = 2;
     private static final int ALT_IPA_NAME = 7;
 
-    private final DefaultHostnameVerifier verifier = new DefaultHostnameVerifier();
+    private final HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
 
     public void testVerify() {
-        assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("cn=imap.g.com")));
-        assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("cn=imap2.g.com")));
-        assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("cn=sub.imap.g.com")));
+        assertTrue(verifyWithServerCertificate(
+                "imap.g.com", new StubX509Certificate("cn=imap.g.com")));
+        assertFalse(verifyWithServerCertificate(
+                "imap.g.com", new StubX509Certificate("cn=imap2.g.com")));
+        assertFalse(verifyWithServerCertificate(
+                "imap.g.com", new StubX509Certificate("cn=sub.imap.g.com")));
     }
 
     /**
@@ -52,32 +64,33 @@
      * be used as the identity and the CN should be ignored.
      */
     public void testSubjectAltNameAndCn() {
-        assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("")
+        assertFalse(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
-        assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("cn=imap.g.com")
-                .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
-        assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("")
+        assertFalse(
+                verifyWithServerCertificate("imap.g.com", new StubX509Certificate("cn=imap.g.com")
+                        .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
+        assertTrue(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")));
     }
 
     public void testSubjectAltNameWithWildcard() {
-        assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("")
+        assertTrue(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_DNS_NAME, "*.g.com")));
     }
 
     public void testSubjectAltNameWithIpAddress() {
-        assertTrue(verifier.verify("1.2.3.4", new StubX509Certificate("")
+        assertTrue(verifyWithServerCertificate("1.2.3.4", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")));
-        assertFalse(verifier.verify("1.2.3.5", new StubX509Certificate("")
+        assertFalse(verifyWithServerCertificate("1.2.3.5", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")));
-        assertTrue(verifier.verify("192.168.100.1", new StubX509Certificate("")
+        assertTrue(verifyWithServerCertificate("192.168.100.1", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")
                 .addSubjectAlternativeName(ALT_IPA_NAME, "192.168.100.1")));
     }
 
     public void testUnknownSubjectAltName() {
         // Has unknown subject alternative names
-        assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("")
+        assertTrue(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -85,7 +98,7 @@
                 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
                 .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
-        assertTrue(verifier.verify("2.33.44.55", new StubX509Certificate("")
+        assertTrue(verifyWithServerCertificate("2.33.44.55", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -93,7 +106,7 @@
                 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
                 .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
-        assertFalse(verifier.verify("g.com", new StubX509Certificate("")
+        assertFalse(verifyWithServerCertificate("g.com", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -101,7 +114,7 @@
                 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
                 .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
-        assertFalse(verifier.verify("2.33.44.1", new StubX509Certificate("")
+        assertFalse(verifyWithServerCertificate("2.33.44.1", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -112,78 +125,78 @@
     }
 
     public void testWildcardMatchesWildcardSuffix() {
-        assertTrue(verifier.verifyHostName("b.c.d", "*.b.c.d"));
-        assertTrue(verifier.verifyHostName("imap.google.com", "*.imap.google.com"));
+        assertTrue(verifyWithDomainNamePattern("b.c.d", "*.b.c.d"));
+        assertTrue(verifyWithDomainNamePattern("imap.google.com", "*.imap.google.com"));
     }
 
     public void testWildcardMatchingSubstring() {
-        assertTrue(verifier.verifyHostName("b.c.d", "b*.c.d"));
-        assertTrue(verifier.verifyHostName("imap.google.com", "ima*.google.com"));
+        assertTrue(verifyWithDomainNamePattern("b.c.d", "b*.c.d"));
+        assertTrue(verifyWithDomainNamePattern("imap.google.com", "ima*.google.com"));
     }
 
     public void testWildcardMatchingEmptySubstring() {
-        assertTrue(verifier.verifyHostName("imap.google.com", "imap*.google.com"));
+        assertTrue(verifyWithDomainNamePattern("imap.google.com", "imap*.google.com"));
     }
 
     public void testWildcardMatchesChildDomain() {
-        assertFalse(verifier.verifyHostName("a.b.c.d", "*.c.d"));
+        assertFalse(verifyWithDomainNamePattern("a.b.c.d", "*.c.d"));
     }
 
     public void testWildcardsRejectedForSingleLabelPatterns() {
-        assertFalse(verifier.verifyHostName("d", "*"));
-        assertFalse(verifier.verifyHostName("d.", "*."));
-        assertFalse(verifier.verifyHostName("d", "d*"));
-        assertFalse(verifier.verifyHostName("d.", "d*."));
-        assertFalse(verifier.verifyHostName("d", "*d"));
-        assertFalse(verifier.verifyHostName("d.", "*d."));
-        assertFalse(verifier.verifyHostName("ddd", "d*d"));
-        assertFalse(verifier.verifyHostName("ddd.", "d*d."));
+        assertFalse(verifyWithDomainNamePattern("d", "*"));
+        assertFalse(verifyWithDomainNamePattern("d.", "*."));
+        assertFalse(verifyWithDomainNamePattern("d", "d*"));
+        assertFalse(verifyWithDomainNamePattern("d.", "d*."));
+        assertFalse(verifyWithDomainNamePattern("d", "*d"));
+        assertFalse(verifyWithDomainNamePattern("d.", "*d."));
+        assertFalse(verifyWithDomainNamePattern("ddd", "d*d"));
+        assertFalse(verifyWithDomainNamePattern("ddd.", "d*d."));
     }
 
     public void testVerifyHostName() {
-        assertTrue(verifier.verifyHostName("a.b.c.d", "a.b.c.d"));
-        assertTrue(verifier.verifyHostName("a.b.c.d", "*.b.c.d"));
-        assertFalse(verifier.verifyHostName("a.b.c.d", "*.*.c.d"));
-        assertTrue(verifier.verifyHostName("imap.google.com", "imap.google.com"));
-        assertFalse(verifier.verifyHostName("imap2.google.com", "imap.google.com"));
-        assertTrue(verifier.verifyHostName("imap.google.com", "*.google.com"));
-        assertTrue(verifier.verifyHostName("imap2.google.com", "*.google.com"));
-        assertFalse(verifier.verifyHostName("imap.google.com", "*.googl.com"));
-        assertFalse(verifier.verifyHostName("imap2.google2.com", "*.google3.com"));
-        assertFalse(verifier.verifyHostName("imap.google.com", "a*.google.com"));
-        assertFalse(verifier.verifyHostName("imap.google.com", "ix*.google.com"));
-        assertTrue(verifier.verifyHostName("imap.google.com", "iMap.Google.Com"));
-        assertTrue(verifier.verifyHostName("weird", "weird"));
-        assertFalse(verifier.verifyHostName("weird", "weird."));
+        assertTrue(verifyWithDomainNamePattern("a.b.c.d", "a.b.c.d"));
+        assertTrue(verifyWithDomainNamePattern("a.b.c.d", "*.b.c.d"));
+        assertFalse(verifyWithDomainNamePattern("a.b.c.d", "*.*.c.d"));
+        assertTrue(verifyWithDomainNamePattern("imap.google.com", "imap.google.com"));
+        assertFalse(verifyWithDomainNamePattern("imap2.google.com", "imap.google.com"));
+        assertTrue(verifyWithDomainNamePattern("imap.google.com", "*.google.com"));
+        assertTrue(verifyWithDomainNamePattern("imap2.google.com", "*.google.com"));
+        assertFalse(verifyWithDomainNamePattern("imap.google.com", "*.googl.com"));
+        assertFalse(verifyWithDomainNamePattern("imap2.google2.com", "*.google3.com"));
+        assertFalse(verifyWithDomainNamePattern("imap.google.com", "a*.google.com"));
+        assertFalse(verifyWithDomainNamePattern("imap.google.com", "ix*.google.com"));
+        assertTrue(verifyWithDomainNamePattern("imap.google.com", "iMap.Google.Com"));
+        assertTrue(verifyWithDomainNamePattern("weird", "weird"));
+        assertFalse(verifyWithDomainNamePattern("weird", "weird."));
 
         // Wildcards rejected for domain names consisting of fewer than two labels (excluding root).
-        assertFalse(verifier.verifyHostName("weird", "weird*"));
-        assertFalse(verifier.verifyHostName("weird", "*weird"));
-        assertFalse(verifier.verifyHostName("weird", "weird*."));
-        assertFalse(verifier.verifyHostName("weird", "weird.*"));
+        assertFalse(verifyWithDomainNamePattern("weird", "weird*"));
+        assertFalse(verifyWithDomainNamePattern("weird", "*weird"));
+        assertFalse(verifyWithDomainNamePattern("weird", "weird*."));
+        assertFalse(verifyWithDomainNamePattern("weird", "weird.*"));
     }
 
     public void testVerifyAbsoluteHostName() {
-        assertTrue(verifier.verifyHostName("a.b.c.d.", "a.b.c.d"));
-        assertTrue(verifier.verifyHostName("a.b.c.d.", "*.b.c.d"));
-        assertFalse(verifier.verifyHostName("a.b.c.d.", "*.*.c.d"));
-        assertTrue(verifier.verifyHostName("imap.google.com.", "imap.google.com"));
-        assertFalse(verifier.verifyHostName("imap2.google.com.", "imap.google.com"));
-        assertTrue(verifier.verifyHostName("imap.google.com.", "*.google.com"));
-        assertTrue(verifier.verifyHostName("imap2.google.com.", "*.google.com"));
-        assertFalse(verifier.verifyHostName("imap.google.com.", "*.googl.com"));
-        assertFalse(verifier.verifyHostName("imap2.google2.com.", "*.google3.com"));
-        assertFalse(verifier.verifyHostName("imap.google.com.", "a*.google.com"));
-        assertFalse(verifier.verifyHostName("imap.google.com.", "ix*.google.com"));
-        assertTrue(verifier.verifyHostName("imap.google.com.", "iMap.Google.Com"));
-        assertTrue(verifier.verifyHostName("weird.", "weird"));
-        assertTrue(verifier.verifyHostName("weird.", "weird."));
+        assertTrue(verifyWithDomainNamePattern("a.b.c.d.", "a.b.c.d"));
+        assertTrue(verifyWithDomainNamePattern("a.b.c.d.", "*.b.c.d"));
+        assertFalse(verifyWithDomainNamePattern("a.b.c.d.", "*.*.c.d"));
+        assertTrue(verifyWithDomainNamePattern("imap.google.com.", "imap.google.com"));
+        assertFalse(verifyWithDomainNamePattern("imap2.google.com.", "imap.google.com"));
+        assertTrue(verifyWithDomainNamePattern("imap.google.com.", "*.google.com"));
+        assertTrue(verifyWithDomainNamePattern("imap2.google.com.", "*.google.com"));
+        assertFalse(verifyWithDomainNamePattern("imap.google.com.", "*.googl.com"));
+        assertFalse(verifyWithDomainNamePattern("imap2.google2.com.", "*.google3.com"));
+        assertFalse(verifyWithDomainNamePattern("imap.google.com.", "a*.google.com"));
+        assertFalse(verifyWithDomainNamePattern("imap.google.com.", "ix*.google.com"));
+        assertTrue(verifyWithDomainNamePattern("imap.google.com.", "iMap.Google.Com"));
+        assertTrue(verifyWithDomainNamePattern("weird.", "weird"));
+        assertTrue(verifyWithDomainNamePattern("weird.", "weird."));
 
         // Wildcards rejected for domain names consisting of fewer than two labels (excluding root).
-        assertFalse(verifier.verifyHostName("weird.", "*weird"));
-        assertFalse(verifier.verifyHostName("weird.", "weird*"));
-        assertFalse(verifier.verifyHostName("weird.", "weird*."));
-        assertFalse(verifier.verifyHostName("weird.", "weird.*"));
+        assertFalse(verifyWithDomainNamePattern("weird.", "*weird"));
+        assertFalse(verifyWithDomainNamePattern("weird.", "weird*"));
+        assertFalse(verifyWithDomainNamePattern("weird.", "weird*."));
+        assertFalse(verifyWithDomainNamePattern("weird.", "weird.*"));
     }
 
     public void testSubjectOnlyCert() throws Exception {
@@ -207,8 +220,8 @@
                 + "rs2oQLwOLnuifH52ey9+tJguabo+brlYYigAuWWFEzJfBzikDkIwnE/L7wlrypIk\n"
                 + "taXDWI4=\n"
                 + "-----END CERTIFICATE-----");
-        assertTrue(verifier.verify("www.example.com", cert));
-        assertFalse(verifier.verify("www2.example.com", cert));
+        assertTrue(verifyWithServerCertificate("www.example.com", cert));
+        assertFalse(verifyWithServerCertificate("www2.example.com", cert));
     }
 
     public void testSubjectAltOnlyCert() throws Exception {
@@ -231,8 +244,8 @@
                 + "JPRynf9244Pn0Sr/wsnmdsTRFIFYynrc51hQ7DkwbUxpcaewkZzilru/SwZ3+pPT\n"
                 + "9JSqm5hJ1pg5WDlPkW7c/1VA0/141N52Q8MIU+2ZpuOj\n"
                 + "-----END CERTIFICATE-----");
-        assertTrue(verifier.verify("www.example.com", cert));
-        assertFalse(verifier.verify("www2.example.com", cert));
+        assertTrue(verifyWithServerCertificate("www.example.com", cert));
+        assertFalse(verifyWithServerCertificate("www2.example.com", cert));
     }
 
     public void testSubjectWithAltNamesCert() throws Exception {
@@ -258,10 +271,10 @@
                 + "hrTVypLSoRXuTB2aWilu4p6aNh84xTdyqo2avtNr2MiQMZIcdamBq8LdBIAShFXI\n"
                 + "h5G2eVGXH/Y=\n"
                 + "-----END CERTIFICATE-----");
-        assertFalse(verifier.verify("www.example.com", cert));
-        assertTrue(verifier.verify("www2.example.com", cert));
-        assertTrue(verifier.verify("www3.example.com", cert));
-        assertFalse(verifier.verify("www4.example.com", cert));
+        assertFalse(verifyWithServerCertificate("www.example.com", cert));
+        assertTrue(verifyWithServerCertificate("www2.example.com", cert));
+        assertTrue(verifyWithServerCertificate("www3.example.com", cert));
+        assertFalse(verifyWithServerCertificate("www4.example.com", cert));
     }
 
     public void testSubjectWithWildAltNamesCert() throws Exception {
@@ -286,11 +299,11 @@
                 + "Y3R0HZvKzNIU3pwAm69HCJoG+/9MZEIDJb0WJc5UygxDT45XE9zQMQe4dBOTaNXT\n"
                 + "+ntgaB62kE10HzrzpqXAgoAWxWK4RzFcUpBWw9qYq9xOCewJ\n"
                 + "-----END CERTIFICATE-----");
-        assertFalse(verifier.verify("www.example.com", cert));
-        assertFalse(verifier.verify("www2.example.com", cert));
-        assertTrue(verifier.verify("www.example2.com", cert));
-        assertTrue(verifier.verify("abc.example2.com", cert));
-        assertFalse(verifier.verify("www.example3.com", cert));
+        assertFalse(verifyWithServerCertificate("www.example.com", cert));
+        assertFalse(verifyWithServerCertificate("www2.example.com", cert));
+        assertTrue(verifyWithServerCertificate("www.example2.com", cert));
+        assertTrue(verifyWithServerCertificate("abc.example2.com", cert));
+        assertFalse(verifyWithServerCertificate("www.example3.com", cert));
     }
 
     public void testWildAltNameOnlyCert() throws Exception {
@@ -313,9 +326,9 @@
                 + "va++ow5r1VxQXFJc0ZPzsDo+6TlktoDHaRQJGMqQomqHWT4i7F5UZgf6BHGfEUPU\n"
                 + "qep+GsF3QRHSBtpObWkVDZNFvky3a1iZ2q25+hFIqQ==\n"
                 + "-----END CERTIFICATE-----");
-        assertTrue(verifier.verify("www.example.com", cert));
-        assertTrue(verifier.verify("www2.example.com", cert));
-        assertFalse(verifier.verify("www.example2.com", cert));
+        assertTrue(verifyWithServerCertificate("www.example.com", cert));
+        assertTrue(verifyWithServerCertificate("www2.example.com", cert));
+        assertFalse(verifyWithServerCertificate("www.example2.com", cert));
     }
 
     public void testAltIpOnlyCert() throws Exception {
@@ -338,8 +351,48 @@
                 + "WPjHQcWfpkFzAF5wyOq0kveVfx0g5xPhOVDd+U+q7WastbXICpCoHp9FxISmZVik\n"
                 + "sAyifp8agkYdzaSh55fFmKXlFnRsQw==\n"
                 + "-----END CERTIFICATE-----");
-        assertTrue(verifier.verify("192.168.10.1", cert));
-        assertFalse(verifier.verify("192.168.10.2", cert));
+        assertTrue(verifyWithServerCertificate("192.168.10.1", cert));
+        assertFalse(verifyWithServerCertificate("192.168.10.2", cert));
+    }
+
+    /**
+     * Verifies the provided hostname against the provided domain name pattern from server
+     * certificate.
+     */
+    private boolean verifyWithDomainNamePattern(String hostname, String pattern) {
+        StubSSLSession session = new StubSSLSession();
+
+        // Verify using a certificate where the pattern is in the CN
+        session.peerCertificates = new Certificate[] {
+                new StubX509Certificate("cn=" + pattern)
+        };
+        boolean resultWhenPatternInCn = verifier.verify(hostname, session);
+
+        // Verify using a certificate where the pattern is in a DNS SubjectAltName
+        session.peerCertificates = new Certificate[] {
+                new StubX509Certificate("ou=test")
+                        .addSubjectAlternativeName(ALT_DNS_NAME, pattern)
+        };
+        boolean resultWhenPatternInSubjectAltName = verifier.verify(hostname, session);
+
+        // Assert that in both cases the verifier gives the same result
+        if (resultWhenPatternInCn != resultWhenPatternInSubjectAltName) {
+            fail("Different results between pattern in CN and SubjectAltName."
+                    + " hostname : " + hostname + ", pattern: " + pattern
+                    + ", when pattern in CN: " + resultWhenPatternInCn
+                    + ", when pattern in SubjectAltName: " + resultWhenPatternInSubjectAltName);
+        }
+        return resultWhenPatternInCn;
+    }
+
+    /**
+     * Verifies the provided hostname against the provided server certificate.
+     */
+    private boolean verifyWithServerCertificate(String hostname, X509Certificate certificate) {
+        StubSSLSession session = new StubSSLSession();
+        session.peerCertificates =
+                (certificate != null) ? new Certificate[] {certificate} : new Certificate[0];
+        return verifier.verify(hostname, session);
     }
 
     X509Certificate parseCertificate(String encoded) throws Exception {
@@ -347,6 +400,117 @@
         return (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(in);
     }
 
+    private static class StubSSLSession implements SSLSession {
+
+        public Certificate[] peerCertificates = new Certificate[0];
+
+        @Override
+        public int getApplicationBufferSize() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String getCipherSuite() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public long getCreationTime() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public byte[] getId() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public long getLastAccessedTime() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Certificate[] getLocalCertificates() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Principal getLocalPrincipal() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int getPacketBufferSize() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+                throws SSLPeerUnverifiedException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+            return peerCertificates;
+        }
+
+        @Override
+        public String getPeerHost() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int getPeerPort() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String getProtocol() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public SSLSessionContext getSessionContext() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Object getValue(String name) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String[] getValueNames() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void invalidate() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isValid() {
+            return true;
+        }
+
+        @Override
+        public void putValue(String name, Object value) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void removeValue(String name) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
     private static class StubX509Certificate extends X509Certificate {
         private final X500Principal subjectX500Principal;
         private Collection<List<?>> subjectAlternativeNames;