Merge "Modify tests to reflect different URL encoding for single quote" into nyc-mr1-dev
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
index a9f64a2..a16c4e4 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
@@ -30,6 +30,7 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.NoSuchElementException;
 import java.util.Spliterator;
 import java.util.Vector;
@@ -850,6 +851,15 @@
         }
     }
 
+    // http://b/30974375
+    public void test_listIterator_addAndPrevious() {
+        ListIterator<String> it = new Vector<String>().listIterator();
+        assertFalse(it.hasNext());
+        it.add("value");
+        assertEquals("value", it.previous());
+        assertTrue(it.hasNext());
+    }
+
     /**
      * java.util.Vector#remove(int)
      */
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java
index 649be09..52d9142 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java
@@ -126,30 +126,12 @@
     public final void test_createSocket_InetAddressIInetAddressI() throws Exception {
         SocketFactory sf = SocketFactory.getDefault();
         int sport = new ServerSocket(0).getLocalPort();
-        int[] invalidPorts = {Integer.MIN_VALUE, -1, 65536, Integer.MAX_VALUE};
 
         Socket s = sf.createSocket(InetAddress.getLocalHost(), sport,
-                                   InetAddress.getLocalHost(), 0);
+                InetAddress.getLocalHost(), 0);
         assertNotNull(s);
         assertTrue("1: Failed to create socket", s.getPort() == sport);
         int portNumber = s.getLocalPort();
-
-        for (int i = 0; i < invalidPorts.length; i++) {
-            try {
-              sf.createSocket(InetAddress.getLocalHost(), invalidPorts[i],
-                              InetAddress.getLocalHost(), portNumber);
-                fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]);
-            } catch (IllegalArgumentException expected) {
-            }
-
-            try {
-                sf.createSocket(InetAddress.getLocalHost(), sport,
-                                InetAddress.getLocalHost(), invalidPorts[i]);
-                fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]);
-            } catch (IllegalArgumentException expected) {
-            }
-        }
-
         try {
             sf.createSocket(InetAddress.getLocalHost(), sport,
                             InetAddress.getLocalHost(), portNumber);
@@ -165,6 +147,64 @@
         }
     }
 
