Merge "reflect: Clean up Proxy to remove dead code"
diff --git a/benchmarks/src/benchmarks/ReferenceBenchmark.java b/benchmarks/src/benchmarks/ReferenceBenchmark.java
index 80bcb5e..f1b84a3 100644
--- a/benchmarks/src/benchmarks/ReferenceBenchmark.java
+++ b/benchmarks/src/benchmarks/ReferenceBenchmark.java
@@ -18,6 +18,9 @@
 
 import java.lang.ref.PhantomReference;
 import java.lang.ref.ReferenceQueue;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Benchmark to evaluate the performance of References.
@@ -67,4 +70,58 @@
             }
         }
     }
+
+    // How fast can references can be implicitly allocated, enqueued, and
+    // removed?
+    public void timeAllocImplicitEnqueueAndRemove(int reps) {
+        ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+        List<Object> refs = new ArrayList<Object>();
+        for (int i = 0; i < reps; i++) {
+            refs.add(new PhantomReference<Object>(new Object(), queue));
+        }
+        Runtime.getRuntime().gc();
+        for (int i = 0; i < reps; i++) {
+            try {
+                queue.remove();
+            } catch (InterruptedException ie) {
+                i--;
+            }
+        }
+    }
+
+    static private class FinalizableObject {
+        AtomicInteger count;
+
+        public FinalizableObject(AtomicInteger count) {
+            this.count = count;
+        }
+
+        @Override
+        protected void finalize() {
+            count.incrementAndGet();
+        }
+    }
+
+    // How fast does finalization run?
+    public void timeFinalization(int reps) {
+        // Allocate a bunch of finalizable objects.
+        int n = reps;
+        AtomicInteger count = new AtomicInteger(0);
+        for (int i = 0; i < n; i++) {
+            new FinalizableObject(count);
+        }
+
+        // Run GC so the objects will be collected for finalization.
+        Runtime.getRuntime().gc();
+
+        // Wait for finalization.
+        Runtime.getRuntime().runFinalization();
+
+        // Double check all the objects were finalized.
+        int got = count.get();
+        if (n != got) {
+            throw new IllegalStateException(
+                    String.format("Only %i of %i objects finalized?", got, n));
+        }
+    }
 }
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index cf6c852..9b5439e 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -1448,8 +1448,8 @@
   description: "OkHttp tests that fail on Wear devices due to a lack of memory",
   bug: 20055487,
   names: [
-    "com.squareup.okhttp.internal.spdy.Http20Draft09Test#tooLargeDataFrame",
-    "com.squareup.okhttp.internal.spdy.Spdy3Test#tooLargeDataFrame"
+    "com.squareup.okhttp.internal.framed.Http2Test#tooLargeDataFrame",
+    "com.squareup.okhttp.internal.framed.Spdy3Test#tooLargeDataFrame"
   ]
 },
 {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java
index 0bc8920..173e37e 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java
@@ -602,7 +602,7 @@
 
         try {
             JarFile jar = new JarFile(signedFile);
-            JarEntry entry = new JarEntry(entryName3);
+            JarEntry entry = jar.getJarEntry(entryName3);
             entry.setSize(1076);
             InputStream in = jar.getInputStream(entry);
             // BEGIN android-added
diff --git a/libart/src/main/java/java/lang/Daemons.java b/libart/src/main/java/java/lang/Daemons.java
index e59643c..15a8ab6 100644
--- a/libart/src/main/java/java/lang/Daemons.java
+++ b/libart/src/main/java/java/lang/Daemons.java
@@ -157,20 +157,9 @@
                 } catch (OutOfMemoryError e) {
                     continue;
                 }
-                enqueue(list);
+                ReferenceQueue.enqueuePending(list);
             }
         }
-
-        private void enqueue(Reference<?> list) {
-            Reference<?> start = list;
-            do {
-                // pendingNext is owned by the GC so no synchronization is required.
-                Reference<?> next = list.pendingNext;
-                list.pendingNext = null;
-                list.enqueueInternal();
-                list = next;
-            } while (list != start);
-        }
     }
 
     private static class FinalizerDaemon extends Daemon {
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/java/net/URLTest.java b/luni/src/test/java/libcore/java/net/URLTest.java
index 07e651c..2717d14 100644
--- a/luni/src/test/java/libcore/java/net/URLTest.java
+++ b/luni/src/test/java/libcore/java/net/URLTest.java
@@ -22,6 +22,8 @@
 import java.net.URISyntaxException;
 import java.net.URI;
 import java.net.URL;
+
+import dalvik.system.BlockGuard;
 import junit.framework.TestCase;
 import libcore.util.SerializationTester;
 
@@ -712,35 +714,40 @@
         assertEquals("a_b.c.d.net", url.getHost());
     }
 
