Merge "Revert "Revert "Assert SNI type is SNI_HOST_NAME"""
diff --git a/luni/src/test/java/libcore/java/io/FileInputStreamTest.java b/luni/src/test/java/libcore/java/io/FileInputStreamTest.java
index b6650ff..398adbc 100644
--- a/luni/src/test/java/libcore/java/io/FileInputStreamTest.java
+++ b/luni/src/test/java/libcore/java/io/FileInputStreamTest.java
@@ -22,6 +22,11 @@
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.system.ErrnoException;
+import android.system.OsConstants;
 import junit.framework.TestCase;
 
 import libcore.io.IoUtils;
@@ -204,4 +209,36 @@
         FileInputStream input = new FileInputStream(file);
         assertTrue(input.available() == 0);
     }
+
+    // http://b/25695227
+    public void testFdLeakWhenOpeningDirectory() throws Exception {
+        File phile = IoUtils.createTemporaryDirectory("test_bug_25695227");
+
+        try {
+            new FileInputStream(phile);
+            fail();
+        } catch (FileNotFoundException expected) {
+        }
+
+        assertTrue(getOpenFdsForPrefix("test_bug_25695227").isEmpty());
+    }
+
+    private static List<Integer> getOpenFdsForPrefix(String path) throws Exception {
+        File[] fds = new File("/proc/self/fd").listFiles();
+        List<Integer> list = new ArrayList<>();
+        for (File fd : fds) {
+            try {
+                File fdPath = new File(android.system.Os.readlink(fd.getAbsolutePath()));
+                if (fdPath.getName().startsWith(path)) {
+                    list.add(Integer.valueOf(fd.getName()));
+                }
+            } catch (ErrnoException e) {
+                if (e.errno != OsConstants.ENOENT) {
+                    throw e.rethrowAsIOException();
+                }
+            }
+        }
+
+        return list;
+    }
 }
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
index 5e3a3d5..d50a170 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
@@ -661,6 +661,20 @@
         }
     }
 
+    public void test_SSLEngine_endpointVerification_Success() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        TestSSLEnginePair p = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
+            @Override
+            void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+                SSLParameters p = client.getSSLParameters();
+                p.setEndpointIdentificationAlgorithm("HTTPS");
+                client.setSSLParameters(p);
+            }
+        });
+        assertConnected(p);
+        c.close();
+    }
+
     public void test_SSLEngine_getEnableSessionCreation() throws Exception {
         TestSSLContext c = TestSSLContext.create();
         SSLEngine e = c.clientContext.createSSLEngine();
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
index 451fd66..d44cda9 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
@@ -334,6 +334,13 @@
         assertFalse(session.isValid());
     }
 