+    // Checks the behavior of createSocket(InetAddress, int, InetAddress, int) when the
+    // ports are invalid.
+    public void test_createSocket_InetAddressIInetAddressI_IllegalArgumentException()
+            throws Exception {
+        SocketFactory sf = SocketFactory.getDefault();
+        int validPort = new ServerSocket(0).getLocalPort();
+        int[] invalidPorts = {Integer.MIN_VALUE, -1, 65536, Integer.MAX_VALUE};
+
+        for (int i = 0; i < invalidPorts.length; i++) {
+            // Check invalid server port.
+            try (Socket s = sf.createSocket(InetAddress.getLocalHost() /* ServerAddress */,
+                    invalidPorts[i] /* ServerPort */,
+                    InetAddress.getLocalHost() /* ClientAddress */,
+                    validPort /* ClientPort */)) {
+                fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]);
+            } catch (IllegalArgumentException expected) {
+            }
+
+            // Check invalid client port.
+            try (Socket s = sf.createSocket(InetAddress.getLocalHost() /* ServerAddress */,
+                    validPort /* ServerPort */,
+                    InetAddress.getLocalHost() /* ClientAddress */,
+                    invalidPorts[i]) /* ClientPort */){
+                fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]);
+            } catch (IllegalArgumentException expected) {
+            }
+        }
+    }
+
+    // b/31019685
+    // Checks the ordering of port number validation (IllegalArgumentException) and binding error.
+    public void test_createSocket_InetAddressIInetAddressI_ExceptionOrder() throws IOException {
+        int invalidPort = Integer.MAX_VALUE;
+        SocketFactory sf = SocketFactory.getDefault();
+        int validServerPortNumber = new ServerSocket(0).getLocalPort();
+
+        // Create a socket with localhost as the client address so that another attempt to bind
+        // would fail.
+        Socket s = sf.createSocket(InetAddress.getLocalHost() /* ServerAddress */,
+                validServerPortNumber /* ServerPortNumber */,
+                InetAddress.getLocalHost() /* ClientAddress */,
+                0 /* ClientPortNumber */);
+
+        int assignedLocalPortNumber = s.getLocalPort();
+
+        // Create a socket with an invalid port and localhost as the client address. Both
+        // BindException and IllegalArgumentException are expected in this case as the address is
+        // already bound to the socket above and port is invalid, however, to preserve the
+        // precedence order, IllegalArgumentException should be thrown.
+        try (Socket s1 = sf.createSocket(InetAddress.getLocalHost() /* ServerAddress */,
+                invalidPort /* ServerPortNumber */,
+                InetAddress.getLocalHost() /* ClientAddress */,
+                assignedLocalPortNumber /* ClientPortNumber */)) {
+            fail("IllegalArgumentException wasn't thrown for " + invalidPort);
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
     /**
      * javax.net.SocketFactory#createSocket(String host, int port,
      *                                             InetAddress localHost, int localPort)
diff --git a/luni/src/main/java/libcore/icu/TimeZoneNames.java b/luni/src/main/java/libcore/icu/TimeZoneNames.java
index daa915e..917d9ce 100644
--- a/luni/src/main/java/libcore/icu/TimeZoneNames.java
+++ b/luni/src/main/java/libcore/icu/TimeZoneNames.java
@@ -68,7 +68,7 @@
             }
 
             long nativeStart = System.nanoTime();
-            fillZoneStrings(locale.toString(), result);
+            fillZoneStrings(locale.toLanguageTag(), result);
             long nativeEnd = System.nanoTime();
 
             internStrings(result);
diff --git a/luni/src/main/java/libcore/net/MimeUtils.java b/luni/src/main/java/libcore/net/MimeUtils.java
index 3b59b87..88a7392 100644
--- a/luni/src/main/java/libcore/net/MimeUtils.java
+++ b/luni/src/main/java/libcore/net/MimeUtils.java
@@ -210,6 +210,12 @@
         add("application/x-xcf", "xcf");
         add("application/x-xfig", "fig");
         add("application/xhtml+xml", "xhtml");
+        // Video mime types for 3GPP first so they'll be default for guessMimeTypeFromExtension
+        // See RFC 3839 for 3GPP and RFC 4393 for 3GPP2
+        add("video/3gpp", "3gpp");
+        add("video/3gpp", "3gp");
+        add("video/3gpp2", "3gpp2");
+        add("video/3gpp2", "3g2");
         add("audio/3gpp", "3gpp");
         add("audio/aac", "aac");
         add("audio/aac-adts", "aac");
@@ -353,10 +359,6 @@
         add("text/x-tex", "cls");
         add("text/x-vcalendar", "vcs");
         add("text/x-vcard", "vcf");
-        add("video/3gpp", "3gpp");
-        add("video/3gpp", "3gp");
-        add("video/3gpp2", "3gpp2");
-        add("video/3gpp2", "3g2");
         add("video/avi", "avi");
         add("video/dl", "dl");
         add("video/dv", "dif");
diff --git a/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java b/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java
index fce8507..ec5ca03 100644
--- a/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java
+++ b/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java
@@ -93,9 +93,6 @@
                 + oldTime.toString());
         System.out.println("Time for " + ITERATIONS + " x new hash processing: "
                 + newTime.toString());
-
-        assertTrue("New hash should be faster:\nold=" + oldTime.toString() + "\nnew="
-                + newTime.toString(), newTime.mean() < oldTime.mean());
     }
 
     /**
diff --git a/luni/src/test/java/libcore/java/net/SocketTest.java b/luni/src/test/java/libcore/java/net/SocketTest.java
index 52638d4..e2d3964 100644
--- a/luni/src/test/java/libcore/java/net/SocketTest.java
+++ b/luni/src/test/java/libcore/java/net/SocketTest.java
@@ -21,6 +21,7 @@
 import java.io.OutputStream;
 import java.net.ConnectException;
 import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.Proxy;
@@ -34,6 +35,7 @@
 import java.net.UnknownHostException;
 import java.nio.channels.ServerSocketChannel;
 import java.nio.channels.SocketChannel;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
@@ -45,6 +47,10 @@
 
 
 public class SocketTest extends junit.framework.TestCase {
+
+    // This hostname is required to resolve to 127.0.0.1 and ::1 for all tests to pass.
+    private static final String ALL_LOOPBACK_HOSTNAME = "loopback46.unittest.grpc.io";
+
     // See http://b/2980559.
     public void test_close() throws Exception {
         Socket s = new Socket();
@@ -542,4 +548,51 @@
             new SocketThatFailOnClose(InetAddress.getLocalHost(), 1, true);
         } catch(IOException expected) {}
     }
+
+    // b/30007735
+    public void testSocketTestAllAddresses() throws Exception {
+        // Socket Ctor should try all sockets.
+        //
+        // This test creates a server socket bound to 127.0.0.1 or ::1 only, and connects using a
+        // hostname that resolves to both addresses. We should be able to connect to the server
+        // socket in either setup.
+        final String loopbackHost = ALL_LOOPBACK_HOSTNAME;
+
+        assertTrue("Loopback DNS record is unreachable or is invalid.", checkLoopbackHost(
+                loopbackHost));
+
+        final int port = 9999;
+        for (InetAddress addr : new InetAddress[]{ Inet4Address.LOOPBACK, Inet6Address.LOOPBACK }) {
+            try (ServerSocket ss = new ServerSocket(port, 0, addr)) {
+                new Thread(() -> {
+                    try {
+                        ss.accept();
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }).start();
+
+                assertTrue(canConnect(loopbackHost, port));
+            }
+        }
+    }
+
+    /** Confirm the supplied hostname maps to only loopback addresses. */
+    private static boolean checkLoopbackHost(String host) {
+        try {
+            List<InetAddress> addrs = Arrays.asList(InetAddress.getAllByName(host));
+            return addrs.stream().allMatch(InetAddress::isLoopbackAddress) &&
+                    addrs.contains(Inet4Address.LOOPBACK) && addrs.contains(Inet6Address.LOOPBACK);
+        } catch (UnknownHostException e) {
+            return false;
+        }
+    }
+
+    private static boolean canConnect(String host, int port) {
+        try(Socket sock = new Socket(host, port)) {
+            return sock.isConnected();
+        } catch (IOException e) {
+            return false;
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index 5a6fa7f..cb2cd0f 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -16,13 +16,20 @@
 
 package libcore.java.util;
 
+import junit.framework.TestCase;
+
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.Collections;
 import java.util.Date;
+import java.util.List;
 import java.util.Locale;
 import java.util.SimpleTimeZone;
 import java.util.TimeZone;
-import junit.framework.TestCase;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.atomic.AtomicInteger;
 
 public class TimeZoneTest extends TestCase {
     // http://code.google.com/p/android/issues/detail?id=877
@@ -261,6 +268,31 @@
         assertEquals("", failures.toString());
     }
 
+    // http://b/30527513
+    public void testDisplayNamesWithScript() throws Exception {
+        Locale latinLocale = Locale.forLanguageTag("sr-Latn-RS");
+        Locale cyrillicLocale = Locale.forLanguageTag("sr-Cyrl-RS");
+        Locale noScriptLocale = Locale.forLanguageTag("sr-RS");
+        TimeZone tz = TimeZone.getTimeZone("Europe/London");
+
+        final String latinName = "Srednje vreme po Griniču";
+        final String cyrillicName = "Средње време по Гриничу";
+
+        // Check java.util.TimeZone
+        assertEquals(latinName, tz.getDisplayName(latinLocale));
+        assertEquals(cyrillicName, tz.getDisplayName(cyrillicLocale));
+        assertEquals(cyrillicName, tz.getDisplayName(noScriptLocale));
+
+        // Check ICU TimeZoneNames
+        // The one-argument getDisplayName() override uses LONG_GENERIC style which is different
+        // from what java.util.TimeZone uses. Force the LONG style to get equivalent results.
+        final int style = android.icu.util.TimeZone.LONG;
+        android.icu.util.TimeZone utz = android.icu.util.TimeZone.getTimeZone(tz.getID());
+        assertEquals(latinName, utz.getDisplayName(false, style, latinLocale));
+        assertEquals(cyrillicName, utz.getDisplayName(false, style, cyrillicLocale));
+        assertEquals(cyrillicName, utz.getDisplayName(false, style, noScriptLocale));
+    }
+
     // http://b/7955614
     public void testApia() throws Exception {
         TimeZone tz = TimeZone.getTimeZone("Pacific/Apia");
@@ -340,4 +372,107 @@
             TimeZone.setDefault(origTz);
         }
     }
+
+    // http://b/30937209
+    public void testSetDefaultDeadlock() throws InterruptedException, BrokenBarrierException {
+        // Since this tests a deadlock, the test has two fundamental problems:
+        // - it is probabilistic: it's not guaranteed to fail if the problem exists
+        // - if it fails, it will effectively hang the current runtime, as no other thread will
+        //   be able to call TimeZone.getDefault()/setDefault() successfully any more.
+
+        // 10 was too low to be reliable, 100 failed more than half the time (on a bullhead).
+        final int iterations = 100;
+        TimeZone otherTimeZone = TimeZone.getTimeZone("Europe/London");
+        AtomicInteger setterCount = new AtomicInteger();
+        CyclicBarrier startBarrier = new CyclicBarrier(2);
+        Thread setter = new Thread(() -> {
+            waitFor(startBarrier);
+            for (int i = 0; i < iterations; i++) {
+                TimeZone.setDefault(otherTimeZone);
+                TimeZone.setDefault(null);
+                setterCount.set(i+1);
+            }
+        });
+        setter.setName("testSetDefaultDeadlock setter");
+
+        AtomicInteger getterCount = new AtomicInteger();
+        Thread getter = new Thread(() -> {
+            waitFor(startBarrier);
+            for (int i = 0; i < iterations; i++) {
+                android.icu.util.TimeZone.getDefault();
+                getterCount.set(i+1);
+            }
+        });
+        getter.setName("testSetDefaultDeadlock getter");
+
+        setter.start();
+        getter.start();
+
+        // 2 seconds is plenty: If successful, we usually complete much faster.
+        setter.join(1000);
+        getter.join(1000);
+        if (setter.isAlive() || getter.isAlive()) {
+            fail("Threads are still alive. Getter iteration count: " + getterCount.get()
+                    + ", setter iteration count: " + setterCount.get());
+        }
+        // Guard against unexpected uncaught exceptions.
+        assertEquals("Setter iterations", iterations, setterCount.get());
+        assertEquals("Getter iterations", iterations, getterCount.get());
+    }
+
+    // http://b/30979219
+    public void testSetDefaultRace() throws InterruptedException {
+        // Since this tests a race condition, the test is probabilistic: it's not guaranteed to
+        // fail if the problem exists
+
+        // These iterations are significantly faster than the ones in #testSetDefaultDeadlock
+        final int iterations = 10000;
+        List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<>());
+        Thread.UncaughtExceptionHandler handler = (t, e) -> exceptions.add(e);
+
+        CyclicBarrier startBarrier = new CyclicBarrier(2);
+        Thread clearer = new Thread(() -> {
+            waitFor(startBarrier);
+            for (int i = 0; i < iterations; i++) {
+                // This is not public API but can effectively be invoked via
+                // java.util.TimeZone.setDefault. Call it directly to reduce the amount of code
+                // involved in this test.
+                android.icu.util.TimeZone.clearCachedDefault();
+            }
+        });
+        clearer.setName("testSetDefaultRace clearer");
+        clearer.setUncaughtExceptionHandler(handler);
+
+        Thread getter = new Thread(() -> {
+            waitFor(startBarrier);
+            for (int i = 0; i < iterations; i++) {
+                android.icu.util.TimeZone.getDefault();
+            }
+        });
+        getter.setName("testSetDefaultRace getter");
+        getter.setUncaughtExceptionHandler(handler);
+
+        clearer.start();
+        getter.start();
+
+        // 2 seconds is plenty: If successful, we usually complete much faster.
+        clearer.join(1000);
+        getter.join(1000);
+
+        if (!exceptions.isEmpty()) {
+            Throwable firstException = exceptions.get(0);
+            firstException.printStackTrace();
+            fail("Threads did not succeed successfully: " + firstException);
+        }
+        assertFalse("clearer thread is still alive", clearer.isAlive());
+        assertFalse("getter thread is still alive", getter.isAlive());
+    }
+
+    private static void waitFor(CyclicBarrier barrier) {
+        try {
+            barrier.await();
+        } catch (InterruptedException | BrokenBarrierException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
index 02210ac..03fa29a 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
@@ -16,7 +16,18 @@
 
 package libcore.java.util.zip;
 
+import android.system.OsConstants;
+import libcore.io.Libcore;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
 import java.util.zip.ZipOutputStream;
 
 public final class ZipFileTest extends AbstractZipFileTest {
@@ -25,4 +36,42 @@
     protected ZipOutputStream createZipOutputStream(OutputStream wrapped) {
         return new ZipOutputStream(wrapped);
     }
+
+    // http://b/30407219
+    public void testZipFileOffsetNeverChangesAfterInit() throws Exception {
+        final File f = createTemporaryZipFile();
+        writeEntries(createZipOutputStream(new BufferedOutputStream(new FileOutputStream(f))),
+                2 /* number of entries */, 1024 /* entry size */, true /* setEntrySize */);
+
+        ZipFile zipFile = new ZipFile(f);
+        FileDescriptor fd = new FileDescriptor();
+        fd.setInt$(zipFile.getFileDescriptor());
+
+        long initialOffset = android.system.Os.lseek(fd, 0, OsConstants.SEEK_CUR);
+
+        Enumeration<? extends ZipEntry> entries = zipFile.entries();
+        assertOffset(initialOffset, fd);
+
+        // Get references to the two elements in the file.
+        ZipEntry entry1 = entries.nextElement();
+        ZipEntry entry2 = entries.nextElement();
+        assertFalse(entries.hasMoreElements());
+        assertOffset(initialOffset, fd);
+
+        InputStream is1 = zipFile.getInputStream(entry1);
+        assertOffset(initialOffset, fd);
+        is1.read(new byte[256]);
+        assertOffset(initialOffset, fd);
+        is1.close();
+
+        assertNotNull(zipFile.getEntry(entry2.getName()));
+        assertOffset(initialOffset, fd);
+
+        zipFile.close();
+    }
+
+    private static void assertOffset(long initialOffset, FileDescriptor fd) throws Exception {
+        long currentOffset = android.system.Os.lseek(fd, 0, OsConstants.SEEK_CUR);
+        assertEquals(initialOffset, currentOffset);
+    }
 }
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index a75a66d..4ce883b 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -2683,6 +2683,27 @@
 
     /*
      * Test vector generation:
+     * openssl rand -hex 16 | sed 's/\(..\)/(byte) 0x\1, /g'
+     */
+    private static final byte[] DES_112_KEY = new byte[] {
+            (byte) 0x6b, (byte) 0xb3, (byte) 0x85, (byte) 0x1c, (byte) 0x3d, (byte) 0x50,
+            (byte) 0xd4, (byte) 0x95, (byte) 0x39, (byte) 0x48, (byte) 0x77, (byte) 0x30,
+            (byte) 0x1a, (byte) 0xd7, (byte) 0x86, (byte) 0x57,
+    };
+
+    /*
+     * Test vector generation:
+     * openssl rand -hex 24 | sed 's/\(..\)/(byte) 0x\1, /g'
+     */
+    private static final byte[] DES_168_KEY = new byte[] {
+            (byte) 0xfe, (byte) 0xd4, (byte) 0xd7, (byte) 0xc9, (byte) 0x8a, (byte) 0x13,
+            (byte) 0x6a, (byte) 0xa8, (byte) 0x5a, (byte) 0xb8, (byte) 0x19, (byte) 0xb8,
+            (byte) 0xcf, (byte) 0x3c, (byte) 0x5f, (byte) 0xe0, (byte) 0xa2, (byte) 0xf7,
+            (byte) 0x7b, (byte) 0x65, (byte) 0x43, (byte) 0xc0, (byte) 0xc4, (byte) 0xe1,
+    };
+
+    /*
+     * Test vector generation:
      * openssl rand -hex 16
      * echo '3d4f8970b1f27537f40a39298a41555f' | sed 's/\(..\)/(byte) 0x\1, /g'
      */
@@ -2727,6 +2748,61 @@
     };
 
     /*
+     * Test vector generation:
+     * echo -n 'Testing rocks!' | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] DES_Plaintext1 = new byte[] {
+            (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x69, (byte) 0x6E,
+            (byte) 0x67, (byte) 0x20, (byte) 0x72, (byte) 0x6F, (byte) 0x63, (byte) 0x6B,
+            (byte) 0x73, (byte) 0x21
+    };
+
+    /*
+     * Test vector generation: take DES_Plaintext1 and PKCS #5 pad it manually (it's not hard).
+     */
+    private static final byte[] DES_Plaintext1_PKCS5_Padded = new byte[] {
+            (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x69, (byte) 0x6E,
+            (byte) 0x67, (byte) 0x20, (byte) 0x72, (byte) 0x6F, (byte) 0x63, (byte) 0x6B,
+            (byte) 0x73, (byte) 0x21, (byte) 0x02, (byte) 0x02,
+    };
+
+    /*
+     * Test vector generation:
+     * openssl rand -hex 8 | sed 's/\(..\)/(byte) 0x\1, /g'
+     */
+    private static final byte[] DES_IV1 = new byte[] {
+            (byte) 0x5c, (byte) 0x47, (byte) 0x5e, (byte) 0x57, (byte) 0x0c, (byte) 0x46,
+            (byte) 0xcb, (byte) 0x47,
+    };
+
+    /*
+     * Test vector generation:
+     * openssl enc -des-ede-cbc -K 6bb3851c3d50d495394877301ad78657 -iv 5c475e570c46cb47 -in blah
+     * | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[]
+            DES_Plaintext1_Encrypted_With_DES_112_KEY_And_DESEDE_CBC_PKCS5PADDING_With_DES_IV1 =
+                    new byte[] {
+            (byte) 0x09, (byte) 0xA5, (byte) 0x5D, (byte) 0x94, (byte) 0x94, (byte) 0xAA,
+            (byte) 0x3F, (byte) 0xC8, (byte) 0xB7, (byte) 0x73, (byte) 0x94, (byte) 0x0E,
+            (byte) 0xFC, (byte) 0xF4, (byte) 0xA5, (byte) 0x28,
+    };
+
+
+    /*
+     * Test vector generation:
+     * openssl enc -des-ede3-cbc -K fed4d7c98a136aa85ab819b8cf3c5fe0a2f77b6543c0c4e1
+     *     -iv 5c475e570c46cb47 -in blah | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[]
+            DES_Plaintext1_Encrypted_With_DES_168_KEY_And_DESEDE_CBC_PKCS5PADDING_With_DES_IV1 =
+                    new byte[] {
+            (byte) 0xC9, (byte) 0xF1, (byte) 0x83, (byte) 0x1F, (byte) 0x24, (byte) 0x83,
+            (byte) 0x2C, (byte) 0x7B, (byte) 0x66, (byte) 0x66, (byte) 0x99, (byte) 0x98,
+            (byte) 0x27, (byte) 0xB0, (byte) 0xED, (byte) 0x47
+    };
+
+    /*
      * Test vector creation:
      * echo -n 'Hello, world!' | recode ../x1 | sed 's/0x/(byte) 0x/g'
      */
@@ -2932,6 +3008,8 @@
 
         public final byte[] key;
 
+        public final String keyAlgorithm;
+
         public final byte[] iv;
 
         public final byte[] aad;
@@ -2942,9 +3020,10 @@
 
         public final byte[] plaintextPadded;
 
-        public CipherTestParam(String transformation, byte[] key, byte[] iv, byte[] aad,
-                byte[] plaintext, byte[] plaintextPadded, byte[] ciphertext) {
+        public CipherTestParam(String transformation, String keyAlgorithm, byte[] key, byte[] iv,
+                byte[] aad, byte[] plaintext, byte[] plaintextPadded, byte[] ciphertext) {
             this.transformation = transformation.toUpperCase(Locale.ROOT);
+            this.keyAlgorithm = keyAlgorithm;
             this.key = key;
             this.iv = iv;
             this.aad = aad;
@@ -2954,22 +3033,47 @@
         }
     }
 
+    private static List<CipherTestParam> DES_CIPHER_TEST_PARAMS = new ArrayList<CipherTestParam>();
+    static {
+        DES_CIPHER_TEST_PARAMS.add(new CipherTestParam(
+                "DESede/CBC/PKCS5Padding",
+                "DESede",
+                DES_112_KEY,
+                DES_IV1,
+                null,
+                DES_Plaintext1,
+                DES_Plaintext1_PKCS5_Padded,
+                DES_Plaintext1_Encrypted_With_DES_112_KEY_And_DESEDE_CBC_PKCS5PADDING_With_DES_IV1
+                ));
+        DES_CIPHER_TEST_PARAMS.add(new CipherTestParam(
+                "DESede/CBC/PKCS5Padding",
+                "DESede",
+                DES_168_KEY,
+                DES_IV1,
+                null,
+                DES_Plaintext1,
+                DES_Plaintext1_PKCS5_Padded,
+                DES_Plaintext1_Encrypted_With_DES_168_KEY_And_DESEDE_CBC_PKCS5PADDING_With_DES_IV1
+                ));
+    }
+
     private static List<CipherTestParam> CIPHER_TEST_PARAMS = new ArrayList<CipherTestParam>();
     static {
-        CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB/PKCS5Padding", AES_128_KEY,
+        CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB/PKCS5Padding", "AES", AES_128_KEY,
                 null,
                 null,
                 AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext,
                 AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded,
                 AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted));
         // PKCS#5 is assumed to be equivalent to PKCS#7 -- same test vectors are thus used for both.
-        CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB/PKCS7Padding", AES_128_KEY,
+        CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB/PKCS7Padding", "AES", AES_128_KEY,
                 null,
                 null,
                 AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext,
                 AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded,
                 AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted));
         CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/GCM/NOPADDING",
+                "AES",
                 AES_128_GCM_TestVector_1_Key,
                 AES_128_GCM_TestVector_1_IV,
                 AES_128_GCM_TestVector_1_AAD,
@@ -2977,19 +3081,19 @@
                 AES_128_GCM_TestVector_1_Plaintext,
                 AES_128_GCM_TestVector_1_Encrypted));
         if (IS_UNLIMITED) {
-            CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CTR/NoPadding", AES_192_KEY,
+            CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CTR/NoPadding", "AES", AES_192_KEY,
                     AES_192_CTR_NoPadding_TestVector_1_IV,
                     null,
                     AES_192_CTR_NoPadding_TestVector_1_Plaintext,
                     AES_192_CTR_NoPadding_TestVector_1_Plaintext,
                     AES_192_CTR_NoPadding_TestVector_1_Ciphertext));