-    // http://b/7369778
-    public void testToURILeniantThrowsURISyntaxExceptionWithPartialTrailingEscape()
-            throws Exception {
-        // make sure if there a partial trailing escape that we don't throw the wrong exception
-        URL[] badUrls = new URL[] {
-            new URL("http://example.com/?foo=%%bar"),
-            new URL("http://example.com/?foo=%%bar%"),
-            new URL("http://example.com/?foo=%%bar%2"),
-            new URL("http://example.com/?foo=%%bar%%"),
-            new URL("http://example.com/?foo=%%bar%%%"),
-            new URL("http://example.com/?foo=%%bar%%%%"),
-        };
-        for (URL badUrl : badUrls) {
-            try {
-                badUrl.toURILenient();
-                fail();
-            } catch (URISyntaxException expected) {
+    // http://b/26895969
+    // http://b/26798800
+    public void testHashCodeAndEqualsDoesNotPerformNetworkIo() throws Exception {
+        final BlockGuard.Policy oldPolicy = BlockGuard.getThreadPolicy();
+        BlockGuard.setThreadPolicy(new BlockGuard.Policy() {
+            @Override
+            public void onWriteToDisk() {
+                fail("Blockguard.Policy.onWriteToDisk");
             }
-        }
 
-        // make sure we properly handle an normal escape at the end of a string
-        String[] goodUrls = new String[] {
-            "http://example.com/?foo=bar",
-            "http://example.com/?foo=bar%20",
-        };
-        for (String goodUrl : goodUrls) {
-            assertEquals(new URI(goodUrl), new URL(goodUrl).toURILenient());
+            @Override
+            public void onReadFromDisk() {
+                fail("Blockguard.Policy.onReadFromDisk");
+            }
+
+            @Override
+            public void onNetwork() {
+                fail("Blockguard.Policy.onNetwork");
+            }
+
+            @Override
+            public int getPolicyMask() {
+                return 0;
+            }
+        });
+
+        try {
+            URL url = new URL("http://www.google.com/");
+            URL url2 = new URL("http://www.nest.com/");
+
+            url.equals(url2);
+            url2.hashCode();
+        } finally {
+            BlockGuard.setThreadPolicy(oldPolicy);
         }
     }
-
-    // Adding a new test? Consider adding an equivalent test to URITest.java
 }
diff --git a/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java
index c901a08..f87cf4d 100644
--- a/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java
@@ -19,8 +19,10 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.reflect.Field;
 import java.net.ConnectException;
 import java.net.Socket;
+import java.net.SocketImpl;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
@@ -264,4 +266,14 @@
     ss.close();
     sc.close();
   }
+
+  public void test_Socket_impl_notNull() throws Exception {
+    SocketChannel sc = SocketChannel.open();
+    Socket socket = sc.socket();
+    Field f_impl = Socket.class.getDeclaredField("impl");
+    f_impl.setAccessible(true);
+    Object implFieldValue = f_impl.get(socket);
+    assertNotNull(implFieldValue);
+    assertTrue(implFieldValue instanceof SocketImpl);
+  }
 }
diff --git a/luni/src/test/java/libcore/java/util/CalendarTest.java b/luni/src/test/java/libcore/java/util/CalendarTest.java
index f7113d0..7f30b3d 100644
--- a/luni/src/test/java/libcore/java/util/CalendarTest.java
+++ b/luni/src/test/java/libcore/java/util/CalendarTest.java
@@ -210,8 +210,22 @@
     }
 
     // https://code.google.com/p/android/issues/detail?id=45877
