Merge "Restore fastpath of Pattern#split"
diff --git a/benchmarks/src/benchmarks/XmlSerializeBenchmark.java b/benchmarks/src/benchmarks/XmlSerializeBenchmark.java
index 0ef2620..c542e87 100644
--- a/benchmarks/src/benchmarks/XmlSerializeBenchmark.java
+++ b/benchmarks/src/benchmarks/XmlSerializeBenchmark.java
@@ -88,7 +88,7 @@
         String[] splitted = datasetAsString.split(" ");
         dataset = new double[splitted.length];
         for (int i = 0; i < splitted.length; i++) {
-            dataset[i] = Double.valueOf(splitted[i]);
+            dataset[i] = Double.parseDouble(splitted[i]);
         }
     }
 
diff --git a/benchmarks/src/benchmarks/regression/StringBuilderBenchmark.java b/benchmarks/src/benchmarks/regression/StringBuilderBenchmark.java
index 07a2305..ef54432 100644
--- a/benchmarks/src/benchmarks/regression/StringBuilderBenchmark.java
+++ b/benchmarks/src/benchmarks/regression/StringBuilderBenchmark.java
@@ -63,6 +63,16 @@
         }
     }
 
+    public void timeAppendSubCharSequence(int reps) {
+        CharSequence cs = "chars";
+        for (int i = 0; i < reps; ++i) {
+            StringBuilder sb = new StringBuilder();
+            for (int j = 0; j < length; ++j) {
+                sb.append(cs);
+            }
+        }
+    }
+
     public void timeAppendDouble(int reps) {
         double d = 1.2;
         for (int i = 0; i < reps; ++i) {
diff --git a/dex/src/main/java/com/android/dex/Dex.java b/dex/src/main/java/com/android/dex/Dex.java
index ea9b627b..d0434d6 100644
--- a/dex/src/main/java/com/android/dex/Dex.java
+++ b/dex/src/main/java/com/android/dex/Dex.java
@@ -93,7 +93,11 @@
      * Creates a new dex buffer of the dex in {@code in}, and closes {@code in}.
      */
     public Dex(InputStream in) throws IOException {
-        loadFrom(in);
+        try {
+            loadFrom(in);
+        } finally {
+            in.close();
+        }
     }
 
     /**
@@ -104,13 +108,17 @@
             ZipFile zipFile = new ZipFile(file);
             ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME);
             if (entry != null) {
-                loadFrom(zipFile.getInputStream(entry));
+                try (InputStream inputStream = zipFile.getInputStream(entry)) {
+                    loadFrom(inputStream);
+                }
                 zipFile.close();
             } else {
                 throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file);
             }
         } else if (file.getName().endsWith(".dex")) {
-            loadFrom(new FileInputStream(file));
+            try (InputStream inputStream = new FileInputStream(file)) {
+                loadFrom(inputStream);
+            }
         } else {
             throw new DexException("unknown output extension: " + file);
         }
@@ -141,6 +149,9 @@
         return new Dex(data);
     }
 
+    /**
+     * It is the caller's responsibility to close {@code in}.
+     */
     private void loadFrom(InputStream in) throws IOException {
         ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
         byte[] buffer = new byte[8192];
@@ -149,7 +160,6 @@
         while ((count = in.read(buffer)) != -1) {
             bytesOut.write(buffer, 0, count);
         }
-        in.close();
 
         this.data = ByteBuffer.wrap(bytesOut.toByteArray());
         this.data.order(ByteOrder.LITTLE_ENDIAN);
@@ -174,9 +184,9 @@
     }
 
     public void writeTo(File dexOut) throws IOException {
-        OutputStream out = new FileOutputStream(dexOut);
-        writeTo(out);
-        out.close();
+        try (OutputStream out = new FileOutputStream(dexOut)) {
+            writeTo(out);
+        }
     }
 
     public TableOfContents getTableOfContents() {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java
index 264e004..d0bb14a 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java
@@ -71,7 +71,7 @@
 
         // Determine if the device is marked to support multicast or not. If this propery is not
         // set we assume the device has an interface capable of supporting multicast.
-        supportsMulticast = Boolean.valueOf(
+        supportsMulticast = Boolean.parseBoolean(
                 System.getProperty("android.cts.device.multicast", "true"));
         if (!supportsMulticast) {
             return;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/ServerSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/ServerSocketTest.java
index dca9b74..5279464 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/ServerSocketTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/ServerSocketTest.java
@@ -378,7 +378,7 @@
         try {
             int portNumber = s.getLocalPort();
             // In IPv6, the all-zeros-address is written as "::"
-            assertEquals("ServerSocket[addr=/::,localport="
+            assertEquals("ServerSocket[addr=::/::,localport="
                     + portNumber + "]", s.toString());
         } finally {
             s.close();
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileChannelTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileChannelTest.java
index f3b5f8b..d6dacb4 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileChannelTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileChannelTest.java
@@ -46,6 +46,8 @@
 
 import junit.framework.TestCase;
 
+import libcore.io.IoUtils;
+
 public class FileChannelTest extends TestCase {
 
     private static final int CAPACITY = 100;
@@ -76,8 +78,12 @@
 
     private FileChannel readOnlyFileChannel;
 
+    private FileChannel readOnlyFileChannel2;
+
     private FileChannel writeOnlyFileChannel;
 
+    private FileChannel writeOnlyFileChannel2;
+
     private FileChannel readWriteFileChannel;
 
     private File fileOfReadOnlyFileChannel;
@@ -121,41 +127,23 @@
         fileLock = null;
         readOnlyFileChannel = new FileInputStream(fileOfReadOnlyFileChannel)
                 .getChannel();
+        readOnlyFileChannel2 = new FileInputStream(fileOfReadOnlyFileChannel)
+                .getChannel();
         writeOnlyFileChannel = new FileOutputStream(fileOfWriteOnlyFileChannel)
                 .getChannel();
+        writeOnlyFileChannel2 = new FileOutputStream(fileOfWriteOnlyFileChannel)
+                .getChannel();
         readWriteFileChannel = new RandomAccessFile(fileOfReadWriteFileChannel,
                 "rw").getChannel();
     }
 
     protected void tearDown() {
-        if (null != readOnlyFileChannel) {
-            try {
-                readOnlyFileChannel.close();
-            } catch (IOException e) {
-                // do nothing
-            }
-        }
-        if (null != writeOnlyFileChannel) {
-            try {
-                writeOnlyFileChannel.close();
-            } catch (IOException e) {
-                // do nothing
-            }
-        }
-        if (null != readWriteFileChannel) {
-            try {
-                readWriteFileChannel.close();
-            } catch (IOException e) {
-                // do nothing
-            }
-        }
-        if (null != fis) {
-            try {
-                fis.close();
-            } catch (IOException e) {
-                // do nothing
-            }
-        }
+        IoUtils.closeQuietly(readOnlyFileChannel);
+        IoUtils.closeQuietly(readOnlyFileChannel2);
+        IoUtils.closeQuietly(writeOnlyFileChannel);
+        IoUtils.closeQuietly(writeOnlyFileChannel2);
+        IoUtils.closeQuietly(readWriteFileChannel);
+        IoUtils.closeQuietly(fis);
 
         if (null != fileLock) {
             try {
@@ -174,56 +162,15 @@
         if (null != fileOfReadWriteFileChannel) {
             fileOfReadWriteFileChannel.delete();
         }
-        if (null != datagramChannelSender) {
-            try {
-                datagramChannelSender.close();
-            } catch (IOException e) {
-                // do nothing
-            }
-        }
-        if (null != datagramChannelReceiver) {
-            try {
-                datagramChannelReceiver.close();
-            } catch (IOException e) {
-                // do nothing
-            }
-        }
-        if (null != serverSocketChannel) {
-            try {
-                serverSocketChannel.close();
-            } catch (IOException e) {
-                // do nothing
-            }
-        }
-        if (null != socketChannelSender) {
-            try {
-                socketChannelSender.close();
-            } catch (IOException e) {
-                // do nothing
-            }
-        }
-        if (null != socketChannelReceiver) {
-            try {
-                socketChannelReceiver.close();
-            } catch (IOException e) {
-                // do nothing
-            }
-        }
+
+        IoUtils.closeQuietly(datagramChannelSender);
+        IoUtils.closeQuietly(datagramChannelReceiver);
+        IoUtils.closeQuietly(serverSocketChannel);
+        IoUtils.closeQuietly(socketChannelSender);
+        IoUtils.closeQuietly(socketChannelReceiver);
         if (null != pipe) {
-            if (null != pipe.source()) {
-                try {
-                    pipe.source().close();
-                } catch (IOException e) {
-                    // do nothing
-                }
-            }
-            if (null != pipe.sink()) {
-                try {
-                    pipe.sink().close();
-                } catch (IOException e) {
-                    // do nothing
-                }
-            }
+            IoUtils.closeQuietly(pipe.source());
+            IoUtils.closeQuietly(pipe.sink());
         }
     }
 
@@ -653,14 +600,81 @@
     /**
      * @tests java.nio.channels.FileChannel#lock()
      */
+    public void test_lock_Closed() throws Exception {
+        readOnlyFileChannel.close();
+        try {
+            readOnlyFileChannel.lock();
+            fail("should throw ClosedChannelException");
+        } catch (ClosedChannelException expected) {}
+
+        writeOnlyFileChannel.close();
+        try {
+            writeOnlyFileChannel.lock();
+            fail("should throw ClosedChannelException");
+        } catch (ClosedChannelException expected) {}
+
+        readWriteFileChannel.close();
+        try {
+            readWriteFileChannel.lock();
+            fail("should throw ClosedChannelException");
+        } catch (ClosedChannelException expected) {}
+    }
+
+    /**
+     * @tests java.nio.channels.FileChannel#lock()
+     */
+    public void test_lock_NonWritable() throws Exception {
+        try {
+            readOnlyFileChannel.lock();
+            fail("should throw NonWritableChannelException");
+        } catch (NonWritableChannelException expected) {}
+    }
+
+    /**
+     * @tests java.nio.channels.FileChannel#lock()
+     */
     public void test_lock() throws Exception {
-        MockFileChannel mockFileChannel = new MockFileChannel();
-        // Verify that calling lock() leads to the method
-        // lock(long, long, boolean) being called with a 0 for the
-        // first parameter, Long.MAX_VALUE as the second parameter and false
-        // as the third parameter.
-        mockFileChannel.lock();
-        assertTrue(mockFileChannel.isLockCalled);
+        fileLock = writeOnlyFileChannel.lock();
+        assertTrue(fileLock.isValid());
+        assertFalse(fileLock.isShared());
+        assertSame(writeOnlyFileChannel, fileLock.channel());
+        assertEquals(Long.MAX_VALUE, fileLock.size());
+        assertEquals(0, fileLock.position());
+    }
+
+    /**
+     * @tests java.nio.channels.FileChannel#lock()
+     */
+    public void test_lock_OverlappingException() throws Exception {
+        fileLock = writeOnlyFileChannel.lock();
+        assertTrue(fileLock.isValid());
+
+        // Test the same channel cannot be locked twice.
+        try {
+            writeOnlyFileChannel.lock();
+            fail("should throw OverlappingFileLockException");
+        } catch (OverlappingFileLockException expected) {}
+
+        // Test that a different channel on the same file also cannot be locked.
+        try {
+            writeOnlyFileChannel2.lock();
+            fail("should throw OverlappingFileLockException");
+        } catch (OverlappingFileLockException expected) {}
+    }
+
+    /**
+     * @tests java.nio.channels.FileChannel#lock()
+     */
+    public void test_lock_After_Release() throws Exception {
+        fileLock = writeOnlyFileChannel.lock();
+        fileLock.release();
+        // After release file lock can be obtained again.
+        fileLock = writeOnlyFileChannel.lock();
+        assertTrue(fileLock.isValid());
+
+        // A different channel should be able to obtain a lock after it has been released
+        fileLock.release();
+        assertTrue(writeOnlyFileChannel2.lock().isValid());
     }
 
     /**
@@ -826,12 +840,17 @@
         fileLock = writeOnlyFileChannel.lock(POSITION, SIZE, false);
         assertTrue(fileLock.isValid());
 
+        // Test the same channel cannot be locked twice.
         try {
             writeOnlyFileChannel.lock(POSITION + 1, SIZE, false);
             fail("should throw OverlappingFileLockException");
-        } catch (OverlappingFileLockException e) {
-            // expected
-        }
+        } catch (OverlappingFileLockException expected) {}
+
+        // Test that a different channel on the same file also cannot be locked.
+        try {
+            writeOnlyFileChannel2.lock(POSITION + 1, SIZE, false);
+            fail("should throw OverlappingFileLockException");
+        } catch (OverlappingFileLockException expected) {}
     }
 
     /**
@@ -861,14 +880,88 @@
     /**
      * @tests java.nio.channels.FileChannel#tryLock()
      */
+    public void test_tryLock_Closed() throws Exception {
+        readOnlyFileChannel.close();
+        try {
+            readOnlyFileChannel.tryLock();
+            fail("should throw ClosedChannelException");
+        } catch (ClosedChannelException expected) {}
+
+        writeOnlyFileChannel.close();
+        try {
+            writeOnlyFileChannel.tryLock();
+            fail("should throw ClosedChannelException");
+        } catch (ClosedChannelException expected) {}
+
+        readWriteFileChannel.close();
+        try {
+            readWriteFileChannel.tryLock();
+            fail("should throw ClosedChannelException");
+        } catch (ClosedChannelException expected) {}
+    }
+
+    /**
+     * @tests java.nio.channels.FileChannel#tryLock()
+     */
+    public void test_tryLock_NonWritable() throws Exception {
+        try {
+            readOnlyFileChannel.tryLock();
+            fail("should throw NonWritableChannelException");
+        } catch (NonWritableChannelException expected) {}
+    }
+
+    /**
+     * @tests java.nio.channels.FileChannel#tryLock()
+     */
     public void test_tryLock() throws Exception {
-        MockFileChannel mockFileChannel = new MockFileChannel();
-        // Verify that calling tryLock() leads to the method
-        // tryLock(long, long, boolean) being called with a 0 for the
-        // first parameter, Long.MAX_VALUE as the second parameter and false
-        // as the third parameter.
-        mockFileChannel.tryLock();
-        assertTrue(mockFileChannel.isTryLockCalled);
+        fileLock = writeOnlyFileChannel.tryLock();
+        assertTrue(fileLock.isValid());
+        assertFalse(fileLock.isShared());
+        assertSame(writeOnlyFileChannel, fileLock.channel());
+        assertEquals(0, fileLock.position());
+        assertEquals(Long.MAX_VALUE, fileLock.size());
+    }
+
+    /**
+     * @tests java.nio.channels.FileChannel#tryLock()
+     */
+    public void test_tryLock_Overlapping() throws Exception {
+        fileLock = writeOnlyFileChannel.tryLock();
+        assertTrue(fileLock.isValid());
+
+        // Test the same channel cannot be locked twice.
+        try {
+            writeOnlyFileChannel.tryLock();
+            fail("should throw OverlappingFileLockException");
+        } catch (OverlappingFileLockException expected) {}
+
+        // Test that a different channel on the same file also cannot be locked.
+        try {
+            writeOnlyFileChannel2.tryLock();
+            fail("should throw OverlappingFileLockException");
+        } catch (OverlappingFileLockException expected) {}
+    }
+
+    /**
+     * @tests java.nio.channels.FileChannel#tryLock()
+     */
+    public void test_tryLock_After_Release() throws Exception {
+        fileLock = writeOnlyFileChannel.tryLock();
+        fileLock.release();
+
+        // After release file lock can be obtained again.
+        fileLock = writeOnlyFileChannel.tryLock();
+        assertTrue(fileLock.isValid());
+
+        // Test that the same channel can acquire the lock after it has been released
+        fileLock.release();
+        fileLock = writeOnlyFileChannel.tryLock();
+        assertTrue(fileLock.isValid());
+
+        // Test that a different channel can acquire the lock after it has been released
+        fileLock.release();
+        fileLock = writeOnlyFileChannel2.tryLock();
+        assertTrue(fileLock.isValid());
     }
 
     /**
@@ -1034,12 +1127,17 @@
         fileLock = writeOnlyFileChannel.lock(POSITION, SIZE, false);
         assertTrue(fileLock.isValid());
 
+        // Test the same channel cannot be locked twice.
         try {
-            writeOnlyFileChannel.lock(POSITION + 1, SIZE, false);
+            writeOnlyFileChannel.tryLock(POSITION + 1, SIZE, false);
             fail("should throw OverlappingFileLockException");
-        } catch (OverlappingFileLockException e) {
-            // expected
-        }
+        } catch (OverlappingFileLockException expected) {}
+
+        // Test that a different channel on the same file also cannot be locked.
+        try {
+            writeOnlyFileChannel2.tryLock(POSITION + 1, SIZE, false);
+            fail("should throw OverlappingFileLockException");
+        } catch (OverlappingFileLockException expected) {}
     }
 
     /**
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ChoiceFormatTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ChoiceFormatTest.java
index d52e586..e7acfdf 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ChoiceFormatTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ChoiceFormatTest.java
@@ -21,6 +21,7 @@
 import java.text.FieldPosition;
 import java.text.MessageFormat;
 import java.text.ParsePosition;
+import java.util.Arrays;
 import java.util.Locale;
 
 import junit.framework.TestCase;
@@ -300,9 +301,11 @@
         // java.text.ChoiceFormat.getFormats()
         String[] orgFormats = (String[]) formats.clone();
         String[] f = (String[]) f1.getFormats();
-        assertTrue("Wrong formats", f.equals(formats));
+        // getFormats() documentation says "Get the formats passed in the constructor",
+        // which can be interpreted as object identity.
+        assertTrue("Wrong formats", f == formats);
         f[0] = "Modified";
-        assertTrue("Formats copied", !f.equals(orgFormats));
+        assertTrue("Formats copied", f != orgFormats);
     }
 
     /**
@@ -312,9 +315,11 @@
         // Test for method double [] java.text.ChoiceFormat.getLimits()
         double[] orgLimits = (double[]) limits.clone();
         double[] l = f1.getLimits();
-        assertTrue("Wrong limits", l.equals(limits));
+        // getLimits() documentation says "Get the limits passed in the constructor",
+        // which can be interpreted as object identity.
+        assertTrue("Wrong limits", l == limits);
         l[0] = 3.14527;
-        assertTrue("Limits copied", !l.equals(orgLimits));
+        assertTrue("Limits copied", l != orgLimits);
     }
 
     /**
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java
index bf8db02..ee1a372 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java
@@ -36,6 +36,7 @@
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
+import java.util.function.BiFunction;
 
 public class IdentityHashMapTest extends junit.framework.TestCase {
     private static final String ID = "hello";
@@ -1054,6 +1055,34 @@
         SpliteratorTester.testSpliteratorNPE(values.spliterator());
     }
 
+    public void test_replaceAll() {
+        IdentityHashMap<String, String> map = new IdentityHashMap<>();
+        String key1 = "key1";
+        String key2 = "key2";
+        String key3 = "key3";
+
+        map.put(key1, "1");
+        map.put(key2, "2");
+        map.put(key3, "3");
+
+        map.replaceAll((k, v) -> k + v);
+
+        assertEquals("key11", map.get(key1));
+        assertEquals("key22", map.get(key2));
+        assertEquals("key33", map.get(key3));
+        assertEquals(3, map.size());
+
+        try {
+            map.replaceAll(new BiFunction<String, String, String>() {
+                @Override
+                public String apply(String s, String s2) {
+                    map.put("key4", "4");
+                    return "";
+                }
+            });
+        } catch (ConcurrentModificationException expected) {}
+    }
+
 
     // comparator for IdentityHashMap objects
     private static final SerializableAssert COMPARATOR = new SerializableAssert() {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/callback/PasswordCallbackTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/callback/PasswordCallbackTest.java
index 024c9e9..8682de7 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/callback/PasswordCallbackTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/callback/PasswordCallbackTest.java
@@ -93,7 +93,7 @@
             }
             pc.clearPassword();
             res = pc.getPassword();
-            if (res.equals(psw2)) {
+            if (Arrays.equals(res, psw2)) {
                 fail("Incorrect password was returned after clear");
             }
             pc.setPassword(psw1);
diff --git a/json/src/main/java/org/json/JSONObject.java b/json/src/main/java/org/json/JSONObject.java
index 9ea91a6..7902389 100644
--- a/json/src/main/java/org/json/JSONObject.java
+++ b/json/src/main/java/org/json/JSONObject.java
@@ -21,6 +21,7 @@
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 // Note: this class was written without inspecting the non-free org.json sourcecode.
@@ -100,6 +101,8 @@
         @Override public boolean equals(Object o) {
             return o == this || o == null; // API specifies this broken equals implementation
         }
+        // at least make the broken equals(null) consistent with Objects.hashCode(null).
+        @Override public int hashCode() { return Objects.hashCode(null); }
         @Override public String toString() {
             return "null";
         }
diff --git a/json/src/test/java/org/json/JSONObjectTest.java b/json/src/test/java/org/json/JSONObjectTest.java
index 9029ec6..07d1cf6 100644
--- a/json/src/test/java/org/json/JSONObjectTest.java
+++ b/json/src/test/java/org/json/JSONObjectTest.java
@@ -27,6 +27,7 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 import java.util.Set;
 import java.util.TreeMap;
 import junit.framework.TestCase;
@@ -825,6 +826,12 @@
         assertTrue(object.isNull("bar"));
     }
 
+    public void testNullValue_equalsAndHashCode() {
+        assertTrue(JSONObject.NULL.equals(null)); // guaranteed by javadoc
+        // not guaranteed by javadoc, but seems like a good idea
+        assertEquals(Objects.hashCode(null), JSONObject.NULL.hashCode());
+    }
+
     public void testHas() throws JSONException {
         JSONObject object = new JSONObject();
         object.put("foo", 5);
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
index 74aedd4..8b27ca2 100644
--- a/libart/src/main/java/dalvik/system/VMRuntime.java
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -396,4 +396,10 @@
      * set.
      */
     public static native boolean didPruneDalvikCache();
+
+    /**
+     * Register the current execution thread to the runtime as sensitive thread.
+     * Should be called just once. Subsequent calls are ignored.
+     */
+    public static native void registerSensitiveThread();
 }
diff --git a/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java b/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java
index 38be0c2..0a73c9f 100644
--- a/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java
+++ b/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java
@@ -68,6 +68,7 @@
     private CharsetDecoderICU(Charset cs, float averageCharsPerByte, long address) {
         super(cs, averageCharsPerByte, MAX_CHARS_PER_BYTE);
         this.converterHandle = address;
+        NativeConverter.registerConverter(this, converterHandle);
     }
 
     @Override protected void implReplaceWith(String newReplacement) {
@@ -155,14 +156,6 @@
         }
     }
 
-    @Override protected void finalize() throws Throwable {
-        try {
-            NativeConverter.closeConverter(converterHandle);
-            converterHandle = 0;
-        } finally {
-            super.finalize();
-        }
-    }
 
     private int getArray(CharBuffer out) {
         if (out.hasArray()) {
diff --git a/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java b/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java
index 3583e19..a981c18 100644
--- a/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java
+++ b/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java
@@ -21,6 +21,7 @@
 import libcore.icu.ICU;
 import libcore.icu.NativeConverter;
 import libcore.util.EmptyArray;
+import libcore.util.NativeAllocationRegistry;
 
 final class CharsetEncoderICU extends CharsetEncoder {
     private static final Map<String, byte[]> DEFAULT_REPLACEMENTS = new HashMap<String, byte[]>();
@@ -49,7 +50,7 @@
     private int[] data = new int[3];
 
     /* handle to the ICU converter that is opened */
-    private long converterHandle=0;
+    private final long converterHandle;
 
     private char[] input = null;
     private byte[] output = null;
@@ -95,6 +96,7 @@
         super(cs, averageBytesPerChar, maxBytesPerChar, replacement, true);
         // Our native peer needs to know what just happened...
         this.converterHandle = address;
+        NativeConverter.registerConverter(this, converterHandle);
         updateCallback();
     }
 
@@ -184,15 +186,6 @@
         }
     }
 
-    @Override protected void finalize() throws Throwable {
-        try {
-            NativeConverter.closeConverter(converterHandle);
-            converterHandle=0;
-        } finally {
-            super.finalize();
-        }
-    }
-
     private int getArray(ByteBuffer out) {
         if (out.hasArray()) {
             output = out.array();
diff --git a/luni/src/main/java/javax/xml/datatype/FactoryFinder.java b/luni/src/main/java/javax/xml/datatype/FactoryFinder.java
index 1fbca2f..c31bee3 100644
--- a/luni/src/main/java/javax/xml/datatype/FactoryFinder.java
+++ b/luni/src/main/java/javax/xml/datatype/FactoryFinder.java
@@ -61,8 +61,8 @@
             File f = new File(configFile);
             if (f.exists()) {
                 if (debug) debugPrintln("Read properties file " + f);
-                try {
-                    cacheProps.load(new FileInputStream(f));
+                try (FileInputStream inputStream = new FileInputStream(f)) {
+                    cacheProps.load(inputStream);
                 } catch (Exception ex) {
                     if (debug) {
                         ex.printStackTrace();
diff --git a/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java b/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java
index 0060612..50a644f 100644
--- a/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java
+++ b/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java
@@ -62,8 +62,8 @@
             File f = new File(configFile);
             if (f.exists()) {
                 if (debug) debugPrintln("Read properties file " + f);
-                try {
-                    cacheProps.load(new FileInputStream(f));
+                try (FileInputStream inputStream = new FileInputStream(f)) {
+                    cacheProps.load(inputStream);
                 } catch (Exception ex) {
                     if (debug) {
                         ex.printStackTrace();
diff --git a/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java b/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java
index 5a7663c..7a4f6b3 100644
--- a/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java
+++ b/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java
@@ -69,8 +69,8 @@
             File f = new File(configFile);
             if (f.exists()) {
                 if (debug) debugPrintln("Read properties file " + f);
-                try {
-                    cacheProps.load(new FileInputStream(f));
+                try (FileInputStream inputStream = new FileInputStream(f)) {
+                    cacheProps.load(inputStream);
                 } catch (Exception ex) {
                     if (debug) {
                         ex.printStackTrace();
diff --git a/luni/src/main/java/libcore/icu/NativeConverter.java b/luni/src/main/java/libcore/icu/NativeConverter.java
index 17be458..a2b5798 100644
--- a/luni/src/main/java/libcore/icu/NativeConverter.java
+++ b/luni/src/main/java/libcore/icu/NativeConverter.java
@@ -9,12 +9,17 @@
 
 package libcore.icu;
 
+import libcore.util.NativeAllocationRegistry;
+
 import java.nio.charset.Charset;
 import java.nio.charset.CharsetDecoder;
 import java.nio.charset.CharsetEncoder;
 import java.nio.charset.CodingErrorAction;
 
 public final class NativeConverter {
+    private static final NativeAllocationRegistry registry = new NativeAllocationRegistry(
+            getNativeFinalizer(), getNativeSize());
+
     public static native int decode(long converterHandle, byte[] input, int inEnd,
             char[] output, int outEnd, int[] data, boolean flush);
 
@@ -24,6 +29,10 @@
     public static native long openConverter(String charsetName);
     public static native void closeConverter(long converterHandle);
 
+    public static void registerConverter(Object referrent, long converterHandle) {
+        registry.registerNativeAllocation(referrent, converterHandle);
+    }
+
     public static native void resetByteToChar(long converterHandle);
     public static native void resetCharToByte(long converterHandle);
 
@@ -67,4 +76,7 @@
                           encoder.replacement());
     }
     private static native void setCallbackEncode(long converterHandle, int onMalformedInput, int onUnmappableInput, byte[] subBytes);
+
+    public static native long getNativeFinalizer();
+    public static native long getNativeSize();
 }
diff --git a/luni/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java b/luni/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java
index e1b62fa..e4002ea 100644
--- a/luni/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java
@@ -56,7 +56,6 @@
      */
     private String documentUri;
     private String inputEncoding;
-    private String xmlEncoding;
     private String xmlVersion = "1.0";
     private boolean xmlStandalone = false;
     private boolean strictErrorChecking = true;
@@ -437,7 +436,7 @@
     }
 
     public String getXmlEncoding() {
-        return xmlEncoding;
+        return null;
     }
 
     public boolean getXmlStandalone() {
diff --git a/luni/src/main/java/org/xml/sax/helpers/XMLReaderFactory.java b/luni/src/main/java/org/xml/sax/helpers/XMLReaderFactory.java
index c4ff069..39dd367 100644
--- a/luni/src/main/java/org/xml/sax/helpers/XMLReaderFactory.java
+++ b/luni/src/main/java/org/xml/sax/helpers/XMLReaderFactory.java
@@ -126,9 +126,12 @@
             in = loader.getResourceAsStream (service);
 
         if (in != null) {
-            reader = new BufferedReader (new InputStreamReader (in, StandardCharsets.UTF_8));
-            className = reader.readLine ();
-            in.close ();
+            try {
+                reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
+                className = reader.readLine();
+            } finally {
+                in.close(); // may throw IOException
+            }
         }
         } catch (Exception e) {
         }
diff --git a/luni/src/main/native/java_util_regex_Matcher.cpp b/luni/src/main/native/java_util_regex_Matcher.cpp
index 35d014c..3b25233 100644
--- a/luni/src/main/native/java_util_regex_Matcher.cpp
+++ b/luni/src/main/native/java_util_regex_Matcher.cpp
@@ -116,8 +116,18 @@
     void operator=(const MatcherAccessor&);
 };
 
-static void Matcher_closeImpl(JNIEnv*, jclass, jlong address) {
-    delete toRegexMatcher(address);
+static void Matcher_free(void* address) {
+    delete reinterpret_cast<icu::RegexMatcher*>(address);
+}
+
+static jlong Matcher_getNativeFinalizer(JNIEnv*, jclass) {
+    return reinterpret_cast<jlong>(&Matcher_free);
+}
+
+// Return a guess of the amount of native memory to be deallocated by a typical call to
+// Matcher_free().
+static jint Matcher_nativeSize(JNIEnv*, jclass) {
+    return 200;  // Very rough guess based on a quick look at the implementation.
 }
 
 static jint Matcher_findImpl(JNIEnv* env, jclass, jlong addr, jstring javaText, jint startIndex, jintArray offsets) {
@@ -198,13 +208,14 @@
 }
 
 static JNINativeMethod gMethods[] = {
-    NATIVE_METHOD(Matcher, closeImpl, "(J)V"),
     NATIVE_METHOD(Matcher, findImpl, "(JLjava/lang/String;I[I)Z"),
     NATIVE_METHOD(Matcher, findNextImpl, "(JLjava/lang/String;[I)Z"),
+    NATIVE_METHOD(Matcher, getNativeFinalizer, "()J"),
     NATIVE_METHOD(Matcher, groupCountImpl, "(J)I"),
     NATIVE_METHOD(Matcher, hitEndImpl, "(J)Z"),
     NATIVE_METHOD(Matcher, lookingAtImpl, "(JLjava/lang/String;[I)Z"),
     NATIVE_METHOD(Matcher, matchesImpl, "(JLjava/lang/String;[I)Z"),
+    NATIVE_METHOD(Matcher, nativeSize, "()I"),
     NATIVE_METHOD(Matcher, openImpl, "(J)J"),
     NATIVE_METHOD(Matcher, requireEndImpl, "(J)Z"),
     NATIVE_METHOD(Matcher, setInputImpl, "(JLjava/lang/String;II)V"),
diff --git a/luni/src/main/native/java_util_regex_Pattern.cpp b/luni/src/main/native/java_util_regex_Pattern.cpp
index f2c07dc..4166da03 100644
--- a/luni/src/main/native/java_util_regex_Pattern.cpp
+++ b/luni/src/main/native/java_util_regex_Pattern.cpp
@@ -27,10 +27,6 @@
 
 // ICU documentation: http://icu-project.org/apiref/icu4c/classRegexPattern.html
 
-static icu::RegexPattern* toRegexPattern(jlong addr) {
-    return reinterpret_cast<icu::RegexPattern*>(static_cast<uintptr_t>(addr));
-}
-
 static const char* regexDetailMessage(UErrorCode status) {
     // These human-readable error messages were culled from "utypes.h", and then slightly tuned
     // to make more sense in context.
@@ -71,8 +67,18 @@
     env->Throw(reinterpret_cast<jthrowable>(exception));
 }
 
-static void Pattern_closeImpl(JNIEnv*, jclass, jlong addr) {
-    delete toRegexPattern(addr);
+static void Pattern_free(void* addr) {
+    delete reinterpret_cast<icu::RegexPattern*>(addr);
+}
+
+static jlong Pattern_getNativeFinalizer(JNIEnv*, jclass) {
+    return reinterpret_cast<jlong>(&Pattern_free);
+}
+
+// Return a guess of the amount of native memory to be deallocated by a typical call to
+// Pattern_free().
+static jint Pattern_nativeSize(JNIEnv*, jclass) {
+    return 500;  // Very rough guess based on a quick look at the implementation.
 }
 
 static jlong Pattern_compileImpl(JNIEnv* env, jclass, jstring javaRegex, jint flags) {
@@ -95,9 +101,11 @@
 }
 
 static JNINativeMethod gMethods[] = {
-    NATIVE_METHOD(Pattern, closeImpl, "(J)V"),
     NATIVE_METHOD(Pattern, compileImpl, "(Ljava/lang/String;I)J"),
+    NATIVE_METHOD(Pattern, getNativeFinalizer, "()J"),
+    NATIVE_METHOD(Pattern, nativeSize, "()I"),
 };
+
 void register_java_util_regex_Pattern(JNIEnv* env) {
     jniRegisterNativeMethods(env, "java/util/regex/Pattern", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_icu_NativeConverter.cpp b/luni/src/main/native/libcore_icu_NativeConverter.cpp
index 334f4c9..bf938d1 100644
--- a/luni/src/main/native/libcore_icu_NativeConverter.cpp
+++ b/luni/src/main/native/libcore_icu_NativeConverter.cpp
@@ -611,6 +611,20 @@
             javaCanonicalName, icuCanonicalNameStr, javaAliases);
 }
 
+static void FreeNativeConverter(void *converter) {
+    ucnv_close(reinterpret_cast<UConverter*>(converter));
+}
+
+static jlong NativeConverter_getNativeFinalizer(JNIEnv*, jclass) {
+    return reinterpret_cast<jlong>(&FreeNativeConverter);
+}
+
+
+static jlong NativeConverter_getNativeSize(JNIEnv*, jclass, jstring) {
+    // TODO: Improve estimate.
+    return 200;
+}
+
 static JNINativeMethod gMethods[] = {
     NATIVE_METHOD(NativeConverter, charsetForName, "(Ljava/lang/String;)Ljava/nio/charset/Charset;"),
     NATIVE_METHOD(NativeConverter, closeConverter, "(J)V"),
@@ -628,6 +642,8 @@
     NATIVE_METHOD(NativeConverter, resetCharToByte, "(J)V"),
     NATIVE_METHOD(NativeConverter, setCallbackDecode, "(JIILjava/lang/String;)V"),
     NATIVE_METHOD(NativeConverter, setCallbackEncode, "(JII[B)V"),
+    NATIVE_METHOD(NativeConverter, getNativeFinalizer, "()J"),
+    NATIVE_METHOD(NativeConverter, getNativeSize, "()J")
 };
 void register_libcore_icu_NativeConverter(JNIEnv* env) {
     jniRegisterNativeMethods(env, "libcore/icu/NativeConverter", gMethods, NELEM(gMethods));
diff --git a/luni/src/test/java/libcore/java/text/DecimalFormatTest.java b/luni/src/test/java/libcore/java/text/DecimalFormatTest.java
index 0eae20a..2b8b566 100644
--- a/luni/src/test/java/libcore/java/text/DecimalFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/DecimalFormatTest.java
@@ -283,6 +283,13 @@
         assertEquals(expected, numberFormat.format(2.01));
     }
 
+    // http://b/27855939
+    public void testBug27855939() {
+        DecimalFormat df = new DecimalFormat("00");
+        assertEquals("01", df.format(BigDecimal.ONE));
+        assertEquals("00", df.format(BigDecimal.ZERO));
+    }
+
     // Confirm the currency symbol used by a format is determined by the locale of the format
     // not the current default Locale.
     public void testSetCurrency_symbolOrigin() {
diff --git a/luni/src/test/java/libcore/java/util/CollectionsTest.java b/luni/src/test/java/libcore/java/util/CollectionsTest.java
index c45b83e..f3284c0 100644
--- a/luni/src/test/java/libcore/java/util/CollectionsTest.java
+++ b/luni/src/test/java/libcore/java/util/CollectionsTest.java
@@ -32,6 +32,9 @@
 
 public final class CollectionsTest extends TestCase {
 
+    private static final Object NOT_A_STRING = new Object();
+    private static final Object A_STRING = "string";
+
     public void testEmptyEnumeration() {
         Enumeration<Object> e = Collections.emptyEnumeration();
         assertFalse(e instanceof Serializable);
@@ -599,4 +602,140 @@
     public void test_SingletonList_sort() {
         Collections.singletonList(1).sort((k1, k2) -> 2);
     }
+
+    public void test_CheckedMap_replaceAll() {
+        Map<Integer, Integer> map = new HashMap<>();
+        Map checkedMap = Collections.checkedMap(map, Integer.class, Integer.class);
+        checkedMap.put(1, 10);
+        checkedMap.put(2, 20);
+        checkedMap.put(3, 30);
+        checkedMap.replaceAll((k, v) -> (Integer)k + (Integer)v);
+        assertEquals(11, checkedMap.get(1));
+        assertEquals(22, checkedMap.get(2));
+        assertEquals(33, checkedMap.get(3));
+        assertEquals(3, checkedMap.size());
+    }
+
+    public void test_CheckedMap_putIfAbsent() {
+        Map<Integer, Double> map = new HashMap<>();
+        Map checkedMap = Collections.checkedMap(map, Integer.class, Double.class);
+        MapDefaultMethodTester.test_putIfAbsent(checkedMap, true /* acceptsNullKey */,
+                true /* acceptsNullValue */);
+
+        // Without generics to check the typeCheck implementation
+        Map checkedMap2 = Collections.checkedMap(new HashMap<>(), Integer.class, String.class);
+
+        // When key is present
+        checkedMap2.putIfAbsent(1, A_STRING);
+        try {
+            checkedMap2.putIfAbsent(1, NOT_A_STRING);
+        } catch (ClassCastException expected) {}
+
+        // When key is absent
+        checkedMap2.clear();
+        try {
+            checkedMap2.putIfAbsent(1, NOT_A_STRING);
+        } catch (ClassCastException expected) {}
+    }
+
+    public void test_CheckedMap_remove() {
+        Map<Integer, Double> map = new HashMap<>();
+        Map checkedMap = Collections.checkedMap(map, Integer.class, Double.class);
+        MapDefaultMethodTester.test_remove(checkedMap, true /* acceptsNullKey */,
+                true /* acceptsNullValue */);
+    }
+
+    public void test_CheckedMap_replace$K$V$V() {
+        Map<Integer, Double> map = new HashMap<>();
+        Map checkedMap = Collections.checkedMap(map, Integer.class, Double.class);
+        MapDefaultMethodTester.test_replace$K$V$V(checkedMap, true /* acceptsNullKey */,
+                true /* acceptsNullValue */);
+
+        // Without generics to check the typeCheck implementation
+        Map checkedMap2 = Collections.checkedMap(new HashMap<>(), Integer.class, String.class);
+        checkedMap2.put(1, A_STRING);
+
+        try {
+            checkedMap2.replace(1, NOT_A_STRING);
+        } catch (ClassCastException expected) {}
+    }
+
+    public void test_CheckedMap_replace$K$V() {
+        Map<Integer, Double> map = new HashMap<>();
+        Map checkedMap = Collections.checkedMap(map, Integer.class, Double.class);
+        MapDefaultMethodTester.test_replace$K$V(checkedMap, true /* acceptsNullKey */,
+                true /* acceptsNullValue */);
+
+        // Without generics to check the typeCheck implementation
+        Map checkedMap2 = Collections.checkedMap(new HashMap<>(), Integer.class, String.class);
+        checkedMap2.put(1, A_STRING);
+
+        try {
+            checkedMap2.replace(1, 1, NOT_A_STRING);
+        } catch (ClassCastException expected) {}
+    }
+
+    public void test_CheckedMap_computeIfAbsent() {
+        Map<Integer, Double> map = new HashMap<>();
+        Map checkedMap = Collections.checkedMap(map, Integer.class, Double.class);
+        MapDefaultMethodTester.test_computeIfAbsent(checkedMap, true /* acceptsNullKey */,
+                true /* acceptsNullValue */);
+
+        // Without generics to check the typeCheck implementation
+        Map checkedMap2 = Collections.checkedMap(new HashMap<>(), Integer.class, String.class);
+        checkedMap2.put(1, A_STRING);
+
+        // When key is present
+        try {
+            checkedMap2.computeIfAbsent(1, k -> NOT_A_STRING);
+        } catch (ClassCastException expected) {}
+
+        // When key is absent
+        checkedMap2.clear();
+        try {
+            checkedMap2.computeIfAbsent(1, k -> NOT_A_STRING);
+        } catch (ClassCastException expected) {}
+    }
+
+    public void test_CheckedMap_computeIfPresent() {
+        Map<Integer, Double> map = new HashMap<>();
+        Map checkedMap = Collections.checkedMap(map, Integer.class, Double.class);
+        MapDefaultMethodTester.test_computeIfPresent(checkedMap, true /* acceptsNullKey */);
+
+        // Without generics to check the typeCheck implementation
+        Map m = new HashMap();
+        Map checkedMap2 = Collections.checkedMap(m, Integer.class, String.class);
+        checkedMap2.put(1, A_STRING);
+
+        try {
+            checkedMap2.computeIfPresent(1, (k, v) -> NOT_A_STRING);
+        } catch (ClassCastException expected) {}
+    }
+
+    public void test_CheckedMap_compute() {
+        Map<Integer, Double> map = new HashMap<>();
+        Map checkedMap = Collections.checkedMap(map, Integer.class, Double.class);
+        MapDefaultMethodTester.test_compute(checkedMap, true /* acceptsNullKey */);
+
+        Map checkedMap2 = Collections.checkedMap(new HashMap(), Integer.class, String.class);
+        checkedMap2.put(1, A_STRING);
+        try {
+            checkedMap2.compute(1, (k, v) -> NOT_A_STRING);
+        } catch (ClassCastException expected) {}
+    }
+
+    public void test_CheckedMap_merge() {
+        Map<Integer, Double> map = new HashMap<>();
+        Map checkedMap = Collections.checkedMap(map, Integer.class, Double.class);
+        MapDefaultMethodTester.test_merge(checkedMap, true /* acceptsNullKey */);
+
+        // Without generics to check the typeCheck implementation
+        Map checkedMap2 =
+                Collections.checkedMap(new HashMap<>(), Integer.class, String.class);
+        checkedMap2.put(1, A_STRING);
+
+        try {
+            checkedMap2.merge(1, A_STRING, (v1, v2) -> NOT_A_STRING);
+        } catch (ClassCastException expected) {}
+    }
 }
diff --git a/luni/src/test/java/libcore/java/util/TreeMapTest.java b/luni/src/test/java/libcore/java/util/TreeMapTest.java
index a358ef8..355d29d 100644
--- a/luni/src/test/java/libcore/java/util/TreeMapTest.java
+++ b/luni/src/test/java/libcore/java/util/TreeMapTest.java
@@ -596,4 +596,14 @@
             fail();
         } catch(ConcurrentModificationException expected) {}
     }
+
+    public void test_replace$K$V$V() {
+        MapDefaultMethodTester.test_replace$K$V$V(new TreeMap<>(), false /* acceptsNullKey */,
+                true /* acceptsNullValue */);
+    }
+
+    public void test_replace$K$V() {
+        MapDefaultMethodTester.test_replace$K$V(new TreeMap<>(), false /* acceptsNullKey */,
+                true /* acceptsNullValue */);
+    }
 }
diff --git a/luni/src/test/java/tests/security/cert/X509CertSelectorTest.java b/luni/src/test/java/tests/security/cert/X509CertSelectorTest.java
index a6eaf05..1616efb 100644
--- a/luni/src/test/java/tests/security/cert/X509CertSelectorTest.java
+++ b/luni/src/test/java/tests/security/cert/X509CertSelectorTest.java
@@ -397,7 +397,8 @@
         selector.setIssuer(iss1);
         assertTrue("The returned issuer should be equal to specified",
                    Arrays.equals(name1, selector.getIssuerAsBytes()));
-        assertFalse("The returned issuer should differ", name2.equals(selector.getIssuerAsBytes()));
+        assertFalse("The returned issuer should differ",
+                   Arrays.equals(name2, selector.getIssuerAsBytes()));
         selector.setIssuer(iss2);
         assertTrue("The returned issuer should be equal to specified",
                    Arrays.equals(name2, selector.getIssuerAsBytes()));
@@ -682,7 +683,7 @@
         assertTrue("The returned issuer should be equal to specified",
                    Arrays.equals(name1, selector.getSubjectAsBytes()));
         assertFalse("The returned issuer should differ",
-                    name2.equals(selector.getSubjectAsBytes()));
+                    Arrays.equals(name2, selector.getSubjectAsBytes()));
         selector.setSubject(sub2);
         assertTrue("The returned issuer should be equal to specified",
                    Arrays.equals(name2, selector.getSubjectAsBytes()));
diff --git a/ojluni/src/main/java/java/lang/AbstractStringBuilder.java b/ojluni/src/main/java/java/lang/AbstractStringBuilder.java
index e11ae3c..dfc2c6b 100755
--- a/ojluni/src/main/java/java/lang/AbstractStringBuilder.java
+++ b/ojluni/src/main/java/java/lang/AbstractStringBuilder.java
@@ -478,8 +478,16 @@
                 + s.length());
         int len = end - start;
         ensureCapacityInternal(count + len);
-        for (int i = start, j = count; i < end; i++, j++)
-            value[j] = s.charAt(i);
+        if (s instanceof String) {
+            ((String) s).getCharsNoCheck(start, end, value, count);
+        } else if (s instanceof AbstractStringBuilder) {
+            AbstractStringBuilder other = (AbstractStringBuilder) s;
+            System.arraycopy(other.value, start, value, count, len);
+        } else {
+            for (int i = start, j = count; i < end; i++, j++) {
+                value[j] = s.charAt(i);
+            }
+        }
         count += len;
         return this;
     }
diff --git a/ojluni/src/main/java/java/lang/String.java b/ojluni/src/main/java/java/lang/String.java
index 2781546..8a83df9 100755
--- a/ojluni/src/main/java/java/lang/String.java
+++ b/ojluni/src/main/java/java/lang/String.java
@@ -2211,17 +2211,13 @@
                 sb = new StringBuilder(count);
             }
 
-            for (int i = lastMatch; i < currentMatch; ++i) {
-                sb.append(charAt(i));
-            }
+            sb.append(this, lastMatch, currentMatch);
             sb.append(replacementStr);
             lastMatch = currentMatch + targetStr.count;
         }
 
         if (sb != null) {
-            for (int i = lastMatch; i < count; ++i) {
-                sb.append(charAt(i));
-            }
+            sb.append(this, lastMatch, count);
             return sb.toString();
         } else {
             return this;
diff --git a/ojluni/src/main/java/java/net/Inet6Address.java b/ojluni/src/main/java/java/net/Inet6Address.java
index a46223d..f66b68d 100755
--- a/ojluni/src/main/java/java/net/Inet6Address.java
+++ b/ojluni/src/main/java/java/net/Inet6Address.java
@@ -171,7 +171,7 @@
 
     /** @hide */
     public static final InetAddress ANY =
-            new Inet6Address(null, new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0);
+            new Inet6Address("::", new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0);
 
     /** @hide */
     public static final InetAddress LOOPBACK = new Inet6Address("localhost",
diff --git a/ojluni/src/main/java/java/text/ChoiceFormat.java b/ojluni/src/main/java/java/text/ChoiceFormat.java
index f513cd9..ccbd24f 100755
--- a/ojluni/src/main/java/java/text/ChoiceFormat.java
+++ b/ojluni/src/main/java/java/text/ChoiceFormat.java
@@ -208,7 +208,7 @@
                     } else if (tempBuffer.equals("-\u221E")) {
                         startValue = Double.NEGATIVE_INFINITY;
                     } else {
-                        startValue = Double.valueOf(segments[0].toString()).doubleValue();
+                        startValue = Double.parseDouble(segments[0].toString());
                     }
                 } catch (Exception e) {
                     throw new IllegalArgumentException();
diff --git a/ojluni/src/main/java/java/text/DecimalFormat.java b/ojluni/src/main/java/java/text/DecimalFormat.java
index b8eade3..bcc9e8b 100755
--- a/ojluni/src/main/java/java/text/DecimalFormat.java
+++ b/ojluni/src/main/java/java/text/DecimalFormat.java
@@ -465,10 +465,7 @@
     private void init(String pattern) {
         this.icuDecimalFormat =  new android.icu.text.DecimalFormat(pattern,
                 symbols.getIcuDecimalFormatSymbols());
-        maximumIntegerDigits = icuDecimalFormat.getMaximumIntegerDigits();
-        minimumIntegerDigits = icuDecimalFormat.getMinimumIntegerDigits();
-        maximumFractionDigits = icuDecimalFormat.getMaximumFractionDigits();
-        minimumFractionDigits = icuDecimalFormat.getMinimumFractionDigits();
+        updateFieldsFromIcu();
     }
 
     /**
@@ -1169,6 +1166,12 @@
     }
 
     private void updateFieldsFromIcu() {
+        // Imitate behaviour of ICU4C NumberFormat that Android used up to M.
+        // If the pattern doesn't enforce a different value (some exponential
+        // patterns do), then set the maximum integer digits to 2 billion.
+        if (icuDecimalFormat.getMaximumIntegerDigits() == DOUBLE_INTEGER_DIGITS) {
+            icuDecimalFormat.setMaximumIntegerDigits(2000000000);
+        }
         maximumIntegerDigits = icuDecimalFormat.getMaximumIntegerDigits();
         minimumIntegerDigits = icuDecimalFormat.getMinimumIntegerDigits();
         maximumFractionDigits = icuDecimalFormat.getMaximumFractionDigits();
diff --git a/ojluni/src/main/java/java/util/Collections.java b/ojluni/src/main/java/java/util/Collections.java
index 6b19738..1a9f83a 100644
--- a/ojluni/src/main/java/java/util/Collections.java
+++ b/ojluni/src/main/java/java/util/Collections.java
@@ -1796,7 +1796,6 @@
 
     // Synch Wrappers
 
-    // TODO: Replace {@code stream} with {@link Stream} once we have them.
     /**
      * Returns a synchronized (thread-safe) collection backed by the specified
      * collection.  In order to guarantee serial access, it is critical that
@@ -1805,7 +1804,7 @@
      *
      * It is imperative that the user manually synchronize on the returned
      * collection when traversing it via {@link Iterator}, {@link Spliterator}
-     * or {@code Stream}:
+     * or {@link Stream}:
      * <pre>
      *  Collection c = Collections.synchronizedCollection(myCollection);
      *     ...
@@ -2884,14 +2883,6 @@
         public boolean addAll(int index, Collection<? extends E> c) {
             return list.addAll(index, checkedCopyOf(c));
         }
-        @Override
-        public void replaceAll(UnaryOperator<E> operator) {
-            list.replaceAll(operator);
-        }
-        @Override
-        public void sort(Comparator<? super E> c) {
-            list.sort(c);
-        }
         public ListIterator<E> listIterator()   { return listIterator(0); }
 
         public ListIterator<E> listIterator(final int index) {
@@ -2926,6 +2917,32 @@
         public List<E> subList(int fromIndex, int toIndex) {
             return new CheckedList<>(list.subList(fromIndex, toIndex), type);
         }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @throws ClassCastException if the class of an element returned by the
+         *         operator prevents it from being added to this collection. The
+         *         exception may be thrown after some elements of the list have
+         *         already been replaced.
+         */
+        @Override
+        public void replaceAll(UnaryOperator<E> operator) {
+            Objects.requireNonNull(operator);
+
+            // Android-changed: Modified from OpenJDK 8 code because typeCheck returns void in
+            // OpenJDK 7.
+            list.replaceAll(e -> {
+                    E newValue = operator.apply(e);
+                    typeCheck(newValue);
+                    return newValue;
+            });
+        }
+
+        @Override
+        public void sort(Comparator<? super E> c) {
+            list.sort(c);
+        }
     }
 
     /**
@@ -3088,6 +3105,68 @@
             m.forEach(action);
         }
 
+        @Override
+        public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
+            m.replaceAll(typeCheck(function));
+        }
+
+        @Override
+        public V putIfAbsent(K key, V value) {
+            typeCheck(key, value);
+            return m.putIfAbsent(key, value);
+        }
+
+        @Override
+        public boolean remove(Object key, Object value) {
+            return m.remove(key, value);
+        }
+
+        @Override
+        public boolean replace(K key, V oldValue, V newValue) {
+            typeCheck(key, newValue);
+            return m.replace(key, oldValue, newValue);
+        }
+
+        @Override
+        public V replace(K key, V value) {
+            typeCheck(key, value);
+            return m.replace(key, value);
+        }
+
+        @Override
+        public V computeIfAbsent(K key,
+                Function<? super K, ? extends V> mappingFunction) {
+            Objects.requireNonNull(mappingFunction);
+            return m.computeIfAbsent(key, k -> {
+                V value = mappingFunction.apply(k);
+                typeCheck(k, value);
+                return value;
+            });
+        }
+
+        @Override
+        public V computeIfPresent(K key,
+                BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+            return m.computeIfPresent(key, typeCheck(remappingFunction));
+        }
+
+        @Override
+        public V compute(K key,
+                BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+            return m.compute(key, typeCheck(remappingFunction));
+        }
+
+        @Override
+        public V merge(K key, V value,
+                BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+            Objects.requireNonNull(remappingFunction);
+            return m.merge(key, value, (v1, v2) -> {
+                V newValue = remappingFunction.apply(v1, v2);
+                typeCheck(null, newValue);
+                return newValue;
+            });
+        }
+
         /**
          * We need this class in addition to CheckedSet as Map.Entry permits
          * modification of the backing Map via the setValue operation.  This
diff --git a/ojluni/src/main/java/java/util/ComparableTimSort.java b/ojluni/src/main/java/java/util/ComparableTimSort.java
index e7c7ac0..36c8d90 100755
--- a/ojluni/src/main/java/java/util/ComparableTimSort.java
+++ b/ojluni/src/main/java/java/util/ComparableTimSort.java
@@ -144,10 +144,14 @@
          * large) stack lengths for smaller arrays.  The "magic numbers" in the
          * computation below must be changed if MIN_MERGE is decreased.  See
          * the MIN_MERGE declaration above for more information.
+         * The maximum value of 49 allows for an array up to length
+         * Integer.MAX_VALUE-4, if array is filled by the worst case stack size
+         * increasing scenario. More explanations are given in section 4 of:
+         * http://envisage-project.eu/wp-content/uploads/2015/02/sorting.pdf
          */
         int stackLen = (len <    120  ?  5 :
                         len <   1542  ? 10 :
-                        len < 119151  ? 24 : 40);
+                        len < 119151  ? 24 : 49);
         runBase = new int[stackLen];
         runLen = new int[stackLen];
     }
diff --git a/ojluni/src/main/java/java/util/IdentityHashMap.java b/ojluni/src/main/java/java/util/IdentityHashMap.java
index ea1948d..b860cca 100755
--- a/ojluni/src/main/java/java/util/IdentityHashMap.java
+++ b/ojluni/src/main/java/java/util/IdentityHashMap.java
@@ -28,6 +28,7 @@
 import java.io.*;
 import java.lang.reflect.Array;
 import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
 import java.util.function.Consumer;
 
 /**
@@ -1359,6 +1360,25 @@
         }
     }
 
+    @SuppressWarnings("unchecked")
+    @Override
+    public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
+        Objects.requireNonNull(function);
+        int expectedModCount = modCount;
+
+        Object[] t = table;
+        for (int index = 0; index < t.length; index += 2) {
+            Object k = t[index];
+            if (k != null) {
+                t[index + 1] = function.apply((K) unmaskNull(k), (V) t[index + 1]);
+            }
+
+            if (modCount != expectedModCount) {
+                throw new ConcurrentModificationException();
+            }
+        }
+    }
+
     /**
      * Similar form as array-based Spliterators, but skips blank elements,
      * and guestimates size as decreasing by half per split.
diff --git a/ojluni/src/main/java/java/util/Spliterators.java b/ojluni/src/main/java/java/util/Spliterators.java
index 3f97a83..79c0ef3 100644
--- a/ojluni/src/main/java/java/util/Spliterators.java
+++ b/ojluni/src/main/java/java/util/Spliterators.java
@@ -384,7 +384,7 @@
      */
     private static void checkFromToBounds(int arrayLength, int origin, int fence) {
         if (origin > fence) {
-            throw new IllegalArgumentException(
+            throw new ArrayIndexOutOfBoundsException(
                     "origin(" + origin + ") > fence(" + fence + ")");
         }
         if (origin < 0) {
diff --git a/ojluni/src/main/java/java/util/TimSort.java b/ojluni/src/main/java/java/util/TimSort.java
index 9966f74..ea0d58f 100755
--- a/ojluni/src/main/java/java/util/TimSort.java
+++ b/ojluni/src/main/java/java/util/TimSort.java
@@ -174,10 +174,14 @@
          * large) stack lengths for smaller arrays.  The "magic numbers" in the
          * computation below must be changed if MIN_MERGE is decreased.  See
          * the MIN_MERGE declaration above for more information.
+         * The maximum value of 49 allows for an array up to length
+         * Integer.MAX_VALUE-4, if array is filled by the worst case stack size
+         * increasing scenario. More explanations are given in section 4 of:
+         * http://envisage-project.eu/wp-content/uploads/2015/02/sorting.pdf
          */
         int stackLen = (len <    120  ?  5 :
                         len <   1542  ? 10 :
-                        len < 119151  ? 24 : 40);
+                        len < 119151  ? 24 : 49);
         runBase = new int[stackLen];
         runLen = new int[stackLen];
     }
diff --git a/ojluni/src/main/java/java/util/TreeMap.java b/ojluni/src/main/java/java/util/TreeMap.java
index 49346ca..5e26f0f 100755
--- a/ojluni/src/main/java/java/util/TreeMap.java
+++ b/ojluni/src/main/java/java/util/TreeMap.java
@@ -998,6 +998,27 @@
     }
 
     @Override
+    public boolean replace(K key, V oldValue, V newValue) {
+        TreeMapEntry<K,V> p = getEntry(key);
+        if (p!=null && Objects.equals(oldValue, p.value)) {
+            p.value = newValue;
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public V replace(K key, V value) {
+        TreeMapEntry<K,V> p = getEntry(key);
+        if (p!=null) {
+            V oldValue = p.value;
+            p.value = value;
+            return oldValue;
+        }
+        return null;
+    }
+
+    @Override
     public void forEach(BiConsumer<? super K, ? super V> action) {
         Objects.requireNonNull(action);
         int expectedModCount = modCount;
@@ -1014,6 +1035,7 @@
     public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
         Objects.requireNonNull(function);
         int expectedModCount = modCount;
+
         for (TreeMapEntry<K, V> e = getFirstEntry(); e != null; e = successor(e)) {
             e.value = function.apply(e.key, e.value);
 
diff --git a/ojluni/src/main/java/java/util/logging/Logger.java b/ojluni/src/main/java/java/util/logging/Logger.java
index e9728de..3652914 100755
--- a/ojluni/src/main/java/java/util/logging/Logger.java
+++ b/ojluni/src/main/java/java/util/logging/Logger.java
@@ -329,7 +329,7 @@
                     return System.getProperty(key);
                 }
             });
-            return Boolean.valueOf(s);
+            return Boolean.parseBoolean(s);
         }
     }
 
diff --git a/ojluni/src/main/java/java/util/regex/Matcher.java b/ojluni/src/main/java/java/util/regex/Matcher.java
index 47062d7..dc37946 100755
--- a/ojluni/src/main/java/java/util/regex/Matcher.java
+++ b/ojluni/src/main/java/java/util/regex/Matcher.java
@@ -26,6 +26,7 @@
 
 package java.util.regex;
 
+import libcore.util.NativeAllocationRegistry;
 
 /**
  * An engine that performs match operations on a {@link java.lang.CharSequence
@@ -115,6 +116,14 @@
     private long address;
 
     /**
+     * If non-null, a Runnable that can be used to explicitly deallocate address.
+     */
+    private Runnable nativeFinalizer;
+
+    private static final NativeAllocationRegistry registry = new NativeAllocationRegistry(
+            getNativeFinalizer(), nativeSize());
+
+    /**
      * Holds the input text.
      */
     private String input;
@@ -211,11 +220,13 @@
         this.pattern = newPattern;
 
         synchronized (this) {
-            if (address != 0) {
-                closeImpl(address);
+            if (nativeFinalizer != null) {
+                nativeFinalizer.run();
                 address = 0; // In case openImpl throws.
+                nativeFinalizer = null;
             }
             address = openImpl(pattern.address);
+            nativeFinalizer = registry.registerNativeAllocation(this, address);
         }
 
         if (input != null) {
@@ -1041,16 +1052,6 @@
         }
     }
 
-    @Override protected void finalize() throws Throwable {
-        try {
-            synchronized (this) {
-                closeImpl(address);
-            }
-        } finally {
-            super.finalize();
-        }
-    }
-
     /**
      * Returns the start index of the previous match.  </p>
      *
@@ -1093,13 +1094,14 @@
         return matchOffsets[group * 2];
     }
 
-    private static native void closeImpl(long addr);
     private static native boolean findImpl(long addr, String s, int startIndex, int[] offsets);
     private static native boolean findNextImpl(long addr, String s, int[] offsets);
+    private static native long getNativeFinalizer();
     private static native int groupCountImpl(long addr);
     private static native boolean hitEndImpl(long addr);
     private static native boolean lookingAtImpl(long addr, String s, int[] offsets);
     private static native boolean matchesImpl(long addr, String s, int[] offsets);
+    private static native int nativeSize();
     private static native long openImpl(long patternAddr);
     private static native boolean requireEndImpl(long addr);
     private static native void setInputImpl(long addr, String s, int start, int end);
diff --git a/ojluni/src/main/java/java/util/regex/Pattern.java b/ojluni/src/main/java/java/util/regex/Pattern.java
index 442e22b..1d772d9 100755
--- a/ojluni/src/main/java/java/util/regex/Pattern.java
+++ b/ojluni/src/main/java/java/util/regex/Pattern.java
@@ -26,6 +26,8 @@
 
 package java.util.regex;
 
+import libcore.util.NativeAllocationRegistry;
+
 import java.util.Iterator;
 import java.util.ArrayList;
 import java.util.NoSuchElementException;
@@ -927,6 +929,9 @@
 
     transient long address;
 
+    private static final NativeAllocationRegistry registry = new NativeAllocationRegistry(
+            getNativeFinalizer(), nativeSize());
+
 
     /**
      * Compiles the given regular expression into a pattern.  </p>
@@ -1333,21 +1338,12 @@
         // They even have the same value in native code.
         int icuFlags = flags & (CASE_INSENSITIVE | COMMENTS | MULTILINE | DOTALL | UNIX_LINES);
         address = compileImpl(icuPattern, icuFlags);
+        registry.registerNativeAllocation(this, address);
     }
 
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            closeImpl(address);
-        } finally {
-            super.finalize();
-        }
-    }
-
-    private static native void closeImpl(long addr);
     private static native long compileImpl(String regex, int flags);
-
+    private static native long getNativeFinalizer();
+    private static native int nativeSize();
 
     /**
      * Creates a predicate which can be used to match a string.
diff --git a/ojluni/src/main/java/sun/misc/VM.java b/ojluni/src/main/java/sun/misc/VM.java
index e8af1d1..b57eb4a 100755
--- a/ojluni/src/main/java/sun/misc/VM.java
+++ b/ojluni/src/main/java/sun/misc/VM.java
@@ -295,7 +295,7 @@
         allowGetCallerClass = (s != null
                                    ? (s.isEmpty() || Boolean.parseBoolean(s))
                                    : true) ||
-             Boolean.valueOf(props.getProperty("jdk.logging.allowStackWalkSearch"));
+             Boolean.parseBoolean(props.getProperty("jdk.logging.allowStackWalkSearch"));
 
         // Remove other private system properties
         // used by java.lang.Integer.IntegerCache
diff --git a/ojluni/src/main/java/sun/misc/Version.java b/ojluni/src/main/java/sun/misc/Version.java
index 0a7de82..2908005 100644
--- a/ojluni/src/main/java/sun/misc/Version.java
+++ b/ojluni/src/main/java/sun/misc/Version.java
@@ -280,7 +280,7 @@
                     int nextChar = 3;
                     try {
                         String uu = cs.subSequence(1, 3).toString();
-                        jvm_update_version = Integer.valueOf(uu).intValue();
+                        jvm_update_version = Integer.parseInt(uu);
                         if (cs.length() >= 4) {
                             char c = cs.charAt(3);
                             if (c >= 'a' && c <= 'z') {
@@ -305,7 +305,7 @@
                             Character.isDigit(s.charAt(1)) &&
                             Character.isDigit(s.charAt(2))) {
                             jvm_build_number =
-                                Integer.valueOf(s.substring(1, 3)).intValue();
+                                Integer.parseInt(s.substring(1, 3));
                             break;
                         }
                     }
diff --git a/ojluni/src/main/java/sun/net/www/http/HttpClient.java b/ojluni/src/main/java/sun/net/www/http/HttpClient.java
index b5a4329..09e0dbb 100755
--- a/ojluni/src/main/java/sun/net/www/http/HttpClient.java
+++ b/ojluni/src/main/java/sun/net/www/http/HttpClient.java
@@ -150,13 +150,13 @@
             new sun.security.action.GetPropertyAction("sun.net.http.retryPost"));
 
         if (keepAlive != null) {
-            keepAliveProp = Boolean.valueOf(keepAlive).booleanValue();
+            keepAliveProp = Boolean.parseBoolean(keepAlive);
         } else {
             keepAliveProp = true;
         }
 
         if (retryPost != null) {
-            retryPostProp = Boolean.valueOf(retryPost).booleanValue();
+            retryPostProp = Boolean.parseBoolean(retryPost);
         } else
             retryPostProp = true;
 
diff --git a/ojluni/src/main/java/sun/nio/ch/EPollSelectorImpl.java b/ojluni/src/main/java/sun/nio/ch/EPollSelectorImpl.java
index aac539b..113f84b 100755
--- a/ojluni/src/main/java/sun/nio/ch/EPollSelectorImpl.java
+++ b/ojluni/src/main/java/sun/nio/ch/EPollSelectorImpl.java
@@ -159,7 +159,7 @@
         if (closed)
             throw new ClosedSelectorException();
         SelChImpl ch = ski.channel;
-        int fd = Integer.valueOf(ch.getFDVal());
+        Integer fd = Integer.valueOf(ch.getFDVal());
         fdToKey.put(fd, ski);
         pollWrapper.add(fd);
         keys.add(ski);
diff --git a/ojluni/src/main/java/sun/security/ssl/SSLSessionContextImpl.java b/ojluni/src/main/java/sun/security/ssl/SSLSessionContextImpl.java
index 51fac9b..756a48e 100755
--- a/ojluni/src/main/java/sun/security/ssl/SSLSessionContextImpl.java
+++ b/ojluni/src/main/java/sun/security/ssl/SSLSessionContextImpl.java
@@ -207,7 +207,7 @@
                         "javax.net.ssl.sessionCacheSize");
                 }
             });
-            cacheLimit = (s != null) ? Integer.valueOf(s).intValue() : 0;
+            cacheLimit = (s != null) ? Integer.parseInt(s) : 0;
         } catch (Exception e) {
         }
 
diff --git a/support/src/test/java/libcore/java/security/CpuFeatures.java b/support/src/test/java/libcore/java/security/CpuFeatures.java
index 30ca868..319056a 100644
--- a/support/src/test/java/libcore/java/security/CpuFeatures.java
+++ b/support/src/test/java/libcore/java/security/CpuFeatures.java
@@ -19,6 +19,8 @@
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Matcher;
@@ -39,6 +41,19 @@
             return true;
         }
 
+        // If we're in an emulated ABI, Conscrypt's NativeCrypto might bridge to
+        // a library that has accelerated AES instructions. See if Conscrypt
+        // detects that condition.
+        try {
+            Class<?> nativeCrypto = Class.forName("com.android.org.conscrypt.NativeCrypto");
+            Method EVP_has_aes_hardware = nativeCrypto.getDeclaredMethod("EVP_has_aes_hardware");
+            return ((Integer) EVP_has_aes_hardware.invoke(null)) == 1;
+        } catch (ClassNotFoundException | NoSuchMethodException | SecurityException
+                | IllegalAccessException | IllegalArgumentException ignored) {
+        } catch (InvocationTargetException e) {
+            throw new IllegalArgumentException(e);
+        }
+
         return false;
     }