-            CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC/PKCS5Padding", AES_256_KEY,
+            CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC/PKCS5Padding", "AES", AES_256_KEY,
                     AES_256_CBC_PKCS5Padding_TestVector_1_IV,
                     null,
                     AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext,
                     AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext_Padded,
                     AES_256_CBC_PKCS5Padding_TestVector_1_Ciphertext));
-            CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC/PKCS7Padding", AES_256_KEY,
+            CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC/PKCS7Padding", "AES", AES_256_KEY,
                     AES_256_CBC_PKCS5Padding_TestVector_1_IV,
                     null,
                     AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext,
@@ -3002,6 +3106,41 @@
         for (String provider : AES_PROVIDERS) {
             testCipher_Success(provider);
         }
+
+        testCipher_Success_ForAllSupportingProviders_AtLeastOneProviderRequired(
+                DES_CIPHER_TEST_PARAMS);
+    }
+
+    /**
+     * For each test vector in the list, tests that the transformation is supported by at least one
+     * provider and that all implementations of the transformation pass the Known Answer Test (KAT)
+     * as well as other functional tests.
+     */
+    private void testCipher_Success_ForAllSupportingProviders_AtLeastOneProviderRequired(
+            List<CipherTestParam> testVectors) throws Exception {
+        ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
+        PrintStream out = new PrintStream(errBuffer);
+        for (CipherTestParam testVector : testVectors) {
+            Provider[] providers = Security.getProviders("Cipher." + testVector.transformation);
+            if ((providers == null) || (providers.length == 0)) {
+                out.append("No providers offer " + testVector.transformation + "\n");
+                continue;
+            }
+            for (Provider provider : providers) {
+                try {
+                    checkCipher(testVector, provider.getName());
+                } catch (Throwable e) {
+                    out.append("Error encountered checking " + testVector.transformation
+                            + ", keySize=" + (testVector.key.length * 8) + " with provider "
+                            + provider.getName() + "\n");
+                    e.printStackTrace(out);
+                }
+            }
+        }
+        out.flush();
+        if (errBuffer.size() > 0) {
+            throw new Exception("Errors encountered:\n\n" + errBuffer.toString() + "\n\n");
+        }
     }
 
     private void testCipher_Success(String provider) throws Exception {
@@ -3025,7 +3164,7 @@
     }
 
     private void checkCipher(CipherTestParam p, String provider) throws Exception {
-        SecretKey key = new SecretKeySpec(p.key, "AES");
+        SecretKey key = new SecretKeySpec(p.key, p.keyAlgorithm);
         Cipher c = Cipher.getInstance(p.transformation, provider);
 
         AlgorithmParameterSpec spec = null;
diff --git a/luni/src/test/java/libcore/javax/crypto/MacTest.java b/luni/src/test/java/libcore/javax/crypto/MacTest.java
index 314a564..b308f02 100644
--- a/luni/src/test/java/libcore/javax/crypto/MacTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/MacTest.java
@@ -59,4 +59,14 @@
             Security.removeProvider(mockProvider.getName());
         }
     }