-    public void test_clear_45877() {
+    public void test_clear_45877_morning() {
       GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("America/Los_Angeles"));
+      // 3rd February 2016 05:32:40.000 America/Los_Angeles time.
+      cal.setTimeInMillis(1454506360000L);
+      checkClear(cal, 0, 28800000);
+    }
+
+    // https://code.google.com/p/android/issues/detail?id=45877
+    public void test_clear_45877_afternoon() {
+      GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("America/Los_Angeles"));
+      // 3rd February 2016 12:32:40.000 America/Los_Angeles time.
+      cal.setTimeInMillis(1454531560000L);
+      checkClear(cal, 12, 72000000);
+    }
+
+    private void checkClear(GregorianCalendar cal, int expectedHourOfDay, long expectedMillis) {
       cal.set(Calendar.YEAR, 1970);
       cal.set(Calendar.MONTH, Calendar.JANUARY);
       cal.set(Calendar.DAY_OF_MONTH, 1);
@@ -226,7 +240,7 @@
       assertFalse(cal.isSet(Calendar.HOUR_OF_DAY));
 
       // When we call get, unset fields are computed.
-      assertEquals(0, cal.get(Calendar.HOUR_OF_DAY));
+      assertEquals(expectedHourOfDay, cal.get(Calendar.HOUR_OF_DAY));
       // And set fields stay the same.
       assertEquals(1, cal.get(Calendar.DAY_OF_MONTH));
 
@@ -234,7 +248,7 @@
       assertTrue(cal.isSet(Calendar.DAY_OF_MONTH));
       assertTrue(cal.isSet(Calendar.HOUR_OF_DAY));
 
-      assertEquals(28800000, cal.getTimeInMillis());
+      assertEquals(expectedMillis, cal.getTimeInMillis());
 
       cal.set(Calendar.HOUR_OF_DAY, 1);
       assertEquals(32400000, cal.getTimeInMillis());
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 278d44e..d44cda9 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
@@ -68,6 +68,7 @@
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.StandardConstants;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.X509KeyManager;
 import javax.net.ssl.X509TrustManager;
@@ -333,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,
@@ -343,6 +351,7 @@
             @Override public Void call() throws Exception {
                 server.startHandshake();
                 assertNotNull(server.getSession());
+                assertNull(server.getHandshakeSession());
                 try {
                     server.getSession().getPeerCertificates();
                     fail();
@@ -393,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();
@@ -406,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();
@@ -585,6 +594,7 @@
                     assertSame(client, socket);
 
                     assertTrue(socket instanceof SSLSocket);
+                    assertNull(((SSLSocket) socket).getHandshakeSession());
 
                     synchronized (handshakeCompletedListenerCalled) {
                         handshakeCompletedListenerCalled[0] = true;
@@ -1015,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 {
@@ -1198,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);
 
@@ -1811,6 +1906,7 @@
         assertNotNull(requestedNames);
         assertEquals(1, requestedNames.size());
         SNIServerName serverName = requestedNames.get(0);
+        assertEquals(StandardConstants.SNI_HOST_NAME, serverName.getType());
         assertTrue(serverName instanceof SNIHostName);
         SNIHostName serverHostName = (SNIHostName) serverName;
         assertEquals("www.example.com", serverHostName.getAsciiName());
diff --git a/ojluni/src/main/java/java/lang/ref/Reference.java b/ojluni/src/main/java/java/lang/ref/Reference.java
index 32abb9b..160eca1 100755
--- a/ojluni/src/main/java/java/lang/ref/Reference.java
+++ b/ojluni/src/main/java/java/lang/ref/Reference.java
@@ -26,8 +26,6 @@
 
 package java.lang.ref;
 
-import sun.misc.Cleaner;
-
 
 /**
  * Abstract base class for reference objects.  This class defines the
@@ -57,13 +55,32 @@
     private static boolean slowPathEnabled = false;
 
     volatile T referent;         /* Treated specially by GC */
-    volatile ReferenceQueue<? super T> queue;
-    volatile Reference queueNext;
+    final ReferenceQueue<? super T> queue;
+
+    /*
+     * This field forms a singly-linked list of reference objects that have
+     * been enqueued. The queueNext field is non-null if and only if this
+     * reference has been enqueued. After this reference has been enqueued and
+     * before it has been removed from its queue, the queueNext field points
+     * to the next reference on the queue. The last reference on a queue
+     * points to itself. Once this reference has been removed from the
+     * reference queue, the queueNext field points to the
+     * ReferenceQueue.sQueueNextUnenqueued sentinel reference object for the
+     * rest of this reference's lifetime.
+     * <p>
+     * Access to the queueNext field is guarded by synchronization on a lock
+     * internal to 'queue'.
+     */
+    Reference queueNext;
 
     /**
-     * @hide
+     * The pendingNext field is initially set by the GC. After the GC forms a
+     * complete circularly linked list, the list is handed off to the
+     * ReferenceQueueDaemon using the ReferenceQueue.class lock. The
+     * ReferenceQueueDaemon can then read the pendingNext fields without
+     * additional synchronization.
      */
-    public volatile Reference<?> pendingNext;
+    Reference<?> pendingNext;
 
     /* -- Referent accessor and setters -- */
 
@@ -105,26 +122,11 @@
      *           been enqueued
      */
     public boolean isEnqueued() {
-        return queueNext != null;
-    }
-
-    /** @hide */
-    public final synchronized boolean enqueueInternal() {
-        if (this instanceof Cleaner) {
-            // If this reference is a Cleaner, then simply invoke the clean method instead
-            // of enqueueing it in the queue. Cleaners are associated with dummy queues that
-            // are never polled and objects are never enqueued on them.
-            Cleaner cl = (sun.misc.Cleaner) this;
-            cl.clean();
-            return true;
-        }
-
-        if (queue != null && queueNext == null) {
-            queue.enqueue(this);
-            queue = null;
-            return true;
-        }
-        return false;
+        // Contrary to what the documentation says, this method returns false
+        // after this reference object has been removed from its queue
+        // (b/26647823). ReferenceQueue.isEnqueued preserves this historically
+        // incorrect behavior.
+        return queue != null && queue.isEnqueued(this);
     }
 
     /**
@@ -139,7 +141,7 @@
      *           it was not registered with a queue when it was created
      */
     public boolean enqueue() {
-       return enqueueInternal();
+       return queue != null && queue.enqueue(this);
     }
 
 
diff --git a/ojluni/src/main/java/java/lang/ref/ReferenceQueue.java b/ojluni/src/main/java/java/lang/ref/ReferenceQueue.java
index 1bf4941..80c65d9 100755
--- a/ojluni/src/main/java/java/lang/ref/ReferenceQueue.java
+++ b/ojluni/src/main/java/java/lang/ref/ReferenceQueue.java
@@ -26,6 +26,8 @@
 
 package java.lang.ref;
 
+import sun.misc.Cleaner;
+
 /**
  * Reference queues, to which registered reference objects are appended by the
  * garbage collector after the appropriate reachability changes are detected.
@@ -35,6 +37,10 @@
  */
 public class ReferenceQueue<T> {
 
+    // Reference.queueNext will be set to sQueueNextUnenqueued to indicate
+    // when a reference has been enqueued and removed from its queue.
+    private static final Reference sQueueNextUnenqueued = new PhantomReference(null, null);
+
     // NOTE: This implementation of ReferenceQueue is FIFO (queue-like) whereas
     // the OpenJdk implementation is LIFO (stack-like).
     private Reference<? extends T> head = null;
@@ -47,18 +53,68 @@
      */
     public ReferenceQueue() { }
 
-    boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
-        synchronized (lock) {
-            if (tail == null) {
-                head = r;
-            } else {
-                tail.queueNext = r;
-            }
-            tail = r;
-            tail.queueNext = r;
-            lock.notifyAll();
+    /**
+     * Enqueue the given reference onto this queue.
+     * The caller is responsible for ensuring the lock is held on this queue,
+     * and for calling notifyAll on this queue after the reference has been
+     * enqueued. Returns true if the reference was enqueued successfully,
+     * false if the reference had already been enqueued.
+     * @GuardedBy("lock")
+     */
+    private boolean enqueueLocked(Reference<? extends T> r) {
+        // Verify the reference has not already been enqueued.
+        if (r.queueNext != null) {
+            return false;
+        }
+
+        if (r instanceof Cleaner) {
+            // If this reference is a Cleaner, then simply invoke the clean method instead
+            // of enqueueing it in the queue. Cleaners are associated with dummy queues that
+            // are never polled and objects are never enqueued on them.
+            Cleaner cl = (sun.misc.Cleaner) r;
+            cl.clean();
+
+            // Update queueNext to indicate that the reference has been
+            // enqueued, but is now removed from the queue.
+            r.queueNext = sQueueNextUnenqueued;
             return true;
         }
+
+        if (tail == null) {
+            head = r;
+        } else {
+            tail.queueNext = r;
+        }
+        tail = r;
+        tail.queueNext = r;
+        return true;
+    }
+
+    /**
+     * Test if the given reference object has been enqueued but not yet
+     * removed from the queue, assuming this is the reference object's queue.
+     */
+    boolean isEnqueued(Reference<? extends T> reference) {
+        synchronized (lock) {
+            return reference.queueNext != null && reference.queueNext != sQueueNextUnenqueued;
+        }
+    }
+
+    /**
+     * Enqueue the reference object on the receiver.
+     *
+     * @param reference
+     *            reference object to be enqueued.
+     * @return true if the reference was enqueued.
+     */
+    boolean enqueue(Reference<? extends T> reference) {
+        synchronized (lock) {
+            if (enqueueLocked(reference)) {
+                lock.notifyAll();
+                return true;
+            }
+            return false;
+        }
     }
 
     // @GuardedBy("lock")
@@ -71,7 +127,10 @@
             } else {
                 head = head.queueNext;
             }
-            r.queueNext = null;
+
+            // Update queueNext to indicate that the reference has been
+            // enqueued, but is now removed from the queue.
+            r.queueNext = sQueueNextUnenqueued;
             return r;
         }
 
@@ -150,7 +209,51 @@
         return remove(0);
     }
 
-    /** @hide */
+    /**
+     * Enqueue the given list of currently pending (unenqueued) references.
+     *
+     * @hide
+     */
+    public static void enqueuePending(Reference<?> list) {
+        Reference<?> start = list;
+        do {
+            ReferenceQueue queue = list.queue;
+            if (queue == null) {
+                Reference<?> next = list.pendingNext;
+
+                // Make pendingNext a self-loop to preserve the invariant that
+                // once enqueued, pendingNext is non-null -- without leaking
+                // the object pendingNext was previously pointing to.
+                list.pendingNext = list;
+                list = next;
+            } else {
+                // To improve performance, we try to avoid repeated
+                // synchronization on the same queue by batching enqueue of
+                // consecutive references in the list that have the same
+                // queue.
+                synchronized (queue.lock) {
+                    do {
+                        Reference<?> next = list.pendingNext;
+
+                        // Make pendingNext a self-loop to preserve the
+                        // invariant that once enqueued, pendingNext is
+                        // non-null -- without leaking the object pendingNext
+                        // was previously pointing to.
+                        list.pendingNext = list;
+                        queue.enqueueLocked(list);
+                        list = next;
+                    } while (list != start && list.queue == queue);
+                    queue.lock.notifyAll();
+                }
+            }
+        } while (list != start);
+    }
+
+    /**
+     * List of references that the GC says need to be enqueued.
+     * Protected by ReferenceQueue.class lock.
+     * @hide
+     */
     public static Reference<?> unenqueued = null;
 
     static void add(Reference<?> list) {
diff --git a/ojluni/src/main/java/java/net/URI.java b/ojluni/src/main/java/java/net/URI.java
index b3dbaad..5a78d6e 100755
--- a/ojluni/src/main/java/java/net/URI.java
+++ b/ojluni/src/main/java/java/net/URI.java
@@ -34,7 +34,6 @@
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CharsetEncoder;
 import java.nio.charset.CoderResult;
 import java.nio.charset.CodingErrorAction;
 import java.nio.charset.CharacterCodingException;
@@ -43,7 +42,6 @@
 
 import java.lang.Character;             // for javadoc
 import java.lang.NullPointerException;  // for javadoc
-import libcore.net.UriCodec;
 
 
 /**
@@ -468,40 +466,6 @@
 public final class URI
     implements Comparable<URI>, Serializable
 {
-    static final String UNRESERVED = "_-!.~\'()*";
-    static final String PUNCTUATION = ",;:$&+=";
-    static final UriCodec AUTHORITY_ENCODER = new PartEncoder("@[]");
-
-    /** for java.net.URL, which foolishly combines these two parts */
-    static final UriCodec FILE_AND_QUERY_ENCODER = new PartEncoder("/@?");
-
-    /** for query, fragment, and scheme-specific part */
-    static final UriCodec ALL_LEGAL_ENCODER = new PartEncoder("?/[]@");
-
-    /**
-     * Encodes the unescaped characters of {@code s} that are not permitted.
-     * Permitted characters are:
-     * <ul>
-     *   <li>Unreserved characters in <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>.
-     *   <li>{@code extraOkayChars},
-     *   <li>non-ASCII, non-control, non-whitespace characters
-     * </ul>
-     */
-    private static class PartEncoder extends UriCodec {
-        private final String extraLegalCharacters;
-
-        PartEncoder(String extraLegalCharacters) {
-            this.extraLegalCharacters = extraLegalCharacters;
-        }
-
-        @Override protected boolean isRetained(char c) {
-            return UNRESERVED.indexOf(c) != -1
-                    || PUNCTUATION.indexOf(c) != -1
-                    || extraLegalCharacters.indexOf(c) != -1
-                    || (c > 127 && !Character.isSpaceChar(c) && !Character.isISOControl(c));
-        }
-    }
-
     // Note: Comments containing the word "ASSERT" indicate places where a
     // throw of an InternalError should be replaced by an appropriate assertion
     // statement once asserts are enabled in the build.
diff --git a/ojluni/src/main/java/java/net/URL.java b/ojluni/src/main/java/java/net/URL.java
index 2372cc4..b585e2b 100755
--- a/ojluni/src/main/java/java/net/URL.java
+++ b/ojluni/src/main/java/java/net/URL.java
@@ -843,16 +843,32 @@
      * equivalent hosts, have the same port number on the host, and the same
      * file and fragment of the file.<p>
      *
-     * Two hosts are considered equivalent if both host names can be resolved
-     * into the same IP addresses; else if either host name can't be
-     * resolved, the host names must be equal without regard to case; or both
-     * host names equal to null.<p>
+     * Returns true if this URL equals {@code o}. URLs are equal if they have
+     * the same protocol, host, port, file, and reference.
      *
-     * Since hosts comparison requires name resolution, this operation is a
-     * blocking operation. <p>
-     *
-     * Note: The defined behavior for <code>equals</code> is known to
-     * be inconsistent with virtual hosting in HTTP.
+     * <h3>Network I/O Warning</h3>
+     * <p>Some implementations of URL.equals() resolve host names over the
+     * network. This is problematic:
+     * <ul>
+     * <li><strong>The network may be slow.</strong> Many classes, including
+     * core collections like {@link java.util.Map Map} and {@link java.util.Set
+     * Set} expect that {@code equals} and {@code hashCode} will return quickly.
+     * By violating this assumption, this method posed potential performance
+     * problems.
+     * <li><strong>Equal IP addresses do not imply equal content.</strong>
+     * Virtual hosting permits unrelated sites to share an IP address. This
+     * method could report two otherwise unrelated URLs to be equal because
+     * they're hosted on the same server.</li>
+     * <li><strong>The network may not be available.</strong> Two URLs could be
+     * equal when a network is available and unequal otherwise.</li>
+     * <li><strong>The network may change.</strong> The IP address for a given
+     * host name varies by network and over time. This is problematic for mobile
+     * devices. Two URLs could be equal on some networks and unequal on
+     * others.</li>
+     * </ul>
+     * <p>This problem is fixed in Android 4.0 (Ice Cream Sandwich). In that
+     * release, URLs are only equal if their host names are equal (ignoring
+     * case).
      *
      * @param   obj   the URL to compare against.
      * @return  <code>true</code> if the objects are the same;
@@ -1294,14 +1310,6 @@
         }
         hashCode = -1;
     }
-
-    public URI toURILenient() throws URISyntaxException {
-        URLStreamHandler handler = getURLStreamHandler(protocol);
-        if (handler == null) {
-          throw new IllegalStateException(protocol);
-        }
-        return new URI(handler.toExternalForm(this, true));
-    }
 }
 
 class Parts {
diff --git a/ojluni/src/main/java/java/net/URLStreamHandler.java b/ojluni/src/main/java/java/net/URLStreamHandler.java
index 0be5d75..e64b9b8 100755
--- a/ojluni/src/main/java/java/net/URLStreamHandler.java
+++ b/ojluni/src/main/java/java/net/URLStreamHandler.java
@@ -27,12 +27,9 @@
 package java.net;
 
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.File;
-import java.io.OutputStream;
-import java.util.Hashtable;
+import java.util.Objects;
+
 import sun.net.util.IPAddressUtil;
-import sun.net.www.ParseUtil;
 
 /**
  * The abstract class <code>URLStreamHandler</code> is the common
@@ -368,9 +365,10 @@
      * @since 1.3
      */
     protected boolean equals(URL u1, URL u2) {
-        String ref1 = u1.getRef();
-        String ref2 = u2.getRef();
-        return (ref1 == ref2 || (ref1 != null && ref1.equals(ref2))) &&
+        return Objects.equals(u1.getRef(), u2.getRef()) &&
+               Objects.equals(u1.getQuery(), u2.getQuery()) &&
+               // sameFile compares the protocol, file, port & host components of
+               // the URLs.
                sameFile(u1, u2);
     }
 
@@ -383,40 +381,14 @@
      * @since 1.3
      */
     protected int hashCode(URL u) {
-        int h = 0;
-
-        // Generate the protocol part.
-        String protocol = u.getProtocol();
-        if (protocol != null)
-            h += protocol.hashCode();
-
-        // Generate the host part.
-        InetAddress addr = getHostAddress(u);
-        if (addr != null) {
-            h += addr.hashCode();
-        } else {
-            String host = u.getHost();
-            if (host != null)
-                h += host.toLowerCase().hashCode();
-        }
-
-        // Generate the file part.
-        String file = u.getFile();
-        if (file != null)
-            h += file.hashCode();
-
-        // Generate the port part.
-        if (u.getPort() == -1)
-            h += getDefaultPort();
-        else
-            h += u.getPort();
-
-        // Generate the ref part.
-        String ref = u.getRef();
-        if (ref != null)
-            h += ref.hashCode();
-
-        return h;
+        // Hash on the same set of fields that we compare in equals().
+        return Objects.hash(
+                u.getRef(),
+                u.getQuery(),
+                u.getProtocol(),
+                u.getFile(),
+                u.getHost(),
+                u.getPort());
     }
 
     /**
@@ -493,18 +465,8 @@
      * @since 1.3
      */
     protected boolean hostsEqual(URL u1, URL u2) {
-        // ----- BEGIN android -----
-        // Stop doing host lookups in URL#equals, it's silly.
-        //
-        // InetAddress a1 = getHostAddress(u1);
-        // InetAddress a2 = getHostAddress(u2);
-        // // if we have internet address for both, compare them
-        // if (a1 != null && a2 != null) {
-        //     return a1.equals(a2);
-        // // else, if both have host names, compare them
-        // } else if (u1.getHost() != null && u2.getHost() != null)
+        // Android changed: Don't compare the InetAddresses of the hosts.
         if (u1.getHost() != null && u2.getHost() != null)
-        // ----- END android -----
             return u1.getHost().equalsIgnoreCase(u2.getHost());
          else
             return u1.getHost() == null && u2.getHost() == null;
@@ -518,10 +480,6 @@
      * @return  a string representation of the <code>URL</code> argument.
      */
     protected String toExternalForm(URL u) {
-      return toExternalForm(u, false);
-    }
-
-    String toExternalForm(URL u, boolean escapeIllegalCharacters) {
         // pre-compute length of StringBuffer
         int len = u.getProtocol().length() + 1;
         if (u.getAuthority() != null && u.getAuthority().length() > 0)
@@ -540,27 +498,15 @@
         result.append(":");
         if (u.getAuthority() != null) {// ANDROID: && u.getAuthority().length() > 0) {
             result.append("//");
-            if (escapeIllegalCharacters) {
-                URI.AUTHORITY_ENCODER.appendPartiallyEncoded(result, u.getAuthority());
-            } else {
-                result.append(u.getAuthority());
-            }
+            result.append(u.getAuthority());
         }
         String fileAndQuery = u.getFile();
         if (fileAndQuery != null) {
-            if (escapeIllegalCharacters) {
-                URI.FILE_AND_QUERY_ENCODER.appendPartiallyEncoded(result, fileAndQuery);
-            } else {
-                result.append(fileAndQuery);
-            }
+            result.append(fileAndQuery);
         }
         if (u.getRef() != null) {
             result.append("#");
-            if (escapeIllegalCharacters) {
-              URI.ALL_LEGAL_ENCODER.appendPartiallyEncoded(result, u.getRef());
-            } else {
-              result.append(u.getRef());
-            }
+            result.append(u.getRef());
         }
         return result.toString();
     }
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/java/java/util/zip/ZipEntry.java b/ojluni/src/main/java/java/util/zip/ZipEntry.java
index c67a0af..af1573e 100755
--- a/ojluni/src/main/java/java/util/zip/ZipEntry.java
+++ b/ojluni/src/main/java/java/util/zip/ZipEntry.java
@@ -59,7 +59,7 @@
     public static final int DEFLATED = 8;
 
 
-    /** @hide - for testing only */
+    /** @hide - Called from StrictJarFile native code. */
     public ZipEntry(String name, String comment, long crc, long compressedSize,
             long size, int compressionMethod, int time, byte[] extra,
             long dataOffset) {
diff --git a/ojluni/src/main/java/sun/nio/ch/FileDescriptorHolderSocketImpl.java b/ojluni/src/main/java/sun/nio/ch/FileDescriptorHolderSocketImpl.java
new file mode 100644
index 0000000..d7a9fc6
--- /dev/null
+++ b/ojluni/src/main/java/sun/nio/ch/FileDescriptorHolderSocketImpl.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  The Android Open Source
+ * Project designates this particular file as subject to the "Classpath"
+ * exception as provided by The Android Open Source Project in the LICENSE
+ * file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+package sun.nio.ch;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.SocketImpl;
+
+/**
+ * This class is only used for {@link SocketAdaptor} to be backward-compatible
+ * with older Android versions which always set the {@code impl} field of
+ * {@link Socket}. As such none of the methods in this class are implemented
+ * since {@link SocketAdaptor} should override everything in {@link Socket}
+ * which may access the {@code impl} field.
+ */
+class FileDescriptorHolderSocketImpl extends SocketImpl {
+    public FileDescriptorHolderSocketImpl(FileDescriptor fd) {
+        this.fd = fd;
+    }
+
+    @Override
+    public void setOption(int optID, Object value) throws SocketException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Object getOption(int optID) throws SocketException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void create(boolean stream) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void connect(String host, int port) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void connect(InetAddress address, int port) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void connect(SocketAddress address, int timeout) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void bind(InetAddress host, int port) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void listen(int backlog) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void accept(SocketImpl s) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected InputStream getInputStream() throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected OutputStream getOutputStream() throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected int available() throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void close() throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void sendUrgentData(int data) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/ojluni/src/main/java/sun/nio/ch/SocketAdaptor.java b/ojluni/src/main/java/sun/nio/ch/SocketAdaptor.java
index d2ebde5..4240aea 100755
--- a/ojluni/src/main/java/sun/nio/ch/SocketAdaptor.java
+++ b/ojluni/src/main/java/sun/nio/ch/SocketAdaptor.java
@@ -61,7 +61,7 @@
     private volatile int timeout = 0;
 
     private SocketAdaptor(SocketChannelImpl sc) throws SocketException {
-        super((SocketImpl) null);
+        super(new FileDescriptorHolderSocketImpl(sc.getFD()));
         this.sc = sc;
     }
 
diff --git a/ojluni/src/main/java/sun/security/provider/CertPathProvider.java b/ojluni/src/main/java/sun/security/provider/CertPathProvider.java
index a36bcd9..f48bd1e 100644
--- a/ojluni/src/main/java/sun/security/provider/CertPathProvider.java
+++ b/ojluni/src/main/java/sun/security/provider/CertPathProvider.java
@@ -43,7 +43,7 @@
         put("CertPathBuilder.PKIX ValidationAlgorithm", "RFC3280");
 
         // CertPathValidator
-        put("CertPathValidator.PKIX", "sun.security.provider.certPath.PKIXCertPathValidator");
+        put("CertPathValidator.PKIX", "sun.security.provider.certpath.PKIXCertPathValidator");
         put("CertPathValidator.PKIX ImplementedIn", "Software");
         put("CertPathValidator.PKIX ValidationAlgorithm", "RFC3280");
     }
diff --git a/ojluni/src/main/java/sun/util/PreHashedMap.java b/ojluni/src/main/java/sun/util/PreHashedMap.java
deleted file mode 100755
index f9cde50e..0000000
--- a/ojluni/src/main/java/sun/util/PreHashedMap.java
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.util;
-
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.AbstractMap;
-import java.util.AbstractSet;
-import java.util.NoSuchElementException;
-
-
-/**
- * A precomputed hash map.
- *
- * <p> Subclasses of this class are of the following form:
- *
- * <blockquote><pre>
- * class FooMap
- *     extends sun.util.PreHashedMap&lt;String&gt;
- * {
- *
- *     private FooMap() {
- *         super(ROWS, SIZE, SHIFT, MASK);
- *     }
- *
- *     protected void init(Object[] ht) {
- *         ht[0] = new Object[] { "key-1", value_1 };
- *         ht[1] = new Object[] { "key-2", value_2,
- *                      new Object { "key-3", value_3 } };
- *         ...
- *     }
- *
- * }</pre></blockquote>
- *
- * <p> The <tt>init</tt> method is invoked by the <tt>PreHashedMap</tt>
- * constructor with an object array long enough for the map's rows.  The method
- * must construct the hash chain for each row and store it in the appropriate
- * element of the array.
- *
- * <p> Each entry in the map is represented by a unique hash-chain node.  The
- * final node of a hash chain is a two-element object array whose first element
- * is the entry's key and whose second element is the entry's value.  A
- * non-final node of a hash chain is a three-element object array whose first
- * two elements are the entry's key and value and whose third element is the
- * next node in the chain.
- *
- * <p> Instances of this class are mutable and are not safe for concurrent
- * access.  They may be made immutable and thread-safe via the appropriate
- * methods in the {@link java.util.Collections} utility class.
- *
- * <p> In the JDK build, subclasses of this class are typically created via the
- * <tt>Hasher</tt> program in the <tt>make/tools/Hasher</tt> directory.
- *
- * @author Mark Reinhold
- * @since 1.5
- *
- * @see java.util.AbstractMap
- */
-
-public abstract class PreHashedMap<V>
-    extends AbstractMap<String,V>
-{
-
-    private final int rows;
-    private final int size;
-    private final int shift;
-    private final int mask;
-    private final Object[] ht;
-
-    /**
-     * Creates a new map.
-     *
-     * <p> This constructor invokes the {@link #init init} method, passing it a
-     * newly-constructed row array that is <tt>rows</tt> elements long.
-     *
-     * @param rows
-     *        The number of rows in the map
-     * @param size
-     *        The number of entries in the map
-     * @param shift
-     *        The value by which hash codes are right-shifted
-     * @param mask
-     *        The value with which hash codes are masked after being shifted
-     */
-    protected PreHashedMap(int rows, int size, int shift, int mask) {
-        this.rows = rows;
-        this.size = size;
-        this.shift = shift;
-        this.mask = mask;
-        this.ht = new Object[rows];
-        init(ht);
-    }
-
-    /**
-     * Initializes this map.
-     *
-     * <p> This method must construct the map's hash chains and store them into
-     * the appropriate elements of the given hash-table row array.
-     *
-     * @param rows
-     *        The row array to be initialized
-     */
-    protected abstract void init(Object[] ht);
-
-    @SuppressWarnings("unchecked")
-    private V toV(Object x) {
-        return (V)x;
-    }
-
-    // Android-added: Add a method to hash Strings.
-    //
-    // TODO: Why does this say hash(Object) instead of hash(String).
-    private static final int hash(Object o) {
-        String s = (String) o;
-        char[] val = s.toCharArray();
-        int h = 0;
-        if (val.length > 0) {
-            for (int i = 0; i < val.length; i++) {
-                h = 31 * h + val[i];
-            }
-        }
-        return h;
-    }
-
-    public V get(Object k) {
-        // Android-changed: Use hash(k) instead of k.hashCode().
-        int h = (hash(k) >> shift) & mask;
-        Object[] a = (Object[])ht[h];
-        if (a == null) return null;
-        for (;;) {
-            if (a[0].equals(k))
-                return toV(a[1]);
-            if (a.length < 3)
-                return null;
-            a = (Object[])a[2];
-        }
-    }
-
-    /**
-     * @throws UnsupportedOperationException
-     *         If the given key is not part of this map's initial key set
-     */
-    public V put(String k, V v) {
-        // Android-changed: Use hash(k) instead of k.hashCode().
-        int h = (hash(k) >> shift) & mask;
-        Object[] a = (Object[])ht[h];
-        if (a == null)
-            throw new UnsupportedOperationException(k);
-        for (;;) {
-            if (a[0].equals(k)) {
-                V ov = toV(a[1]);
-                a[1] = v;
-                return ov;
-            }
-            if (a.length < 3)
-                throw new UnsupportedOperationException(k);
-            a = (Object[])a[2];
-        }
-    }
-
-    public Set<String> keySet() {
-        return new AbstractSet<String> () {
-
-            public int size() {
-                return size;
-            }
-
-            public Iterator<String> iterator() {
-                return new Iterator<String>() {
-                    private int i = -1;
-                    Object[] a = null;
-                    String cur = null;
-
-                    private boolean findNext() {
-                        if (a != null) {
-                            if (a.length == 3) {
-                                a = (Object[])a[2];
-                                cur = (String)a[0];
-                                return true;
-                            }
-                            i++;
-                            a = null;
-                        }
-                        cur = null;
-                        if (i >= rows)
-                            return false;
-                        if (i < 0 || ht[i] == null) {
-                            do {
-                                if (++i >= rows)
-                                    return false;
-                            } while (ht[i] == null);
-                        }
-                        a = (Object[])ht[i];
-                        cur = (String)a[0];
-                        return true;
-                    }
-
-                    public boolean hasNext() {
-                        if (cur != null)
-                            return true;
-                        return findNext();
-                    }
-
-                    public String next() {
-                        if (cur == null) {
-                            if (!findNext())
-                                throw new NoSuchElementException();
-                        }
-                        String s = cur;
-                        cur = null;
-                        return s;
-                    }
-
-                    public void remove() {
-                        throw new UnsupportedOperationException();
-                    }
-
-                };
-            }
-        };
-    }
-
-    public Set<Map.Entry<String,V>> entrySet() {
-        return new AbstractSet<Map.Entry<String,V>> () {
-
-            public int size() {
-                return size;
-            }
-
-            public Iterator<Map.Entry<String,V>> iterator() {
-                return new Iterator<Map.Entry<String,V>>() {
-                    final Iterator<String> i = keySet().iterator();
-
-                    public boolean hasNext() {
-                        return i.hasNext();
-                    }
-
-                    public Map.Entry<String,V> next() {
-                        return new Map.Entry<String,V>() {
-                            String k = i.next();
-                            public String getKey() { return k; }
-                            public V getValue() { return get(k); }
-                            public int hashCode() {
-                                V v = get(k);
-                                return (k.hashCode()
-                                        + (v == null
-                                           ? 0
-                                           : v.hashCode()));
-                            }
-                            @SuppressWarnings("unchecked")
-                            public boolean equals(Object ob) {
-                                if (ob == this)
-                                    return true;
-                                if (!(ob instanceof Map.Entry))
-                                    return false;
-                                Map.Entry<String,V> that
-                                    = (Map.Entry<String,V>)ob;
-                                return ((this.getKey() == null
-                                         ? that.getKey() == null
-                                         : this.getKey()
-                                               .equals(that.getKey()))
-                                        &&
-                                        (this.getValue() == null
-                                         ? that.getValue() == null
-                                         : this.getValue()
-                                               .equals(that.getValue())));
-                            }
-                            public V setValue(V v) {
-                                throw new UnsupportedOperationException();
-                            }
-                        };
-                    }
-
-                    public void remove() {
-                        throw new UnsupportedOperationException();
-                    }
-
-                };
-            }
-        };
-    }
-
-}
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/openjdk_java_files.mk b/openjdk_java_files.mk
index f6e3966..29f53aa 100644
--- a/openjdk_java_files.mk
+++ b/openjdk_java_files.mk
@@ -1124,6 +1124,7 @@
     ojluni/src/main/java/sun/nio/ch/EPollSelectorProvider.java \
     ojluni/src/main/java/sun/nio/ch/ExtendedSocketOption.java \
     ojluni/src/main/java/sun/nio/ch/FileChannelImpl.java \
+    ojluni/src/main/java/sun/nio/ch/FileDescriptorHolderSocketImpl.java \
     ojluni/src/main/java/sun/nio/ch/FileDispatcherImpl.java \
     ojluni/src/main/java/sun/nio/ch/FileDispatcher.java \
     ojluni/src/main/java/sun/nio/ch/FileKey.java \
@@ -1441,7 +1442,6 @@
     ojluni/src/main/java/sun/util/logging/LoggingProxy.java \
     ojluni/src/main/java/sun/util/logging/LoggingSupport.java \
     ojluni/src/main/java/sun/util/logging/PlatformLogger.java \
-    ojluni/src/main/java/sun/util/PreHashedMap.java \
     ojluni/src/main/java/sun/util/ResourceBundleEnumeration.java \
     ojluni/src/main/java/sun/util/resources/OpenListResourceBundle.java \
     $(openjdk_javadoc_files)
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);