+    public void test_SSLSocket_getHandshakeSession() throws Exception {
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket ssl = (SSLSocket) sf.createSocket();
+        SSLSession session = ssl.getHandshakeSession();
+        assertNull(session);
+    }
+
     public void test_SSLSocket_startHandshake() throws Exception {
         final TestSSLContext c = TestSSLContext.create();
         SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host,
@@ -344,6 +351,7 @@
             @Override public Void call() throws Exception {
                 server.startHandshake();
                 assertNotNull(server.getSession());
+                assertNull(server.getHandshakeSession());
                 try {
                     server.getSession().getPeerCertificates();
                     fail();
@@ -394,8 +402,8 @@
         final TestSSLContext c = TestSSLContext.create();
         final ExecutorService executor = Executors.newSingleThreadExecutor();
 
-        final SSLSocket client1 = (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host,
-                                                                                       c.port);
+        final SSLSocket client1 = (SSLSocket) c.clientContext.getSocketFactory()
+                .createSocket(c.host.getHostName(), c.port);
         final SSLSocket server1 = (SSLSocket) c.serverSocket.accept();
         final Future<byte[]> future1 = executor.submit(new SSLServerSessionIdCallable(server1));
         client1.startHandshake();
@@ -407,8 +415,8 @@
         client1.close();
         server1.close();
 
-        final SSLSocket client2 = (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host,
-                                                                                       c.port);
+        final SSLSocket client2 = (SSLSocket) c.clientContext.getSocketFactory()
+                .createSocket(c.host.getHostName(), c.port);
         final SSLSocket server2 = (SSLSocket) c.serverSocket.accept();
         final Future<byte[]> future2 = executor.submit(new SSLServerSessionIdCallable(server2));
         client2.startHandshake();
@@ -586,6 +594,7 @@
                     assertSame(client, socket);
 
                     assertTrue(socket instanceof SSLSocket);
+                    assertNull(((SSLSocket) socket).getHandshakeSession());
 
                     synchronized (handshakeCompletedListenerCalled) {
                         handshakeCompletedListenerCalled[0] = true;
@@ -1016,6 +1025,14 @@
 
         assertEquals(p.getWantClientAuth(), ssl.getWantClientAuth());
         assertEquals(p.getNeedClientAuth(), ssl.getNeedClientAuth());
+
+        assertNull(p.getEndpointIdentificationAlgorithm());
+        p.setEndpointIdentificationAlgorithm(null);
+        assertNull(p.getEndpointIdentificationAlgorithm());
+        p.setEndpointIdentificationAlgorithm("HTTPS");
+        assertEquals("HTTPS", p.getEndpointIdentificationAlgorithm());
+        p.setEndpointIdentificationAlgorithm("FOO");
+        assertEquals("FOO", p.getEndpointIdentificationAlgorithm());
     }
 
     public void test_SSLSocket_setSSLParameters() throws Exception {
@@ -1199,6 +1216,83 @@
         server.close();
     }
 
+    public void test_SSLSocket_endpointIdentification_Success() throws Exception {
+        final TestSSLContext c = TestSSLContext.create();
+        SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket();
+        SSLParameters p = client.getSSLParameters();
+        p.setEndpointIdentificationAlgorithm("HTTPS");
+        client.connect(new InetSocketAddress(c.host, c.port));
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        Future<Void> future = executor.submit(new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                server.startHandshake();
+                assertNotNull(server.getSession());
+                try {
+                    server.getSession().getPeerCertificates();
+                    fail();
+                } catch (SSLPeerUnverifiedException expected) {
+                }
+                Certificate[] localCertificates = server.getSession().getLocalCertificates();
+                assertNotNull(localCertificates);
+                TestKeyStore.assertChainLength(localCertificates);
+                assertNotNull(localCertificates[0]);
+                TestSSLContext.assertCertificateInKeyStore(localCertificates[0],
+                                                           c.serverKeyStore);
+                return null;
+            }
+        });
+        executor.shutdown();
+        client.startHandshake();
+        assertNotNull(client.getSession());
+        assertNull(client.getSession().getLocalCertificates());
+        Certificate[] peerCertificates = client.getSession().getPeerCertificates();
+        assertNotNull(peerCertificates);
+        TestKeyStore.assertChainLength(peerCertificates);
+        assertNotNull(peerCertificates[0]);
+        TestSSLContext.assertCertificateInKeyStore(peerCertificates[0], c.serverKeyStore);
+        future.get();
+        client.close();
+        server.close();
+        c.close();
+    }
+
+    public void test_SSLSocket_endpointIdentification_Failure() throws Exception {
+        final TestSSLContext c = TestSSLContext.create();
+        SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket(
+                InetAddress.getByName("127.0.0.2"), c.port);
+        SSLParameters p = client.getSSLParameters();
+        p.setEndpointIdentificationAlgorithm("HTTPS");
+        client.setSSLParameters(p);
+        // client.connect(new InetSocketAddress(c.host, c.port));
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        Future<Void> future = executor.submit(new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                try {
+                    server.startHandshake();
+                    fail("Should receive SSLHandshakeException as server");
+                } catch (SSLHandshakeException expected) {
+                }
+                return null;
+            }
+        });
+        executor.shutdown();
+        try {
+            client.startHandshake();
+            fail("Should throw when hostname does not match expected");
+        } catch (SSLHandshakeException expected) {
+        } finally {
+            try {
+                future.get();
+            } finally {
+                client.close();
+                server.close();
+                c.close();
+            }
+        }
+    }
+
     public void test_SSLSocket_setSoTimeout_basic() throws Exception {
         ServerSocket listening = new ServerSocket(0);
 
diff --git a/ojluni/src/main/java/java/util/WeakHashMap.java b/ojluni/src/main/java/java/util/WeakHashMap.java
index a8ca523..652b053 100755
--- a/ojluni/src/main/java/java/util/WeakHashMap.java
+++ b/ojluni/src/main/java/java/util/WeakHashMap.java
@@ -243,9 +243,10 @@
     /**
      * A randomizing value associated with this instance that is applied to
      * hash code of keys to make hash collisions harder to find.
+     *
+     * This hash seed is only used if {@code useAltHashing} is true.
      */
     transient int hashSeed;
-    volatile boolean seedSet = false;
 
     @SuppressWarnings("unchecked")
     private Entry<K,V>[] newTable(int n) {
@@ -279,6 +280,11 @@
         threshold = (int)(capacity * loadFactor);
         useAltHashing = sun.misc.VM.isBooted() &&
                 (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
+        if (useAltHashing) {
+            hashSeed = sun.misc.Hashing.randomHashSeed(this);
+        } else {
+            hashSeed = 0;
+        }
     }
 
     /**
@@ -357,14 +363,6 @@
 
         int h;
         if (useAltHashing) {
-            if (!seedSet) {
-                synchronized(this) {
-                    if (!seedSet) {
-                        hashSeed = sun.misc.Hashing.randomHashSeed(this);
-                        seedSet = true;
-                    }
-                }
-            }
             h = hashSeed;
             if (k instanceof String) {
                 return sun.misc.Hashing.stringHash32((String) k);
@@ -572,6 +570,9 @@
         useAltHashing |= sun.misc.VM.isBooted() &&
                 (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
         boolean rehash = oldAltHashing ^ useAltHashing;
+        if (rehash) {
+            hashSeed = sun.misc.Hashing.randomHashSeed(this);
+        }
         transfer(oldTable, newTable, rehash);
         table = newTable;
 
diff --git a/ojluni/src/main/native/io_util_md.c b/ojluni/src/main/native/io_util_md.c
index 1df4b9d..4b1de48 100755
--- a/ojluni/src/main/native/io_util_md.c
+++ b/ojluni/src/main/native/io_util_md.c
@@ -83,6 +83,7 @@
             fstat(fd, &stat);
 
             if (S_ISDIR(stat.st_mode)) {
+              close(fd);
               errno = EISDIR; // For Exception message
               throwFileNotFoundException(env, path);
               return;
diff --git a/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java
index f7b8337..9eb9e6c 100644
--- a/support/src/test/java/libcore/java/security/StandardNames.java
+++ b/support/src/test/java/libcore/java/security/StandardNames.java
@@ -758,6 +758,8 @@
         addBoth(   "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384");
         addOpenSsl("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");
         addOpenSsl("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384");
+        addOpenSsl("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256");
+        addOpenSsl("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256");
 
         // Pre-Shared Key (PSK) cipher suites
         addOpenSsl("TLS_PSK_WITH_RC4_128_SHA");
@@ -765,6 +767,7 @@
         addOpenSsl("TLS_PSK_WITH_AES_256_CBC_SHA");
         addOpenSsl("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA");
         addOpenSsl("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA");
+        addOpenSsl("TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256");
 
         // RFC 5746's Signaling Cipher Suite Value to indicate a request for secure renegotiation
         addBoth(CIPHER_SUITE_SECURE_RENEGOTIATION);
@@ -924,8 +927,10 @@
                             "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
                             "SSL_RSA_WITH_RC4_128_MD5",
                             "TLS_EMPTY_RENEGOTIATION_INFO_SCSV")
-            : Arrays.asList("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+            : Arrays.asList("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
+                            "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                             "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+                            "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                             "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                             "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                             "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
@@ -948,6 +953,7 @@
     // NOTE: This list needs to be kept in sync with Javadoc of javax.net.ssl.SSLSocket and
     // javax.net.ssl.SSLEngine.
     public static final List<String> CIPHER_SUITES_DEFAULT_PSK = Arrays.asList(
+            "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
             "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
             "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
             "TLS_PSK_WITH_AES_128_CBC_SHA",
@@ -966,7 +972,8 @@
                                               "AES_128_CBC",
                                               "AES_256_CBC",
                                               "AES_128_GCM",
-                                              "AES_256_GCM"));
+                                              "AES_256_GCM",
+                                              "CHACHA20_POLY1305"));
 
     private static final Set<String> PERMITTED_DEFAULT_MACS =
             new HashSet<String>(Arrays.asList("SHA",
diff --git a/support/src/test/java/libcore/javax/net/ssl/TestSSLContext.java b/support/src/test/java/libcore/javax/net/ssl/TestSSLContext.java
index 8173e9d..1daa2a1 100644
--- a/support/src/test/java/libcore/javax/net/ssl/TestSSLContext.java
+++ b/support/src/test/java/libcore/javax/net/ssl/TestSSLContext.java
@@ -33,6 +33,7 @@
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509ExtendedTrustManager;
 import javax.net.ssl.X509TrustManager;
 import junit.framework.Assert;
 import libcore.java.security.StandardNames;
@@ -83,8 +84,8 @@
     public final char[] serverStorePassword;
     public final KeyManager[] clientKeyManagers;
     public final KeyManager[] serverKeyManagers;
-    public final X509TrustManager clientTrustManager;
-    public final X509TrustManager serverTrustManager;
+    public final X509ExtendedTrustManager clientTrustManager;
+    public final X509ExtendedTrustManager serverTrustManager;
     public final SSLContext clientContext;
     public final SSLContext serverContext;
     public final SSLServerSocket serverSocket;
@@ -97,8 +98,8 @@
                            char[] serverStorePassword,
                            KeyManager[] clientKeyManagers,
                            KeyManager[] serverKeyManagers,
-                           X509TrustManager clientTrustManager,
-                           X509TrustManager serverTrustManager,
+                           X509ExtendedTrustManager clientTrustManager,
+                           X509ExtendedTrustManager serverTrustManager,
                            SSLContext clientContext,
                            SSLContext serverContext,
                            SSLServerSocket serverSocket,
@@ -189,8 +190,8 @@
                                       serverKeyStore, serverStorePassword,
                                       clientKeyManagers,
                                       serverKeyManagers,
-                                      (X509TrustManager) clientTrustManagers,
-                                      (X509TrustManager) serverTrustManagers,
+                                      (X509ExtendedTrustManager) clientTrustManagers,
+                                      (X509ExtendedTrustManager) serverTrustManagers,
                                       clientContext, serverContext,
                                       serverSocket, host, port);
         } catch (RuntimeException e) {
diff --git a/support/src/test/java/libcore/javax/net/ssl/TestTrustManager.java b/support/src/test/java/libcore/javax/net/ssl/TestTrustManager.java
index dc4bb28..b703984 100644
--- a/support/src/test/java/libcore/javax/net/ssl/TestTrustManager.java
+++ b/support/src/test/java/libcore/javax/net/ssl/TestTrustManager.java
@@ -20,6 +20,7 @@
 import java.net.Socket;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509ExtendedTrustManager;
 import javax.net.ssl.X509TrustManager;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
@@ -28,15 +29,16 @@
 
 /**
  * TestTrustManager is a simple proxy class that wraps an existing
- * X509TrustManager to provide debug logging and recording of
+ * X509ExtendedTrustManager to provide debug logging and recording of
  * values.
  */
-public final class TestTrustManager implements X509TrustManager {
+public final class TestTrustManager extends X509ExtendedTrustManager {
 
     private static final boolean LOG = false;
     private static final PrintStream out = LOG ? System.out : new NullPrintStream();
 
     private final X509TrustManager trustManager;
+    private final X509ExtendedTrustManager extendedTrustManager;
 
     public static TrustManager[] wrap(TrustManager[] trustManagers) {
         TrustManager[] result = trustManagers.clone();
@@ -47,14 +49,23 @@
     }
 
     public static TrustManager wrap(TrustManager trustManager) {
-        if (trustManager instanceof X509TrustManager) {
+        if (trustManager instanceof X509ExtendedTrustManager) {
+            return new TestTrustManager((X509ExtendedTrustManager) trustManager);
+        } else if (trustManager instanceof X509TrustManager) {
             return new TestTrustManager((X509TrustManager) trustManager);
         }
         return trustManager;
     }
 
+    public TestTrustManager(X509ExtendedTrustManager trustManager) {
+        out.println("TestTrustManager.<init> extendedTrustManager=" + trustManager);
+        this.extendedTrustManager = trustManager;
+        this.trustManager = trustManager;
+    }
+
     public TestTrustManager(X509TrustManager trustManager) {
         out.println("TestTrustManager.<init> trustManager=" + trustManager);
+        this.extendedTrustManager = null;
         this.trustManager = trustManager;
     }
 
@@ -73,6 +84,50 @@
         }
     }
 
+    @Override
+    public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
+            throws CertificateException {
+        if (extendedTrustManager == null) {
+            out.print("(fallback to X509TrustManager) ");
+            checkClientTrusted(chain, authType);
+            return;
+        }
+        out.print("TestTrustManager.checkClientTrusted "
+                + "chain=" + chain.length + " "
+                + "authType=" + authType + " "
+                + "socket=" + socket + " ");
+        try {
+            assertClientAuthType(authType);
+            extendedTrustManager.checkClientTrusted(chain, authType, socket);
+            out.println("OK");
+        } catch (CertificateException e) {
+            e.printStackTrace(out);
+            throw e;
+        }
+    }
+
+    @Override
+    public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
+            throws CertificateException {
+        if (extendedTrustManager == null) {
+            out.print("(fallback to X509TrustManager) ");
+            checkClientTrusted(chain, authType);
+            return;
+        }
+        out.print("TestTrustManager.checkClientTrusted "
+                + "chain=" + chain.length + " "
+                + "authType=" + authType + " "
+                + "engine=" + engine + " ");
+        try {
+            assertClientAuthType(authType);
+            extendedTrustManager.checkClientTrusted(chain, authType, engine);
+            out.println("OK");
+        } catch (CertificateException e) {
+            e.printStackTrace(out);
+            throw e;
+        }
+    }
+
     private void assertClientAuthType(String authType) {
         if (!StandardNames.CLIENT_AUTH_TYPES.contains(authType)) {
             throw new AssertionError("Unexpected client auth type " + authType);
@@ -94,6 +149,50 @@
         }
     }
 
+    @Override
+    public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
+            throws CertificateException {
+        if (extendedTrustManager == null) {
+            out.print("(fallback to X509TrustManager) ");
+            checkServerTrusted(chain, authType);
+            return;
+        }
+        out.print("TestTrustManager.checkServerTrusted "
+                + "chain=" + chain.length + " "
+                + "authType=" + authType + " "
+                + "socket=" + socket.toString() + " ");
+        try {
+            assertServerAuthType(authType);
+            extendedTrustManager.checkServerTrusted(chain, authType, socket);
+            out.println("OK");
+        } catch (CertificateException e) {
+            e.printStackTrace(out);
+            throw e;
+        }
+    }
+
+    @Override
+    public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
+            throws CertificateException {
+        if (extendedTrustManager == null) {
+            out.print("(fallback to X509TrustManager) ");
+            checkServerTrusted(chain, authType);
+            return;
+        }
+        out.print("TestTrustManager.checkServerTrusted "
+                + "chain=" + chain.length + " "
+                + "authType=" + authType + " "
+                + "engine=" + engine.toString() + " ");
+        try {
+            assertServerAuthType(authType);
+            extendedTrustManager.checkServerTrusted(chain, authType, engine);
+            out.println("OK");
+        } catch (CertificateException e) {
+            e.printStackTrace(out);
+            throw e;
+        }
+    }
+
     private void assertServerAuthType(String authType) {
         if (!StandardNames.SERVER_AUTH_TYPES.contains(authType)) {
             throw new AssertionError("Unexpected server auth type " + authType);