+
+    /**
+     * Aliases used to be wrong due to a typo.
+     * http://b/31114355
+     */
+    public void testMac_correctAlias() throws Exception {
+        Provider androidOpenSSLProvider = Security.getProvider("AndroidOpenSSL");
+        assertEquals("HmacSHA224", androidOpenSSLProvider.get("Alg.Alias.Mac.1.2.840.113549.2.8"));
+        assertEquals("HmacSHA256", androidOpenSSLProvider.get("Alg.Alias.Mac.1.2.840.113549.2.9"));
+    }
 }
diff --git a/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java b/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java
index 4f5d658..5471b1f 100644
--- a/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java
+++ b/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java
@@ -120,6 +120,30 @@
         expectExceptionInDNConstructor("l=\\g0");
     }
 
+    public void testNegativeLen() {
+        try {
+            X500Principal p = new X500Principal(new byte[]{
+                    0x30, // DerValue.tag_Sequence read in DerValue#getSequence
+                    9,    // Length of the vector. read in readVector.
+                          // DerInputStream.getLength will just return this as 10 & 0x80 == 0
+                    -1,   // Tag of the first value in the sequencevalue. Convenient so that it
+                          // doesn't hold DerIndefLenConverter.isEOC()
+                    (byte) 0x80, // Encoding in indefinite form
+                    -1,          // Second tag to be read by DerIndefLenConverter
+                    (byte) 0x84, // Second length byte to be read, 0x80 means long form, 4 bytes
+                    (byte) 0xff, // Length to be read by DerIndefLenConverter, -6, will move the
+                                 // buffer position to the second tag
+                    (byte) 0xff,
+                    (byte) 0xff,
+                    (byte) -6,
+                    0,           // Needed as otherwise it's detected that there's nothing after
+                    // the length
+            });
+            fail("expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
     private void expectExceptionInDNConstructor(String dn) {
         try {
             X500Principal principal = new X500Principal(dn);
diff --git a/luni/src/test/java/libcore/net/MimeUtilsTest.java b/luni/src/test/java/libcore/net/MimeUtilsTest.java
index ff22632..8b34ea2 100644
--- a/luni/src/test/java/libcore/net/MimeUtilsTest.java
+++ b/luni/src/test/java/libcore/net/MimeUtilsTest.java
@@ -18,8 +18,6 @@
 
 import junit.framework.TestCase;
 
-import libcore.net.MimeUtils;
-
 public class MimeUtilsTest extends TestCase {
   public void test_15715370() {
     assertEquals("audio/flac", MimeUtils.guessMimeTypeFromExtension("flac"));
@@ -52,4 +50,11 @@
   public void test_18390752() {
     assertEquals("jpg", MimeUtils.guessExtensionFromMimeType("image/jpeg"));
   }
+
+  public void test_30793548() {
+    assertEquals("video/3gpp", MimeUtils.guessMimeTypeFromExtension("3gpp"));
+    assertEquals("video/3gpp", MimeUtils.guessMimeTypeFromExtension("3gp"));
+    assertEquals("video/3gpp2", MimeUtils.guessMimeTypeFromExtension("3gpp2"));
+    assertEquals("video/3gpp2", MimeUtils.guessMimeTypeFromExtension("3g2"));
+  }
 }
diff --git a/ojluni/src/main/java/java/net/Socket.java b/ojluni/src/main/java/java/net/Socket.java
index 2aa057f..96a7355 100755
--- a/ojluni/src/main/java/java/net/Socket.java
+++ b/ojluni/src/main/java/java/net/Socket.java
@@ -207,9 +207,7 @@
     public Socket(String host, int port)
         throws UnknownHostException, IOException
     {
-        this(host != null ? new InetSocketAddress(host, port) :
-             new InetSocketAddress(InetAddress.getByName(null), port),
-             (SocketAddress) null, true);
+        this(InetAddress.getAllByName(host), port, (SocketAddress) null, true);
     }
 
     /**
@@ -240,8 +238,7 @@
      * @see        SecurityManager#checkConnect
      */
     public Socket(InetAddress address, int port) throws IOException {
-        this(address != null ? new InetSocketAddress(address, port) : null,
-             (SocketAddress) null, true);
+        this(nonNullAddress(address), port, (SocketAddress) null, true);
     }
 
     /**
@@ -279,8 +276,7 @@
      */
     public Socket(String host, int port, InetAddress localAddr,
                   int localPort) throws IOException {
-        this(host != null ? new InetSocketAddress(host, port) :
-               new InetSocketAddress(InetAddress.getByName(null), port),
+        this(InetAddress.getAllByName(host), port,
              new InetSocketAddress(localAddr, localPort), true);
     }
 
@@ -318,7 +314,7 @@
      */
     public Socket(InetAddress address, int port, InetAddress localAddr,
                   int localPort) throws IOException {
-        this(address != null ? new InetSocketAddress(address, port) : null,
+        this(nonNullAddress(address), port,
              new InetSocketAddress(localAddr, localPort), true);
     }
 
@@ -364,9 +360,7 @@
      */
     @Deprecated
     public Socket(String host, int port, boolean stream) throws IOException {
-        this(host != null ? new InetSocketAddress(host, port) :
-               new InetSocketAddress(InetAddress.getByName(null), port),
-             (SocketAddress) null, stream);
+        this(InetAddress.getAllByName(host), port, (SocketAddress) null, stream);
     }
 
     /**
@@ -407,32 +401,57 @@
      */
     @Deprecated
     public Socket(InetAddress host, int port, boolean stream) throws IOException {
-        this(host != null ? new InetSocketAddress(host, port) : null,
-             new InetSocketAddress(0), stream);
+        this(nonNullAddress(host), port, new InetSocketAddress(0), stream);
     }
 
-    private Socket(SocketAddress address, SocketAddress localAddr,
-                   boolean stream) throws IOException {
-        setImpl();
-
+    private static InetAddress[] nonNullAddress(InetAddress address) {
         // backward compatibility
         if (address == null)
             throw new NullPointerException();
 
-        try {
-            createImpl(stream);
-            if (localAddr != null)
-                bind(localAddr);
-            if (address != null)
+        return new InetAddress[] { address };
+    }
+
+    // Android-changed: Socket ctor should try all addresses
+    // b/30007735
+    private Socket(InetAddress[] addresses, int port, SocketAddress localAddr,
+            boolean stream) throws IOException {
+        if (addresses == null || addresses.length == 0) {
+            throw new SocketException("Impossible: empty address list");
+        }
+
+        for (int i = 0; i < addresses.length; i++) {
+            setImpl();
+            try {
+                InetSocketAddress address = new InetSocketAddress(addresses[i], port);
+                createImpl(stream);
+                if (localAddr != null) {
+                    bind(localAddr);
+                }
                 connect(address);
-        } catch (IOException e) {
-            // Do not call #close, classes that extend this class may do not expect a call
-            // to #close coming from the superclass constructor.
-            if (impl != null) {
-                impl.close();
+                break;
+            } catch (IOException | IllegalArgumentException | SecurityException e) {
+                try {
+                    // Android-changed:
+                    // Do not call #close, classes that extend this class may do not expect a call
+                    // to #close coming from the superclass constructor.
+                    impl.close();
+                    closed = true;
+                } catch (IOException ce) {
+                    e.addSuppressed(ce);
+                }
+
+                // Only stop on the last address.
+                if (i == addresses.length - 1) {
+                    throw e;
+                }
             }
-            closed = true;
-            throw e;
+
+            // Discard the connection state and try again.
+            impl = null;
+            created = false;
+            bound = false;
+            closed = false;
         }
     }
 
diff --git a/ojluni/src/main/java/java/nio/charset/Charset.java b/ojluni/src/main/java/java/nio/charset/Charset.java
index f85b82b..17e1160 100755
--- a/ojluni/src/main/java/java/nio/charset/Charset.java
+++ b/ojluni/src/main/java/java/nio/charset/Charset.java
@@ -214,10 +214,7 @@
  * input sequence are not omitted since the same code is used to represent
  * <small>ZERO-WIDTH NON-BREAKING SPACE</small>.
  *
- * <p> Every instance of the Java virtual machine has a default charset, which
- * may or may not be one of the standard charsets.  The default charset is
- * determined during virtual-machine startup and typically depends upon the
- * locale and charset being used by the underlying operating system. </p>
+ * <p>Android note: The Android platform default is always UTF-8.
  *
  * <p>The {@link StandardCharsets} class defines constants for each of the
  * standard charsets.
@@ -653,9 +650,7 @@
     /**
      * Returns the default charset of this Java virtual machine.
      *
-     * <p> The default charset is determined during virtual-machine startup and
-     * typically depends upon the locale and charset of the underlying
-     * operating system.
+     * <p>Android note: The Android platform default is always UTF-8.
      *
      * @return  A charset object for the default charset
      *
diff --git a/ojluni/src/main/java/java/util/Vector.java b/ojluni/src/main/java/java/util/Vector.java
index b2bc200..0f2dedd 100755
--- a/ojluni/src/main/java/java/util/Vector.java
+++ b/ojluni/src/main/java/java/util/Vector.java
@@ -1238,6 +1238,7 @@
                 checkForComodification();
                 Vector.this.add(i, e);
                 expectedModCount = modCount;
+                limit++;
             }
             cursor = i + 1;
             lastRet = -1;
diff --git a/ojluni/src/main/java/java/util/zip/ZipFile.java b/ojluni/src/main/java/java/util/zip/ZipFile.java
index e6624af..9ff661a 100755
--- a/ojluni/src/main/java/java/util/zip/ZipFile.java
+++ b/ojluni/src/main/java/java/util/zip/ZipFile.java
@@ -771,6 +771,14 @@
         return locsig;
     }
 
+    /** @hide */
+    // @VisibleForTesting
+    public int getFileDescriptor() {
+        return getFileDescriptor(jzfile);
+    }
+
+    private static native int getFileDescriptor(long jzfile);
+
     private static native long open(String name, int mode, long lastModified,
                                     boolean usemmap) throws IOException;
     private static native int getTotal(long jzfile);
diff --git a/ojluni/src/main/java/sun/security/util/DerIndefLenConverter.java b/ojluni/src/main/java/sun/security/util/DerIndefLenConverter.java
index 78d9e30..cbd5ecc 100755
--- a/ojluni/src/main/java/sun/security/util/DerIndefLenConverter.java
+++ b/ojluni/src/main/java/sun/security/util/DerIndefLenConverter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2012, 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
@@ -156,12 +156,18 @@
         }
         if (isLongForm(lenByte)) {
             lenByte &= LEN_MASK;
-            if (lenByte > 4)
+            if (lenByte > 4) {
                 throw new IOException("Too much data");
-            if ((dataSize - dataPos) < (lenByte + 1))
+            }
+            if ((dataSize - dataPos) < (lenByte + 1)) {
                 throw new IOException("Too little data");
-            for (int i = 0; i < lenByte; i++)
+            }
+            for (int i = 0; i < lenByte; i++) {
                 curLen = (curLen << 8) + (data[dataPos++] & 0xff);
+            }
+            if (curLen < 0) {
+                throw new IOException("Invalid length bytes");
+            }
         } else {
            curLen = (lenByte & LEN_MASK);
         }
@@ -188,10 +194,15 @@
         }
         if (isLongForm(lenByte)) {
             lenByte &= LEN_MASK;
-            for (int i = 0; i < lenByte; i++)
+            for (int i = 0; i < lenByte; i++) {
                 curLen = (curLen << 8) + (data[dataPos++] & 0xff);
-        } else
+            }
+            if (curLen < 0) {
+                throw new IOException("Invalid length bytes");
+            }
+        } else {
             curLen = (lenByte & LEN_MASK);
+        }
         writeLength(curLen);
         writeValue(curLen);
     }
diff --git a/ojluni/src/main/java/sun/security/util/DerInputStream.java b/ojluni/src/main/java/sun/security/util/DerInputStream.java
index 8f51439..dae8afd 100755
--- a/ojluni/src/main/java/sun/security/util/DerInputStream.java
+++ b/ojluni/src/main/java/sun/security/util/DerInputStream.java
@@ -604,6 +604,10 @@
                 value <<= 8;
                 value += 0x0ff & in.read();
             }
+            if (value < 0) {
+                throw new IOException("DerInputStream.getLength(): "
+                        + "Invalid length bytes");
+            }
         }
         return value;
     }
diff --git a/ojluni/src/main/native/java_util_zip_ZipFile.c b/ojluni/src/main/native/java_util_zip_ZipFile.c
index 7280d97..5353ff4 100644
--- a/ojluni/src/main/native/java_util_zip_ZipFile.c
+++ b/ojluni/src/main/native/java_util_zip_ZipFile.c
@@ -156,6 +156,12 @@
     ZIP_Close(jlong_to_ptr(zfile));
 }
 
+JNIEXPORT jint JNICALL
+ZipFile_getFileDescriptor(JNIEnv *env, jclass cls, jlong zfile) {
+    jzfile *zip = jlong_to_ptr(zfile);
+    return zip->zfd;
+}
+
 JNIEXPORT jlong JNICALL
 ZipFile_getEntry(JNIEnv *env, jclass cls, jlong zfile,
                  jbyteArray name, jboolean addSlash)
@@ -390,6 +396,7 @@
 }
 
 static JNINativeMethod gMethods[] = {
+  NATIVE_METHOD(ZipFile, getFileDescriptor, "(J)I"),
   NATIVE_METHOD(ZipFile, getEntry, "(J[BZ)J"),
   NATIVE_METHOD(ZipFile, freeEntry, "(JJ)V"),
   NATIVE_METHOD(ZipFile, getNextEntry, "(JI)J"),
diff --git a/ojluni/src/main/native/zip_util.c b/ojluni/src/main/native/zip_util.c
index 66ce7e5..5a2a0b8 100644
--- a/ojluni/src/main/native/zip_util.c
+++ b/ojluni/src/main/native/zip_util.c
@@ -47,8 +47,6 @@
 #include "zip_util.h"
 #include <zlib.h>
 
-#include "cutils/log.h"
-
 #ifdef _ALLBSD_SOURCE
 #define off64_t off_t
 #define mmap64 mmap
@@ -144,7 +142,7 @@
 }
 
 static int
-ZFILE_read(ZFILE zfd, char *buf, jint nbytes) {
+ZFILE_read(ZFILE zfd, char *buf, jint nbytes, jlong offset) {
 #ifdef WIN32
     return (int) IO_Read(zfd, buf, nbytes);
 #else
@@ -156,7 +154,7 @@
      * JVM_IO_INTR is tricky and could cause undesired side effect. So we decided
      * to simply call "read" on Solaris/Linux. See details in bug 6304463.
      */
-    return read(zfd, buf, nbytes);
+    return pread(zfd, buf, nbytes, offset);
 #endif
 }
 
@@ -185,11 +183,11 @@
 }
 
 /*
- * Reads len bytes of data into buf.
+ * Reads len bytes of data from the specified offset into buf.
  * Returns 0 if all bytes could be read, otherwise returns -1.
  */
 static int
-readFully(ZFILE zfd, void *buf, jlong len) {
+readFullyAt(ZFILE zfd, void *buf, jlong len, jlong offset) {
   char *bp = (char *) buf;
 
   while (len > 0) {
@@ -197,9 +195,10 @@
         jint count = (len < limit) ?
             (jint) len :
             (jint) limit;
-        jint n = ZFILE_read(zfd, bp, count);
+        jint n = ZFILE_read(zfd, bp, count, offset);
         if (n > 0) {
             bp += n;
+            offset += n;
             len -= n;
         } else if (n == JVM_IO_ERR && errno == EINTR) {
           /* Retry after EINTR (interrupted by signal).
@@ -212,19 +211,6 @@
     return 0;
 }
 
-/*
- * Reads len bytes of data from the specified offset into buf.
- * Returns 0 if all bytes could be read, otherwise returns -1.
- */
-static int
-readFullyAt(ZFILE zfd, void *buf, jlong len, jlong offset)
-{
-    if (IO_Lseek(zfd, offset, SEEK_SET) == -1) {
-        return -1; /* lseek failure. */
-    }
-
-    return readFully(zfd, buf, len);
-}
 
 /*
  * Allocates a new zip file object for the specified file name.
@@ -879,14 +865,17 @@
         return NULL;
     }
 
-    // Assumption, zfd refers to start of file. Trivially, reuse errbuf.
-    if (readFully(zfd, errbuf, 4) != -1) {  // errors will be handled later
+    // Trivially, reuse errbuf.
+    if (readFullyAt(zfd, errbuf, 4, 0 /* offset */) != -1) {  // errors will be handled later
         if (GETSIG(errbuf) == LOCSIG)
             zip->locsig = JNI_TRUE;
         else
             zip->locsig = JNI_FALSE;
     }
 
+    // This lseek is safe because it happens during construction of the ZipFile
+    // object. We must take care not to perform any operations that change the
+    // offset after (see b/30407219).
     len = zip->len = IO_Lseek(zfd, 0, SEEK_END);
     if (len <= 0) {
         if (len == 0) { /* zip file is empty */
@@ -986,7 +975,7 @@
     censize = CENSIZE(cen);
     if (censize <= bufsize) return cen;
     if ((cen = realloc(cen, censize)) == NULL)              goto Catch;
-    if (readFully(zfd, cen+bufsize, censize-bufsize) == -1) goto Catch;
+    if (readFullyAt(zfd, cen+bufsize, censize-bufsize, cenpos + bufsize) == -1) goto Catch;
     return cen;
 
  Catch:
@@ -1287,40 +1276,6 @@
     MUNLOCK(zip->lock);
 }
 
-// Temporary debugging information for b/30529561. If we encounter
-// a zip file with a bad LOC header, we log :
-//
-// - the file data at the offset.
-// - the offset itself.
-// - the first PATH_MAX bytes of the path associated with the fd.
-//
-// TODO(narayan): Remove this once b/30529561 is resolved.
-void ZIP_PrintDebugInfo(jzfile *zip, jzentry *entry,
-                        const unsigned char *loc)
-{
-  ALOGE("b/30529561: Unexpected LOC header: %x", *((unsigned int*) loc));
-  ALOGE("b/30529561: Entry offset: %zd", (size_t) entry->pos);
-
-  char path[64];
-  snprintf(path, sizeof(path), "/proc/self/fd/%d", zip->zfd);
-
-  char buf[PATH_MAX];
-  ssize_t count = readlink(path, buf, PATH_MAX);
-
-  if (count == -1) {
-    ALOGE("b/30529561: readlink failed for %s (%s)", path, strerror(errno));
-    return;
-  } else if (count == PATH_MAX) {
-    // Truncate the name, we're just using this for debugging purposes
-    // anyway.
-    --count;
-  }
-
-  buf[count] = '\0';
-
-  ALOGE("b/30529561: Reading zip file %s", buf);
-}
-
 /*
  * Returns the offset of the entry data within the zip file.
  * Returns -1 if an error occurred, in which case zip->msg will
@@ -1345,10 +1300,6 @@
             return -1;
         }
         if (GETSIG(loc) != LOCSIG) {
-            // TODO(narayan): Remove this debug message once b/30529561 is
-            // sorted out satisfactorily.
-            ZIP_PrintDebugInfo(zip, entry, loc);
-
             zip->msg = "invalid LOC header (bad signature)";
             return -1;
         }