Merge "Expose rethrow* methods in the public SDK API"
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
index 8d32931..486ee90 100644
--- a/dalvik/src/main/java/dalvik/system/DexFile.java
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -110,8 +110,9 @@
         //System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName);
     }
 
-    DexFile(ByteBuffer[] bufs) throws IOException {
-        mCookie = openInMemoryDexFiles(bufs);
+    DexFile(ByteBuffer[] bufs, ClassLoader loader, DexPathList.Element[] elements)
+            throws IOException {
+        mCookie = openInMemoryDexFiles(bufs, loader, elements);
         mInternalCookie = mCookie;
         mFileName = null;
     }
@@ -370,7 +371,8 @@
                                  elements);
     }
 
-    private static Object openInMemoryDexFiles(ByteBuffer[] bufs) throws IOException {
+    private static Object openInMemoryDexFiles(ByteBuffer[] bufs, ClassLoader loader,
+            DexPathList.Element[] elements) throws IOException {
         // Preprocess the ByteBuffers for openInMemoryDexFilesNative. We extract
         // the backing array (non-direct buffers only) and start/end positions
         // so that the native method does not have to call Java methods anymore.
@@ -382,11 +384,27 @@
             starts[i] = bufs[i].position();
             ends[i] = bufs[i].limit();
         }
-        return openInMemoryDexFilesNative(bufs, arrays, starts, ends);
+        return openInMemoryDexFilesNative(bufs, arrays, starts, ends, loader, elements);
     }
 
     private static native Object openInMemoryDexFilesNative(ByteBuffer[] bufs, byte[][] arrays,
-            int[] starts, int[] ends);
+            int[] starts, int[] ends, ClassLoader loader, DexPathList.Element[] elements);
+
+    /*
+     * Initiates background verification of this DexFile. This is a sepearate down-call
+     * from openDexFile and openInMemoryDexFiles because it requires the class loader's
+     * DexPathList to have been initialized for its classes to be resolvable by ART.
+     * DexPathList will open the dex files first, finalize `dexElements` and then call this.
+     */
+    /*package*/ void verifyInBackground(ClassLoader classLoader, String classLoaderContext) {
+        verifyInBackgroundNative(mCookie, classLoader, classLoaderContext);
+    }
+
+    private static native void verifyInBackgroundNative(Object mCookie, ClassLoader classLoader,
+            String classLoaderContext);
+
+    /*package*/ static native String getClassLoaderContext(ClassLoader classLoader,
+            DexPathList.Element[] elements);
 
     /*
      * Returns true if the dex file is backed by a valid oat file.
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
index fc57dc0..c63bb13 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -266,8 +266,24 @@
 
         try {
             Element[] null_elements = null;
-            DexFile dex = new DexFile(dexFiles);
+            DexFile dex = new DexFile(dexFiles, definingContext, null_elements);
+            // Capture class loader context from *before* `dexElements` is set (see comment below).
+            String classLoaderContext = dex.isBackedByOatFile()
+                    ? null : DexFile.getClassLoaderContext(definingContext, null_elements);
             dexElements = new Element[] { new Element(dex) };
+            // Spawn background thread to verify all classes and cache verification results.
+            // Must be called *after* `dexElements` has been initialized for ART to find
+            // its classes (the field is hardcoded in ART and dex files iterated over in
+            // the order of the array), but with class loader context from *before*
+            // `dexElements` was set because that is what it will be compared against next
+            // time the same bytecode is loaded.
+            // We only spawn the background thread if the bytecode is not backed by an oat
+            // file, i.e. this is the first time this bytecode is being loaded and/or
+            // verification results have not been cached yet. Skip spawning the thread on
+            // all subsequent loads of the same bytecode in the same class loader context.
+            if (classLoaderContext != null) {
+                dex.verifyInBackground(definingContext, classLoaderContext);
+            }
         } catch (IOException suppressed) {
             System.logE("Unable to load dex files", suppressed);
             suppressedExceptions.add(suppressed);
@@ -330,7 +346,8 @@
         int elementPos = 0;
         for (ByteBuffer buf : dexFiles) {
             try {
-                DexFile dex = new DexFile(new ByteBuffer[] { buf });
+                DexFile dex = new DexFile(new ByteBuffer[] { buf }, /* classLoader */ null,
+                        /* dexElements */ null);
                 elements[elementPos++] = new Element(dex);
             } catch (IOException suppressed) {
                 System.logE("Unable to load dex file: " + buf, suppressed);
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index e3e36aa..5dd1d1e 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -1471,7 +1471,7 @@
   bug: 19764047,
   result: EXEC_FAILED,
   names: [
-    "libcore.libcore.io.OsTest#test_PacketSocketAddress"
+    "libcore.android.system.OsTest#test_PacketSocketAddress"
   ]
 },
 {
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
index 0260e68..7d70680 100644
--- a/libart/src/main/java/dalvik/system/VMRuntime.java
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -696,4 +696,10 @@
      */
     @libcore.api.CorePlatformApi
     public static native void setProcessPackageName(String packageName);
+
+    /**
+     * Sets the full path to data directory of the app running in this process.
+     */
+    @libcore.api.CorePlatformApi
+    public static native void setProcessDataDirectory(String dataDir);
 }
diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java
index 8c96524..ded3b1c 100644
--- a/luni/src/main/java/android/system/Os.java
+++ b/luni/src/main/java/android/system/Os.java
@@ -40,10 +40,18 @@
 public final class Os {
     private Os() {}
 
+    // Ideally we'd just have the version that accepts SocketAddress but we're stuck with
+    // this one for legacy reasons. http://b/123568439
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/accept.2.html">accept(2)</a>.
      */
-    public static FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException { return Libcore.os.accept(fd, peerAddress); }
+    public static FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException { return accept(fd, (SocketAddress) peerAddress); }
+
+    /**
+     * See <a href="http://man7.org/linux/man-pages/man2/accept.2.html">accept(2)</a>.
+     * @hide
+     */
+    public static FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException { return Libcore.os.accept(fd, peerAddress); }
 
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/access.2.html">access(2)</a>.
diff --git a/luni/src/main/java/libcore/net/android.mime.types b/luni/src/main/java/libcore/net/android.mime.types
index 8a090fc..dd3b21a 100644
--- a/luni/src/main/java/libcore/net/android.mime.types
+++ b/luni/src/main/java/libcore/net/android.mime.types
@@ -90,4 +90,5 @@
 video/3gpp 3gpp!
 video/mpeg mpeg!
 video/quicktime mov!
+video/vnd.youtube.yt yt
 video/x-matroska mkv!
diff --git a/luni/src/main/java/libcore/util/HexEncoding.java b/luni/src/main/java/libcore/util/HexEncoding.java
index eceec6b..f5eed4f 100644
--- a/luni/src/main/java/libcore/util/HexEncoding.java
+++ b/luni/src/main/java/libcore/util/HexEncoding.java
@@ -23,17 +23,43 @@
 @libcore.api.CorePlatformApi
 public class HexEncoding {
 
+    private static final char[] LOWER_CASE_DIGITS = {
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+    };
+
+    private static final char[] UPPER_CASE_DIGITS = {
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+    };
+
     /** Hidden constructor to prevent instantiation. */
     private HexEncoding() {}
 
-    private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
+    /**
+     * Encodes the provided byte as a two-digit hexadecimal String value.
+     */
+    @libcore.api.CorePlatformApi
+    public static String encodeToString(byte b, boolean upperCase) {
+        char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS;
+        char[] buf = new char[2]; // We always want two digits.
+        buf[0] = digits[(b >> 4) & 0xf];
+        buf[1] = digits[b & 0xf];
+        return new String(buf, 0, 2);
+    }
 
     /**
      * Encodes the provided data as a sequence of hexadecimal characters.
      */
     @libcore.api.CorePlatformApi
     public static char[] encode(byte[] data) {
-        return encode(data, 0, data.length);
+        return encode(data, 0, data.length, true /* upperCase */);
+    }
+
+    /**
+     * Encodes the provided data as a sequence of hexadecimal characters.
+     */
+    @libcore.api.CorePlatformApi
+    public static char[] encode(byte[] data, boolean upperCase) {
+        return encode(data, 0, data.length, upperCase);
     }
 
     /**
@@ -41,12 +67,20 @@
      */
     @libcore.api.CorePlatformApi
     public static char[] encode(byte[] data, int offset, int len) {
+        return encode(data, offset, len, true /* upperCase */);
+    }
+
+    /**
+     * Encodes the provided data as a sequence of hexadecimal characters.
+     */
+    private static char[] encode(byte[] data, int offset, int len, boolean upperCase) {
+        char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS;
         char[] result = new char[len * 2];
         for (int i = 0; i < len; i++) {
             byte b = data[offset + i];
             int resultIndex = 2 * i;
-            result[resultIndex] = (HEX_DIGITS[(b >>> 4) & 0x0f]);
-            result[resultIndex + 1] = (HEX_DIGITS[b & 0x0f]);
+            result[resultIndex] = (digits[(b >> 4) & 0x0f]);
+            result[resultIndex + 1] = (digits[b & 0x0f]);
         }
 
         return result;
@@ -57,7 +91,15 @@
      */
     @libcore.api.CorePlatformApi
     public static String encodeToString(byte[] data) {
-        return new String(encode(data));
+        return encodeToString(data, true /* upperCase */);
+    }
+
+    /**
+     * Encodes the provided data as a sequence of hexadecimal characters.
+     */
+    @libcore.api.CorePlatformApi
+    public static String encodeToString(byte[] data, boolean upperCase) {
+        return new String(encode(data, upperCase));
     }
 
     /**
diff --git a/luni/src/main/java/libcore/util/ZoneInfo.java b/luni/src/main/java/libcore/util/ZoneInfo.java
index 65df611..abd98b1 100644
--- a/luni/src/main/java/libcore/util/ZoneInfo.java
+++ b/luni/src/main/java/libcore/util/ZoneInfo.java
@@ -37,8 +37,9 @@
  * Our concrete TimeZone implementation, backed by zoneinfo data.
  *
  * <p>This reads time zone information from a binary file stored on the platform. The binary file
- * is essentially a single file containing compacted versions of all the tzfile (see
- * {@code man 5 tzfile} for details of the source) and an index by long name, e.g. Europe/London.
+ * is essentially a single file containing compacted versions of all the tzfiles produced by the
+ * zone info compiler (zic) tool (see {@code man 5 tzfile} for details of the format and
+ * {@code man 8 zic}) and an index by long name, e.g. Europe/London.
  *
  * <p>The compacted form is created by {@code external/icu/tools/ZoneCompactor.java} and is used
  * by both this and Bionic. {@link ZoneInfoDB} is responsible for mapping the binary file, and
@@ -307,6 +308,8 @@
 
         // Use the latest non-daylight offset (if any) as the raw offset.
         if (mTransitions.length == 0) {
+            // This case is no longer expected to occur in the data used on Android after changes
+            // made in zic version 2014c. It is kept as a fallback.
             // If there are no transitions then use the first GMT offset.
             mRawOffset = gmtOffsets[0];
         } else {
diff --git a/luni/src/test/java/libcore/android/system/OsTest.java b/luni/src/test/java/libcore/android/system/OsTest.java
new file mode 100644
index 0000000..dd5e698
--- /dev/null
+++ b/luni/src/test/java/libcore/android/system/OsTest.java
@@ -0,0 +1,1418 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.android.system;
+
+import android.system.ErrnoException;
+import android.system.Int64Ref;
+import android.system.NetlinkSocketAddress;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.PacketSocketAddress;
+import android.system.StructRlimit;
+import android.system.StructStat;
+import android.system.StructTimeval;
+import android.system.StructUcred;
+import android.system.UnixSocketAddress;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.ServerSocket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.atomic.AtomicReference;
+
+import junit.framework.TestCase;
+
+import libcore.io.IoUtils;
+import libcore.testing.io.TestIoUtils;
+
+import static android.system.OsConstants.*;
+
+public class OsTest extends TestCase {
+
+    public void testIsSocket() throws Exception {
+        File f = new File("/dev/null");
+        FileInputStream fis = new FileInputStream(f);
+        assertFalse(S_ISSOCK(Os.fstat(fis.getFD()).st_mode));
+        fis.close();
+
+        ServerSocket s = new ServerSocket();
+        assertTrue(S_ISSOCK(Os.fstat(s.getImpl().getFD$()).st_mode));
+        s.close();
+    }
+
+    public void testFcntlInt() throws Exception {
+        File f = File.createTempFile("OsTest", "tst");
+        FileInputStream fis = null;
+        try {
+            fis = new FileInputStream(f);
+            Os.fcntlInt(fis.getFD(), F_SETFD, FD_CLOEXEC);
+            int flags = Os.fcntlVoid(fis.getFD(), F_GETFD);
+            assertTrue((flags & FD_CLOEXEC) != 0);
+        } finally {
+            TestIoUtils.closeQuietly(fis);
+            f.delete();
+        }
+    }
+
+    public void testUnixDomainSockets_in_file_system() throws Exception {
+        String path = System.getProperty("java.io.tmpdir") + "/test_unix_socket";
+        new File(path).delete();
+        checkUnixDomainSocket(UnixSocketAddress.createFileSystem(path), false);
+    }
+
+    public void testUnixDomainSocket_abstract_name() throws Exception {
+        // Linux treats a sun_path starting with a NUL byte as an abstract name. See unix(7).
+        checkUnixDomainSocket(UnixSocketAddress.createAbstract("/abstract_name_unix_socket"), true);
+    }
+
+    public void testUnixDomainSocket_unnamed() throws Exception {
+        final FileDescriptor fd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
+        // unix(7) says an unbound socket is unnamed.
+        checkNoSockName(fd);
+        Os.close(fd);
+    }
+
+    private void checkUnixDomainSocket(final UnixSocketAddress address, final boolean isAbstract)
+            throws Exception {
+        final FileDescriptor serverFd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
+        Os.bind(serverFd, address);
+        Os.listen(serverFd, 5);
+
+        checkSockName(serverFd, isAbstract, address);
+
+        Thread server = new Thread(new Runnable() {
+            public void run() {
+                try {
+                    UnixSocketAddress peerAddress = UnixSocketAddress.createUnnamed();
+                    FileDescriptor clientFd = Os.accept(serverFd, peerAddress);
+                    checkSockName(clientFd, isAbstract, address);
+                    checkNoName(peerAddress);
+
+                    checkNoPeerName(clientFd);
+
+                    StructUcred credentials = Os.getsockoptUcred(clientFd, SOL_SOCKET, SO_PEERCRED);
+                    assertEquals(Os.getpid(), credentials.pid);
+                    assertEquals(Os.getuid(), credentials.uid);
+                    assertEquals(Os.getgid(), credentials.gid);
+
+                    byte[] request = new byte[256];
+                    Os.read(clientFd, request, 0, request.length);
+
+                    String s = new String(request, "UTF-8");
+                    byte[] response = s.toUpperCase(Locale.ROOT).getBytes("UTF-8");
+                    Os.write(clientFd, response, 0, response.length);
+
+                    Os.close(clientFd);
+                } catch (Exception ex) {
+                    throw new RuntimeException(ex);
+                }
+            }
+        });
+        server.start();
+
+        FileDescriptor clientFd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
+
+        Os.connect(clientFd, address);
+        checkNoSockName(clientFd);
+
+        String string = "hello, world!";
+
+        byte[] request = string.getBytes("UTF-8");
+        assertEquals(request.length, Os.write(clientFd, request, 0, request.length));
+
+        byte[] response = new byte[request.length];
+        assertEquals(response.length, Os.read(clientFd, response, 0, response.length));
+
+        assertEquals(string.toUpperCase(Locale.ROOT), new String(response, "UTF-8"));
+
+        Os.close(clientFd);
+    }
+
+    private static void checkSockName(FileDescriptor fd, boolean isAbstract,
+            UnixSocketAddress address) throws Exception {
+        UnixSocketAddress isa = (UnixSocketAddress) Os.getsockname(fd);
+        assertEquals(address, isa);
+        if (isAbstract) {
+            assertEquals(0, isa.getSunPath()[0]);
+        }
+    }
+
+    private void checkNoName(UnixSocketAddress usa) {
+        assertEquals(0, usa.getSunPath().length);
+    }
+
+    private void checkNoPeerName(FileDescriptor fd) throws Exception {
+        checkNoName((UnixSocketAddress) Os.getpeername(fd));
+    }
+
+    private void checkNoSockName(FileDescriptor fd) throws Exception {
+        checkNoName((UnixSocketAddress) Os.getsockname(fd));
+    }
+
+    public void test_strsignal() throws Exception {
+        assertEquals("Killed", Os.strsignal(9));
+        assertEquals("Unknown signal -1", Os.strsignal(-1));
+    }
+
+    public void test_byteBufferPositions_write_pwrite() throws Exception {
+        FileOutputStream fos = new FileOutputStream(new File("/dev/null"));
+        FileDescriptor fd = fos.getFD();
+        final byte[] contents = new String("goodbye, cruel world")
+                .getBytes(StandardCharsets.US_ASCII);
+        ByteBuffer byteBuffer = ByteBuffer.wrap(contents);
+
+        byteBuffer.position(0);
+        int written = Os.write(fd, byteBuffer);
+        assertTrue(written > 0);
+        assertEquals(written, byteBuffer.position());
+
+        byteBuffer.position(4);
+        written = Os.write(fd, byteBuffer);
+        assertTrue(written > 0);
+        assertEquals(written + 4, byteBuffer.position());
+
+        byteBuffer.position(0);
+        written = Os.pwrite(fd, byteBuffer, 64 /* offset */);
+        assertTrue(written > 0);
+        assertEquals(written, byteBuffer.position());
+
+        byteBuffer.position(4);
+        written = Os.pwrite(fd, byteBuffer, 64 /* offset */);
+        assertTrue(written > 0);
+        assertEquals(written + 4, byteBuffer.position());
+
+        fos.close();
+    }
+
+    public void test_byteBufferPositions_read_pread() throws Exception {
+        FileInputStream fis = new FileInputStream(new File("/dev/zero"));
+        FileDescriptor fd = fis.getFD();
+        ByteBuffer byteBuffer = ByteBuffer.allocate(64);
+
+        byteBuffer.position(0);
+        int read = Os.read(fd, byteBuffer);
+        assertTrue(read > 0);
+        assertEquals(read, byteBuffer.position());
+
+        byteBuffer.position(4);
+        read = Os.read(fd, byteBuffer);
+        assertTrue(read > 0);
+        assertEquals(read + 4, byteBuffer.position());
+
+        byteBuffer.position(0);
+        read = Os.pread(fd, byteBuffer, 64 /* offset */);
+        assertTrue(read > 0);
+        assertEquals(read, byteBuffer.position());
+
+        byteBuffer.position(4);
+        read = Os.pread(fd, byteBuffer, 64 /* offset */);
+        assertTrue(read > 0);
+        assertEquals(read + 4, byteBuffer.position());
+
+        fis.close();
+    }
+
+    static void checkByteBufferPositions_sendto_recvfrom(
+            int family, InetAddress loopback) throws Exception {
+        final FileDescriptor serverFd = Os.socket(family, SOCK_STREAM, 0);
+        Os.bind(serverFd, loopback, 0);
+        Os.listen(serverFd, 5);
+
+        InetSocketAddress address = (InetSocketAddress) Os.getsockname(serverFd);
+
+        final Thread server = new Thread(new Runnable() {
+            public void run() {
+                try {
+                    InetSocketAddress peerAddress = new InetSocketAddress();
+                    FileDescriptor clientFd = Os.accept(serverFd, peerAddress);
+
+                    // Attempt to receive a maximum of 24 bytes from the client, and then
+                    // close the connection.
+                    ByteBuffer buffer = ByteBuffer.allocate(16);
+                    int received = Os.recvfrom(clientFd, buffer, 0, null);
+                    assertTrue(received > 0);
+                    assertEquals(received, buffer.position());
+
+                    ByteBuffer buffer2 = ByteBuffer.allocate(16);
+                    buffer2.position(8);
+                    received = Os.recvfrom(clientFd, buffer2, 0, null);
+                    assertTrue(received > 0);
+                    assertEquals(received + 8, buffer.position());
+
+                    Os.close(clientFd);
+                } catch (Exception ex) {
+                    throw new RuntimeException(ex);
+                }
+            }
+        });
+
+        server.start();
+
+        FileDescriptor clientFd = Os.socket(family, SOCK_STREAM, 0);
+        Os.connect(clientFd, address.getAddress(), address.getPort());
+
+        final byte[] bytes = "good bye, cruel black hole with fancy distortion"
+                .getBytes(StandardCharsets.US_ASCII);
+        assertTrue(bytes.length > 24);
+
+        ByteBuffer input = ByteBuffer.wrap(bytes);
+        input.position(0);
+        input.limit(16);
+
+        int sent = Os.sendto(clientFd, input, 0, address.getAddress(), address.getPort());
+        assertTrue(sent > 0);
+        assertEquals(sent, input.position());
+
+        input.position(16);
+        input.limit(24);
+        sent = Os.sendto(clientFd, input, 0, address.getAddress(), address.getPort());
+        assertTrue(sent > 0);
+        assertEquals(sent + 16, input.position());
+
+        Os.close(clientFd);
+    }
+
+    interface ExceptionalRunnable {
+
+        public void run() throws Exception;
+    }
+
+    /**
+     * Expects that the given Runnable will throw an exception of the specified class. If the class
+     * is ErrnoException, and expectedErrno is non-null, also checks that the errno is equal to
+     * expectedErrno.
+     */
+    private static void expectException(ExceptionalRunnable r, Class<? extends Exception> exClass,
+            Integer expectedErrno, String msg) {
+        try {
+            r.run();
+            fail(msg + " did not throw exception");
+        } catch (Exception e) {
+            assertEquals(msg + " threw unexpected exception", exClass, e.getClass());
+
+            if (expectedErrno != null) {
+                if (e instanceof ErrnoException) {
+                    assertEquals(msg + "threw ErrnoException with unexpected error number",
+                            (int) expectedErrno, ((ErrnoException) e).errno);
+                } else {
+                    fail("Can only pass expectedErrno when expecting ErrnoException");
+                }
+            }
+
+        }
+    }
+
+    private static void expectBindException(FileDescriptor socket, SocketAddress addr,
+            Class exClass, Integer expectedErrno) {
+        String msg = String.format("bind(%s, %s)", socket, addr);
+        expectException(() -> {
+            Os.bind(socket, addr);
+        }, exClass, expectedErrno, msg);
+    }
+
+    private static void expectConnectException(FileDescriptor socket, SocketAddress addr,
+            Class exClass, Integer expectedErrno) {
+        String msg = String.format("connect(%s, %s)", socket, addr);
+        expectException(() -> {
+            Os.connect(socket, addr);
+        }, exClass, expectedErrno, msg);
+    }
+
+    private static void expectSendtoException(FileDescriptor socket, SocketAddress addr,
+            Class exClass, Integer expectedErrno) {
+        String msg = String.format("sendto(%s, %s)", socket, addr);
+        byte[] packet = new byte[42];
+        expectException(() -> {
+                    Os.sendto(socket, packet, 0, packet.length, 0, addr);
+                },
+                exClass, expectedErrno, msg);
+    }
+
+    private static void expectBindConnectSendtoSuccess(FileDescriptor socket, String socketDesc,
+            SocketAddress addr) {
+        String msg = socketDesc + " socket to " + addr.toString();
+
+        try {
+            try {
+                // Expect that bind throws when any of its arguments are null.
+                expectBindException(null, addr, ErrnoException.class, EBADF);
+                expectBindException(socket, null, NullPointerException.class, null);
+                expectBindException(null, null, NullPointerException.class, null);
+
+                // Expect bind to succeed.
+                Os.bind(socket, addr);
+
+                // Find out which port we're actually bound to, and use that in subsequent connect()
+                // and send() calls. We can't send to addr because that has a port of 0.
+                if (addr instanceof InetSocketAddress) {
+                    InetSocketAddress addrISA = (InetSocketAddress) addr;
+                    InetSocketAddress socknameISA = (InetSocketAddress) Os.getsockname(socket);
+
+                    assertEquals(addrISA.getAddress(), socknameISA.getAddress());
+                    assertEquals(0, addrISA.getPort());
+                    assertFalse(0 == socknameISA.getPort());
+                    addr = socknameISA;
+                }
+
+                // Expect sendto with a null address to throw because the socket is not connected,
+                // but to succeed with a non-null address.
+                byte[] packet = new byte[42];
+                Os.sendto(socket, packet, 0, packet.length, 0, addr);
+                // UNIX and IP sockets return different errors for this operation, so we can't check
+                // errno.
+                expectSendtoException(socket, null, ErrnoException.class, null);
+                expectSendtoException(null, null, ErrnoException.class, EBADF);
+
+                // Expect that connect throws when any of its arguments are null.
+                expectConnectException(null, addr, ErrnoException.class, EBADF);
+                expectConnectException(socket, null, NullPointerException.class, null);
+                expectConnectException(null, null, NullPointerException.class, null);
+
+                // Expect connect to succeed.
+                Os.connect(socket, addr);
+                assertEquals(Os.getsockname(socket), Os.getpeername(socket));
+
+                // Expect sendto to succeed both when given an explicit address and a null address.
+                Os.sendto(socket, packet, 0, packet.length, 0, addr);
+                Os.sendto(socket, packet, 0, packet.length, 0, null);
+            } catch (SocketException | ErrnoException e) {
+                fail("Expected success for " + msg + ", but got: " + e);
+            }
+
+        } finally {
+            IoUtils.closeQuietly(socket);
+        }
+    }
+
+    private static void expectBindConnectSendtoErrno(int bindErrno, int connectErrno,
+            int sendtoErrno, FileDescriptor socket, String socketDesc, SocketAddress addr) {
+        try {
+
+            // Expect bind to fail with bindErrno.
+            String msg = "bind " + socketDesc + " socket to " + addr.toString();
+            try {
+                Os.bind(socket, addr);
+                fail("Expected to fail " + msg);
+            } catch (ErrnoException e) {
+                assertEquals("Expected errno " + bindErrno + " " + msg, bindErrno, e.errno);
+            } catch (SocketException e) {
+                fail("Unexpected SocketException " + msg);
+            }
+
+            // Expect connect to fail with connectErrno.
+            msg = "connect " + socketDesc + " socket to " + addr.toString();
+            try {
+                Os.connect(socket, addr);
+                fail("Expected to fail " + msg);
+            } catch (ErrnoException e) {
+                assertEquals("Expected errno " + connectErrno + " " + msg, connectErrno, e.errno);
+            } catch (SocketException e) {
+                fail("Unexpected SocketException " + msg);
+            }
+
+            // Expect sendto to fail with sendtoErrno.
+            byte[] packet = new byte[42];
+            msg = "sendto " + socketDesc + " socket to " + addr.toString();
+            try {
+                Os.sendto(socket, packet, 0, packet.length, 0, addr);
+                fail("Expected to fail " + msg);
+            } catch (ErrnoException e) {
+                assertEquals("Expected errno " + sendtoErrno + " " + msg, sendtoErrno, e.errno);
+            } catch (SocketException e) {
+                fail("Unexpected SocketException " + msg);
+            }
+
+        } finally {
+            // No matter what happened, close the socket.
+            IoUtils.closeQuietly(socket);
+        }
+    }
+
+    private FileDescriptor makeIpv4Socket() throws Exception {
+        return Os.socket(AF_INET, SOCK_DGRAM, 0);
+    }
+
+    private FileDescriptor makeIpv6Socket() throws Exception {
+        return Os.socket(AF_INET6, SOCK_DGRAM, 0);
+    }
+
+    private FileDescriptor makeUnixSocket() throws Exception {
+        return Os.socket(AF_UNIX, SOCK_DGRAM, 0);
+    }
+
+    public void testCrossFamilyBindConnectSendto() throws Exception {
+        SocketAddress addrIpv4 = new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 0);
+        SocketAddress addrIpv6 = new InetSocketAddress(InetAddress.getByName("::1"), 0);
+        SocketAddress addrUnix = UnixSocketAddress.createAbstract("/abstract_name_unix_socket");
+
+        expectBindConnectSendtoSuccess(makeIpv4Socket(), "ipv4", addrIpv4);
+        expectBindConnectSendtoErrno(EAFNOSUPPORT, EAFNOSUPPORT, EAFNOSUPPORT,
+                makeIpv4Socket(), "ipv4", addrIpv6);
+        expectBindConnectSendtoErrno(EAFNOSUPPORT, EAFNOSUPPORT, EAFNOSUPPORT,
+                makeIpv4Socket(), "ipv4", addrUnix);
+
+        // This succeeds because Java always uses dual-stack sockets and all InetAddress and
+        // InetSocketAddress objects represent IPv4 addresses using IPv4-mapped IPv6 addresses.
+        expectBindConnectSendtoSuccess(makeIpv6Socket(), "ipv6", addrIpv4);
+        expectBindConnectSendtoSuccess(makeIpv6Socket(), "ipv6", addrIpv6);
+        expectBindConnectSendtoErrno(EAFNOSUPPORT, EAFNOSUPPORT, EINVAL,
+                makeIpv6Socket(), "ipv6", addrUnix);
+
+        expectBindConnectSendtoErrno(EINVAL, EINVAL, EINVAL,
+                makeUnixSocket(), "unix", addrIpv4);
+        expectBindConnectSendtoErrno(EINVAL, EINVAL, EINVAL,
+                makeUnixSocket(), "unix", addrIpv6);
+        expectBindConnectSendtoSuccess(makeUnixSocket(), "unix", addrUnix);
+    }
+
+    public void testUnknownSocketAddressSubclass() throws Exception {
+        class MySocketAddress extends SocketAddress {
+
+        }
+        MySocketAddress myaddr = new MySocketAddress();
+
+        for (int family : new int[] { AF_INET, AF_INET6, AF_NETLINK }) {
+            FileDescriptor s = Os.socket(family, SOCK_DGRAM, 0);
+            try {
+
+                try {
+                    Os.bind(s, myaddr);
+                    fail("bind socket family " + family
+                            + " to unknown SocketAddress subclass succeeded");
+                } catch (UnsupportedOperationException expected) {
+                }
+
+                try {
+                    Os.connect(s, myaddr);
+                    fail("connect socket family " + family
+                            + " to unknown SocketAddress subclass succeeded");
+                } catch (UnsupportedOperationException expected) {
+                }
+
+                byte[] msg = new byte[42];
+                try {
+                    Os.sendto(s, msg, 0, msg.length, 0, myaddr);
+                    fail("sendto socket family " + family
+                            + " to unknown SocketAddress subclass succeeded");
+                } catch (UnsupportedOperationException expected) {
+                }
+
+            } finally {
+                Os.close(s);
+            }
+        }
+    }
+
+    public void test_NetlinkSocket() throws Exception {
+        FileDescriptor nlSocket = Os.socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+        Os.bind(nlSocket, new NetlinkSocketAddress());
+        NetlinkSocketAddress address = (NetlinkSocketAddress) Os.getsockname(nlSocket);
+        assertTrue(address.getPortId() > 0);
+        assertEquals(0, address.getGroupsMask());
+
+        NetlinkSocketAddress nlKernel = new NetlinkSocketAddress();
+        Os.connect(nlSocket, nlKernel);
+        NetlinkSocketAddress nlPeer = (NetlinkSocketAddress) Os.getpeername(nlSocket);
+        assertEquals(0, nlPeer.getPortId());
+        assertEquals(0, nlPeer.getGroupsMask());
+        Os.close(nlSocket);
+    }
+
+    public void test_PacketSocketAddress() throws Exception {
+        NetworkInterface lo = NetworkInterface.getByName("lo");
+        FileDescriptor fd = Os.socket(AF_PACKET, SOCK_DGRAM, ETH_P_IPV6);
+        PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6, lo.getIndex());
+        Os.bind(fd, addr);
+
+        PacketSocketAddress bound = (PacketSocketAddress) Os.getsockname(fd);
+        assertEquals((short) ETH_P_IPV6, bound.sll_protocol);  // ETH_P_IPV6 is an int.
+        assertEquals(lo.getIndex(), bound.sll_ifindex);
+        assertEquals(ARPHRD_LOOPBACK, bound.sll_hatype);
+        assertEquals(0, bound.sll_pkttype);
+
+        // The loopback address is ETH_ALEN bytes long and is all zeros.
+        // http://lxr.free-electrons.com/source/drivers/net/loopback.c?v=3.10#L167
+        assertEquals(6, bound.sll_addr.length);
+        for (int i = 0; i < 6; i++) {
+            assertEquals(0, bound.sll_addr[i]);
+        }
+    }
+
+    public void test_byteBufferPositions_sendto_recvfrom_af_inet() throws Exception {
+        checkByteBufferPositions_sendto_recvfrom(AF_INET, InetAddress.getByName("127.0.0.1"));
+    }
+
+    public void test_byteBufferPositions_sendto_recvfrom_af_inet6() throws Exception {
+        checkByteBufferPositions_sendto_recvfrom(AF_INET6, InetAddress.getByName("::1"));
+    }
+
+    private void checkSendToSocketAddress(int family, InetAddress loopback) throws Exception {
+        FileDescriptor recvFd = Os.socket(family, SOCK_DGRAM, 0);
+        Os.bind(recvFd, loopback, 0);
+        StructTimeval tv = StructTimeval.fromMillis(20);
+        Os.setsockoptTimeval(recvFd, SOL_SOCKET, SO_RCVTIMEO, tv);
+
+        InetSocketAddress to = ((InetSocketAddress) Os.getsockname(recvFd));
+        FileDescriptor sendFd = Os.socket(family, SOCK_DGRAM, 0);
+        byte[] msg = ("Hello, I'm going to a socket address: " + to.toString()).getBytes("UTF-8");
+        int len = msg.length;
+
+        assertEquals(len, Os.sendto(sendFd, msg, 0, len, 0, to));
+        byte[] received = new byte[msg.length + 42];
+        InetSocketAddress from = new InetSocketAddress();
+        assertEquals(len, Os.recvfrom(recvFd, received, 0, received.length, 0, from));
+        assertEquals(loopback, from.getAddress());
+    }
+
+    public void test_sendtoSocketAddress_af_inet() throws Exception {
+        checkSendToSocketAddress(AF_INET, InetAddress.getByName("127.0.0.1"));
+    }
+
+    public void test_sendtoSocketAddress_af_inet6() throws Exception {
+        checkSendToSocketAddress(AF_INET6, InetAddress.getByName("::1"));
+    }
+
+    public void test_socketFamilies() throws Exception {
+        FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+        Os.bind(fd, InetAddress.getByName("::"), 0);
+        InetSocketAddress localSocketAddress = (InetSocketAddress) Os.getsockname(fd);
+        assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
+
+        fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+        Os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
+        localSocketAddress = (InetSocketAddress) Os.getsockname(fd);
+        assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
+
+        fd = Os.socket(AF_INET, SOCK_STREAM, 0);
+        Os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
+        localSocketAddress = (InetSocketAddress) Os.getsockname(fd);
+        assertEquals(Inet4Address.ANY, localSocketAddress.getAddress());
+        try {
+            Os.bind(fd, InetAddress.getByName("::"), 0);
+            fail("Expected ErrnoException binding IPv4 socket to ::");
+        } catch (ErrnoException expected) {
+            assertEquals("Expected EAFNOSUPPORT binding IPv4 socket to ::", EAFNOSUPPORT,
+                    expected.errno);
+        }
+    }
+
+    private static void assertArrayEquals(byte[] expected, byte[] actual) {
+        assertTrue("Expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual),
+                Arrays.equals(expected, actual));
+    }
+
+    private static void checkSocketPing(FileDescriptor fd, InetAddress to, byte[] packet,
+            byte type, byte responseType, boolean useSendto) throws Exception {
+        int len = packet.length;
+        packet[0] = type;
+        if (useSendto) {
+            assertEquals(len, Os.sendto(fd, packet, 0, len, 0, to, 0));
+        } else {
+            Os.connect(fd, to, 0);
+            assertEquals(len, Os.sendto(fd, packet, 0, len, 0, null, 0));
+        }
+
+        int icmpId = ((InetSocketAddress) Os.getsockname(fd)).getPort();
+        byte[] received = new byte[4096];
+        InetSocketAddress srcAddress = new InetSocketAddress();
+        assertEquals(len, Os.recvfrom(fd, received, 0, received.length, 0, srcAddress));
+        assertEquals(to, srcAddress.getAddress());
+        assertEquals(responseType, received[0]);
+        assertEquals(received[4], (byte) (icmpId >> 8));
+        assertEquals(received[5], (byte) (icmpId & 0xff));
+
+        received = Arrays.copyOf(received, len);
+        received[0] = (byte) type;
+        received[2] = received[3] = 0;  // Checksum.
+        received[4] = received[5] = 0;  // ICMP ID.
+        assertArrayEquals(packet, received);
+    }
+
+    public void test_socketPing() throws Exception {
+        final byte ICMP_ECHO = 8, ICMP_ECHOREPLY = 0;
+        final byte ICMPV6_ECHO_REQUEST = (byte) 128, ICMPV6_ECHO_REPLY = (byte) 129;
+        final byte[] packet = ("\000\000\000\000" +  // ICMP type, code.
+                "\000\000\000\003" +  // ICMP ID (== port), sequence number.
+                "Hello myself").getBytes(StandardCharsets.US_ASCII);
+
+        FileDescriptor fd = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
+        InetAddress ipv6Loopback = InetAddress.getByName("::1");
+        checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, true);
+        checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, false);
+
+        fd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+        InetAddress ipv4Loopback = InetAddress.getByName("127.0.0.1");
+        checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, true);
+        checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, false);
+    }
+
+    public void test_Ipv4Fallback() throws Exception {
+        // This number of iterations gives a ~60% chance of creating the conditions that caused
+        // http://b/23088314 without making test times too long. On a hammerhead running MRZ37C
+        // using vogar, this test takes about 4s.
+        final int ITERATIONS = 10000;
+        for (int i = 0; i < ITERATIONS; i++) {
+            FileDescriptor mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+            try {
+                Os.bind(mUdpSock, Inet4Address.ANY, 0);
+            } catch (ErrnoException e) {
+                fail("ErrnoException after " + i + " iterations: " + e);
+            } finally {
+                Os.close(mUdpSock);
+            }
+        }
+    }
+
+    public void test_unlink() throws Exception {
+        File f = File.createTempFile("OsTest", "tst");
+        assertTrue(f.exists());
+        Os.unlink(f.getAbsolutePath());
+        assertFalse(f.exists());
+
+        try {
+            Os.unlink(f.getAbsolutePath());
+            fail();
+        } catch (ErrnoException e) {
+            assertEquals(OsConstants.ENOENT, e.errno);
+        }
+    }
+
+    // b/27294715
+    public void test_recvfrom_concurrentShutdown() throws Exception {
+        final FileDescriptor serverFd = Os.socket(AF_INET, SOCK_DGRAM, 0);
+        Os.bind(serverFd, InetAddress.getByName("127.0.0.1"), 0);
+        // Set 4s timeout
+        StructTimeval tv = StructTimeval.fromMillis(4000);
+        Os.setsockoptTimeval(serverFd, SOL_SOCKET, SO_RCVTIMEO, tv);
+
+        final AtomicReference<Exception> killerThreadException = new AtomicReference<Exception>(
+                null);
+        final Thread killer = new Thread(new Runnable() {
+            public void run() {
+                try {
+                    Thread.sleep(2000);
+                    try {
+                        Os.shutdown(serverFd, SHUT_RDWR);
+                    } catch (ErrnoException expected) {
+                        if (OsConstants.ENOTCONN != expected.errno) {
+                            killerThreadException.set(expected);
+                        }
+                    }
+                } catch (Exception ex) {
+                    killerThreadException.set(ex);
+                }
+            }
+        });
+        killer.start();
+
+        ByteBuffer buffer = ByteBuffer.allocate(16);
+        InetSocketAddress srcAddress = new InetSocketAddress();
+        int received = Os.recvfrom(serverFd, buffer, 0, srcAddress);
+        assertTrue(received == 0);
+        Os.close(serverFd);
+
+        killer.join();
+        assertNull(killerThreadException.get());
+    }
+
+    public void test_xattr() throws Exception {
+        final String NAME_TEST = "user.meow";
+
+        final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
+        final byte[] VALUE_PIE = "pie".getBytes(StandardCharsets.UTF_8);
+
+        File file = File.createTempFile("xattr", "test");
+        String path = file.getAbsolutePath();
+
+        try {
+            try {
+                Os.getxattr(path, NAME_TEST);
+                fail("Expected ENODATA");
+            } catch (ErrnoException e) {
+                assertEquals(OsConstants.ENODATA, e.errno);
+            }
+            assertFalse(Arrays.asList(Os.listxattr(path)).contains(NAME_TEST));
+
+            Os.setxattr(path, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
+            byte[] xattr_create = Os.getxattr(path, NAME_TEST);
+            assertTrue(Arrays.asList(Os.listxattr(path)).contains(NAME_TEST));
+            assertEquals(VALUE_CAKE.length, xattr_create.length);
+            assertStartsWith(VALUE_CAKE, xattr_create);
+
+            try {
+                Os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_CREATE);
+                fail("Expected EEXIST");
+            } catch (ErrnoException e) {
+                assertEquals(OsConstants.EEXIST, e.errno);
+            }
+
+            Os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_REPLACE);
+            byte[] xattr_replace = Os.getxattr(path, NAME_TEST);
+            assertTrue(Arrays.asList(Os.listxattr(path)).contains(NAME_TEST));
+            assertEquals(VALUE_PIE.length, xattr_replace.length);
+            assertStartsWith(VALUE_PIE, xattr_replace);
+
+            Os.removexattr(path, NAME_TEST);
+            try {
+                Os.getxattr(path, NAME_TEST);
+                fail("Expected ENODATA");
+            } catch (ErrnoException e) {
+                assertEquals(OsConstants.ENODATA, e.errno);
+            }
+            assertFalse(Arrays.asList(Os.listxattr(path)).contains(NAME_TEST));
+
+        } finally {
+            file.delete();
+        }
+    }
+
+    public void test_xattr_NPE() throws Exception {
+        File file = File.createTempFile("xattr", "test");
+        final String path = file.getAbsolutePath();
+        final String NAME_TEST = "user.meow";
+        final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
+
+        // getxattr
+        try {
+            Os.getxattr(null, NAME_TEST);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+        try {
+            Os.getxattr(path, null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        // listxattr
+        try {
+            Os.listxattr(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        // removexattr
+        try {
+            Os.removexattr(null, NAME_TEST);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+        try {
+            Os.removexattr(path, null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        // setxattr
+        try {
+            Os.setxattr(null, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+        try {
+            Os.setxattr(path, null, VALUE_CAKE, OsConstants.XATTR_CREATE);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+        try {
+            Os.setxattr(path, NAME_TEST, null, OsConstants.XATTR_CREATE);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    public void test_xattr_Errno() throws Exception {
+        final String NAME_TEST = "user.meow";
+        final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
+
+        // ENOENT, No such file or directory.
+        try {
+            Os.getxattr("", NAME_TEST);
+            fail();
+        } catch (ErrnoException e) {
+            assertEquals(ENOENT, e.errno);
+        }
+        try {
+            Os.listxattr("");
+            fail();
+        } catch (ErrnoException e) {
+            assertEquals(ENOENT, e.errno);
+        }
+        try {
+            Os.removexattr("", NAME_TEST);
+            fail();
+        } catch (ErrnoException e) {
+            assertEquals(ENOENT, e.errno);
+        }
+        try {
+            Os.setxattr("", NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
+            fail();
+        } catch (ErrnoException e) {
+            assertEquals(ENOENT, e.errno);
+        }
+
+        // ENOTSUP, Extended attributes are not supported by the filesystem, or are disabled.
+        // Since kernel version 4.9 (or some other version after 4.4), *xattr() methods
+        // may set errno to EACCESS instead. This behavior change is likely related to
+        // https://patchwork.kernel.org/patch/9294421/ which reimplemented getxattr, setxattr,
+        // and removexattr on top of generic handlers.
+        final String path = "/proc/self/stat";
+        try {
+            Os.setxattr(path, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
+            fail();
+        } catch (ErrnoException e) {
+            assertTrue("Unexpected errno: " + e.errno, e.errno == ENOTSUP || e.errno == EACCES);
+        }
+        try {
+            Os.getxattr(path, NAME_TEST);
+            fail();
+        } catch (ErrnoException e) {
+            assertEquals(ENOTSUP, e.errno);
+        }
+        try {
+            // Linux listxattr does not set errno.
+            Os.listxattr(path);
+        } catch (ErrnoException e) {
+            fail();
+        }
+        try {
+            Os.removexattr(path, NAME_TEST);
+            fail();
+        } catch (ErrnoException e) {
+            assertTrue("Unexpected errno: " + e.errno, e.errno == ENOTSUP || e.errno == EACCES);
+        }
+    }
+
+    public void test_realpath() throws Exception {
+        File tmpDir = new File(System.getProperty("java.io.tmpdir"));
+        // This is a chicken and egg problem. We have no way of knowing whether
+        // the temporary directory or one of its path elements were symlinked, so
+        // we'll need this call to realpath.
+        String canonicalTmpDir = Os.realpath(tmpDir.getAbsolutePath());
+
+        // Test that "." and ".." are resolved correctly.
+        assertEquals(canonicalTmpDir,
+                Os.realpath(canonicalTmpDir + "/./../" + tmpDir.getName()));
+
+        // Test that symlinks are resolved correctly.
+        File target = new File(tmpDir, "target");
+        File link = new File(tmpDir, "link");
+        try {
+            assertTrue(target.createNewFile());
+            Os.symlink(target.getAbsolutePath(), link.getAbsolutePath());
+
+            assertEquals(canonicalTmpDir + "/target",
+                    Os.realpath(canonicalTmpDir + "/link"));
+        } finally {
+            boolean deletedTarget = target.delete();
+            boolean deletedLink = link.delete();
+            // Asserting this here to provide a definitive reason for
+            // a subsequent failure on the same run.
+            assertTrue("deletedTarget = " + deletedTarget + ", deletedLink =" + deletedLink,
+                    deletedTarget && deletedLink);
+        }
+    }
+
+    /**
+     * Tests that TCP_USER_TIMEOUT can be set on a TCP socket, but doesn't test
+     * that it behaves as expected.
+     */
+    public void test_socket_tcpUserTimeout_setAndGet() throws Exception {
+        final FileDescriptor fd = Os.socket(AF_INET, SOCK_STREAM, 0);
+        try {
+            int v = Os.getsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_USER_TIMEOUT);
+            assertEquals(0, v); // system default value
+            int newValue = 3000;
+            Os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_USER_TIMEOUT,
+                    newValue);
+            int actualValue = Os.getsockoptInt(fd, OsConstants.IPPROTO_TCP,
+                    OsConstants.TCP_USER_TIMEOUT);
+            // The kernel can round the requested value based on the HZ setting. We allow up to 10ms
+            // difference.
+            assertTrue("Returned incorrect timeout:" + actualValue,
+                    Math.abs(newValue - actualValue) <= 10);
+            // No need to reset the value to 0, since we're throwing the socket away
+        } finally {
+            Os.close(fd);
+        }
+    }
+
+    public void test_socket_tcpUserTimeout_doesNotWorkOnDatagramSocket() throws Exception {
+        final FileDescriptor fd = Os.socket(AF_INET, SOCK_DGRAM, 0);
+        try {
+            Os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_USER_TIMEOUT,
+                    3000);
+            fail("datagram (connectionless) sockets shouldn't support TCP_USER_TIMEOUT");
+        } catch (ErrnoException expected) {
+            // expected
+        } finally {
+            Os.close(fd);
+        }
+    }
+
+    public void test_socket_sockoptTimeval_readWrite() throws Exception {
+        FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+        try {
+            StructTimeval v = Os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO);
+            assertEquals(0, v.toMillis()); // system default value
+
+            StructTimeval newValue = StructTimeval.fromMillis(3000);
+            Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, newValue);
+
+            StructTimeval actualValue = Os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO);
+
+            // The kernel can round the requested value based on the HZ setting. We allow up to 10ms
+            // difference.
+            assertTrue("Returned incorrect timeout:" + actualValue,
+                    Math.abs(newValue.toMillis() - actualValue.toMillis()) <= 10);
+            // No need to reset the value to 0, since we're throwing the socket away
+        } finally {
+            Os.close(fd);
+        }
+    }
+
+    public void test_socket_setSockoptTimeval_effective() throws Exception {
+        int timeoutValueMillis = 50;
+        int allowedTimeoutMillis = 500;
+
+        FileDescriptor fd = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+        try {
+            StructTimeval tv = StructTimeval.fromMillis(timeoutValueMillis);
+            Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv);
+            Os.bind(fd, InetAddress.getByName("::1"), 0);
+
+            byte[] request = new byte[1];
+            long startTime = System.nanoTime();
+            expectException(() -> Os.read(fd, request, 0, request.length),
+                    ErrnoException.class, EAGAIN, "Expected timeout");
+            long endTime = System.nanoTime();
+            assertTrue(Duration.ofNanos(endTime - startTime).toMillis() < allowedTimeoutMillis);
+        } finally {
+            Os.close(fd);
+        }
+    }
+
+    public void test_socket_setSockoptTimeval_nullFd() throws Exception {
+        StructTimeval tv = StructTimeval.fromMillis(500);
+        expectException(
+                () -> Os.setsockoptTimeval(null, SOL_SOCKET, SO_RCVTIMEO, tv),
+                ErrnoException.class, EBADF, "setsockoptTimeval(null, ...)");
+    }
+
+    public void test_socket_setSockoptTimeval_fileFd() throws Exception {
+        File testFile = createTempFile("test_socket_setSockoptTimeval_invalidFd", "");
+        try (FileInputStream fis = new FileInputStream(testFile)) {
+            final FileDescriptor fd = fis.getFD();
+
+            StructTimeval tv = StructTimeval.fromMillis(500);
+            expectException(
+                    () -> Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv),
+                    ErrnoException.class, ENOTSOCK, "setsockoptTimeval(<file fd>, ...)");
+        }
+    }
+
+    public void test_socket_setSockoptTimeval_badFd() throws Exception {
+        StructTimeval tv = StructTimeval.fromMillis(500);
+        FileDescriptor invalidFd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+        Os.close(invalidFd);
+
+        expectException(
+                () -> Os.setsockoptTimeval(invalidFd, SOL_SOCKET, SO_RCVTIMEO, tv),
+                ErrnoException.class, EBADF, "setsockoptTimeval(<closed fd>, ...)");
+    }
+
+    public void test_socket_setSockoptTimeval_invalidLevel() throws Exception {
+        StructTimeval tv = StructTimeval.fromMillis(500);
+        FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+        try {
+            expectException(
+                    () -> Os.setsockoptTimeval(fd, -1, SO_RCVTIMEO, tv),
+                    ErrnoException.class, ENOPROTOOPT,
+                    "setsockoptTimeval(fd, <invalid level>, ...)");
+        } finally {
+            Os.close(fd);
+        }
+    }
+
+    public void test_socket_setSockoptTimeval_invalidOpt() throws Exception {
+        StructTimeval tv = StructTimeval.fromMillis(500);
+        FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+        try {
+            expectException(
+                    () -> Os.setsockoptTimeval(fd, SOL_SOCKET, -1, tv),
+                    ErrnoException.class, ENOPROTOOPT,
+                    "setsockoptTimeval(fd, <invalid level>, ...)");
+        } finally {
+            Os.close(fd);
+        }
+    }
+
+    public void test_socket_setSockoptTimeval_nullTimeVal() throws Exception {
+        FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+        try {
+            expectException(
+                    () -> Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, null),
+                    NullPointerException.class, null, "setsockoptTimeval(..., null)");
+        } finally {
+            Os.close(fd);
+        }
+    }
+
+    public void test_socket_getSockoptTimeval_invalidOption() throws Exception {
+        FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+        try {
+            expectException(
+                    () -> Os.getsockoptTimeval(fd, SOL_SOCKET, SO_DEBUG),
+                    IllegalArgumentException.class, null,
+                    "getsockoptTimeval(..., <non-timeval option>)");
+        } finally {
+            Os.close(fd);
+        }
+    }
+
+    public void test_if_nametoindex_if_indextoname() throws Exception {
+        List<NetworkInterface> nis = Collections.list(NetworkInterface.getNetworkInterfaces());
+
+        assertTrue(nis.size() > 0);
+        for (NetworkInterface ni : nis) {
+            int index = ni.getIndex();
+            String name = ni.getName();
+            assertEquals(index, Os.if_nametoindex(name));
+            assertTrue(Os.if_indextoname(index).equals(name));
+        }
+
+        assertEquals(0, Os.if_nametoindex("this-interface-does-not-exist"));
+        assertEquals(null, Os.if_indextoname(-1000));
+
+        try {
+            Os.if_nametoindex(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    private static void assertStartsWith(byte[] expectedContents, byte[] container) {
+        for (int i = 0; i < expectedContents.length; i++) {
+            if (expectedContents[i] != container[i]) {
+                fail("Expected " + Arrays.toString(expectedContents) + " but found "
+                        + Arrays.toString(expectedContents));
+            }
+        }
+    }
+
+    public void test_readlink() throws Exception {
+        File path = new File(TestIoUtils.createTemporaryDirectory("test_readlink"), "symlink");
+
+        // ext2 and ext4 have PAGE_SIZE limits on symlink targets.
+        // If file encryption is enabled, there's extra overhead to store the
+        // size of the encrypted symlink target. There's also an off-by-one
+        // in current kernels (and marlin/sailfish where we're seeing this
+        // failure are still on 3.18, far from current). Given that we don't
+        // really care here, just use 2048 instead. http://b/33306057.
+        int size = 2048;
+        String xs = "";
+        for (int i = 0; i < size - 1; ++i) {
+            xs += "x";
+        }
+
+        Os.symlink(xs, path.getPath());
+
+        assertEquals(xs, Os.readlink(path.getPath()));
+    }
+
+    // Address should be correctly set for empty packets. http://b/33481605
+    public void test_recvfrom_EmptyPacket() throws Exception {
+        try (DatagramSocket ds = new DatagramSocket();
+             DatagramSocket srcSock = new DatagramSocket()) {
+            srcSock.send(new DatagramPacket(new byte[0], 0, ds.getLocalSocketAddress()));
+
+            byte[] recvBuf = new byte[16];
+            InetSocketAddress address = new InetSocketAddress();
+            int recvCount =
+                    android.system.Os.recvfrom(ds.getFileDescriptor$(), recvBuf, 0, 16, 0, address);
+            assertEquals(0, recvCount);
+            assertTrue(address.getAddress().isLoopbackAddress());
+            assertEquals(srcSock.getLocalPort(), address.getPort());
+        }
+    }
+
+    public void test_fstat_times() throws Exception {
+        File file = File.createTempFile("OsTest", "fstattest");
+        FileOutputStream fos = new FileOutputStream(file);
+        StructStat structStat1 = Os.fstat(fos.getFD());
+        assertEquals(structStat1.st_mtim.tv_sec, structStat1.st_mtime);
+        assertEquals(structStat1.st_ctim.tv_sec, structStat1.st_ctime);
+        assertEquals(structStat1.st_atim.tv_sec, structStat1.st_atime);
+        Thread.sleep(100);
+        fos.write(new byte[] { 1, 2, 3 });
+        fos.flush();
+        StructStat structStat2 = Os.fstat(fos.getFD());
+        fos.close();
+
+        assertEquals(-1, structStat1.st_mtim.compareTo(structStat2.st_mtim));
+        assertEquals(-1, structStat1.st_ctim.compareTo(structStat2.st_ctim));
+        assertEquals(0, structStat1.st_atim.compareTo(structStat2.st_atim));
+    }
+
+    public void test_getrlimit() throws Exception {
+        StructRlimit rlimit = Os.getrlimit(OsConstants.RLIMIT_NOFILE);
+        // We can't really make any assertions about these values since they might vary from
+        // device to device and even process to process. We do know that they will be greater
+        // than zero, though.
+        assertTrue(rlimit.rlim_cur > 0);
+        assertTrue(rlimit.rlim_max > 0);
+    }
+
+    // http://b/65051835
+    public void test_pipe2_errno() throws Exception {
+        try {
+            // flag=-1 is not a valid value for pip2, will EINVAL
+            Os.pipe2(-1);
+            fail();
+        } catch (ErrnoException expected) {
+        }
+    }
+
+    // http://b/65051835
+    public void test_sendfile_errno() throws Exception {
+        try {
+            // FileDescriptor.out is not open for input, will cause EBADF
+            Int64Ref offset = new Int64Ref(10);
+            Os.sendfile(FileDescriptor.out, FileDescriptor.out, offset, 10);
+            fail();
+        } catch (ErrnoException expected) {
+        }
+    }
+
+    public void test_sendfile_null() throws Exception {
+        File in = createTempFile("test_sendfile_null", "Hello, world!");
+        try {
+            int len = "Hello".length();
+            assertEquals("Hello", checkSendfile(in, null, len, null));
+        } finally {
+            in.delete();
+        }
+    }
+
+    public void test_sendfile_offset() throws Exception {
+        File in = createTempFile("test_sendfile_offset", "Hello, world!");
+        try {
+            // checkSendfile(sendFileImplToUse, in, startOffset, maxBytes, expectedEndOffset)
+            assertEquals("Hello", checkSendfile(in, 0L, 5, 5L));
+            assertEquals("ello,", checkSendfile(in, 1L, 5, 6L));
+            // At offset 9, only 4 bytes/chars available, even though we're asking for 5.
+            assertEquals("rld!", checkSendfile(in, 9L, 5, 13L));
+            assertEquals("", checkSendfile(in, 1L, 0, 1L));
+        } finally {
+            in.delete();
+        }
+    }
+
+    private static String checkSendfile(File in, Long startOffset,
+            int maxBytes, Long expectedEndOffset) throws IOException, ErrnoException {
+        File out = File.createTempFile(OsTest.class.getSimpleName() + "_checkSendFile", ".out");
+        try (FileInputStream inStream = new FileInputStream(in)) {
+            FileDescriptor inFd = inStream.getFD();
+            try (FileOutputStream outStream = new FileOutputStream(out)) {
+                FileDescriptor outFd = outStream.getFD();
+                Int64Ref offset = (startOffset == null) ? null : new Int64Ref(startOffset);
+                android.system.Os.sendfile(outFd, inFd, offset, maxBytes);
+                assertEquals(expectedEndOffset, offset == null ? null : offset.value);
+            }
+            return TestIoUtils.readFileAsString(out.getPath());
+        } finally {
+            out.delete();
+        }
+    }
+
+    private static File createTempFile(String namePart, String contents) throws IOException {
+        File f = File.createTempFile(OsTest.class.getSimpleName() + namePart, ".in");
+        try (FileWriter writer = new FileWriter(f)) {
+            writer.write(contents);
+        }
+        return f;
+    }
+
+    public void test_odirect() throws Exception {
+        File testFile = createTempFile("test_odirect", "");
+        try {
+            FileDescriptor fd =
+                    Os.open(testFile.toString(), O_WRONLY | O_DIRECT, S_IRUSR | S_IWUSR);
+            assertNotNull(fd);
+            assertTrue(fd.valid());
+            int flags = Os.fcntlVoid(fd, F_GETFL);
+            assertTrue("Expected file flags to include " + O_DIRECT + ", actual value: " + flags,
+                    0 != (flags & O_DIRECT));
+            Os.close(fd);
+        } finally {
+            testFile.delete();
+        }
+    }
+
+    public void test_splice() throws Exception {
+        FileDescriptor[] pipe = Os.pipe2(0);
+        File in = createTempFile("splice1", "foobar");
+        File out = createTempFile("splice2", "");
+
+        Int64Ref offIn = new Int64Ref(1);
+        Int64Ref offOut = new Int64Ref(0);
+
+        // Splice into pipe
+        try (FileInputStream streamIn = new FileInputStream(in)) {
+            FileDescriptor fdIn = streamIn.getFD();
+            long result = Os
+                    .splice(fdIn, offIn, pipe[1], null /* offOut */, 10 /* len */, 0 /* flags */);
+            assertEquals(5, result);
+            assertEquals(6, offIn.value);
+        }
+
+        // Splice from pipe
+        try (FileOutputStream streamOut = new FileOutputStream(out)) {
+            FileDescriptor fdOut = streamOut.getFD();
+            long result = Os
+                    .splice(pipe[0], null /* offIn */, fdOut, offOut, 10 /* len */, 0 /* flags */);
+            assertEquals(5, result);
+            assertEquals(5, offOut.value);
+        }
+
+        assertEquals("oobar", TestIoUtils.readFileAsString(out.getPath()));
+
+        Os.close(pipe[0]);
+        Os.close(pipe[1]);
+    }
+
+    public void test_splice_errors() throws Exception {
+        File in = createTempFile("splice3", "");
+        File out = createTempFile("splice4", "");
+        FileDescriptor[] pipe = Os.pipe2(0);
+
+        //.fdIn == null
+        try {
+            Os.splice(null /* fdIn */, null /* offIn */, pipe[1],
+                    null /*offOut*/, 10 /* len */, 0 /* flags */);
+            fail();
+        } catch (ErrnoException expected) {
+            assertEquals(EBADF, expected.errno);
+        }
+
+        //.fdOut == null
+        try {
+            Os.splice(pipe[0] /* fdIn */, null /* offIn */, null  /* fdOut */,
+                    null /*offOut*/, 10 /* len */, 0 /* flags */);
+            fail();
+        } catch (ErrnoException expected) {
+            assertEquals(EBADF, expected.errno);
+        }
+
+        // No pipe fd
+        try (FileOutputStream streamOut = new FileOutputStream(out)) {
+            try (FileInputStream streamIn = new FileInputStream(in)) {
+                FileDescriptor fdIn = streamIn.getFD();
+                FileDescriptor fdOut = streamOut.getFD();
+                Os.splice(fdIn, null  /* offIn */, fdOut, null /* offOut */, 10 /* len */,
+                        0 /* flags */);
+                fail();
+            } catch (ErrnoException expected) {
+                assertEquals(EINVAL, expected.errno);
+            }
+        }
+
+        Os.close(pipe[0]);
+        Os.close(pipe[1]);
+    }
+
+    public void testCloseNullFileDescriptor() throws Exception {
+        try {
+            Os.close(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    public void testSocketpairNullFileDescriptor1() throws Exception {
+        try {
+            Os.socketpair(AF_UNIX, SOCK_STREAM, 0, null, new FileDescriptor());
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    public void testSocketpairNullFileDescriptor2() throws Exception {
+        try {
+            Os.socketpair(AF_UNIX, SOCK_STREAM, 0, new FileDescriptor(), null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    public void testSocketpairNullFileDescriptorBoth() throws Exception {
+        try {
+            Os.socketpair(AF_UNIX, SOCK_STREAM, 0, null, null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    public void testInetPtonIpv4() {
+        String srcAddress = "127.0.0.1";
+        InetAddress inetAddress = Os.inet_pton(AF_INET, srcAddress);
+        assertEquals(srcAddress, inetAddress.getHostAddress());
+    }
+
+    public void testInetPtonIpv6() {
+        String srcAddress = "1123:4567:89ab:cdef:fedc:ba98:7654:3210";
+        InetAddress inetAddress = Os.inet_pton(AF_INET6, srcAddress);
+        assertEquals(srcAddress, inetAddress.getHostAddress());
+    }
+
+    public void testInetPtonInvalidFamily() {
+        String srcAddress = "127.0.0.1";
+        InetAddress inetAddress = Os.inet_pton(AF_UNIX, srcAddress);
+        assertNull(inetAddress);
+    }
+
+    public void testInetPtonWrongFamily() {
+        String srcAddress = "127.0.0.1";
+        InetAddress inetAddress = Os.inet_pton(AF_INET6, srcAddress);
+        assertNull(inetAddress);
+    }
+
+    public void testInetPtonInvalidData() {
+        String srcAddress = "10.1";
+        InetAddress inetAddress = Os.inet_pton(AF_INET, srcAddress);
+        assertNull(inetAddress);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/net/InetAddressTest.java b/luni/src/test/java/libcore/java/net/InetAddressTest.java
index 307cd1d..2ce79fd 100644
--- a/luni/src/test/java/libcore/java/net/InetAddressTest.java
+++ b/luni/src/test/java/libcore/java/net/InetAddressTest.java
@@ -36,6 +36,7 @@
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
 import libcore.libcore.util.SerializationTester;
+import libcore.net.InetAddressUtils;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -312,8 +313,11 @@
     public void test_getByName_invalid(String invalid) throws Exception {
         try {
             InetAddress.getByName(invalid);
-            fail("Invalid IP address incorrectly recognized as valid: "
-                + invalid);
+            String failMessage = "Invalid IP address incorrectly recognized as valid: " + invalid;
+            if (InetAddressUtils.parseNumericAddressNoThrowStripOptionalBrackets(invalid) == null) {
+                failMessage += " (it was probably unexpectedly resolved by this network's DNS)";
+            }
+            fail(failMessage);
         } catch (UnknownHostException expected) {
         }
 
diff --git a/luni/src/test/java/libcore/java/util/DateTest.java b/luni/src/test/java/libcore/java/util/DateTest.java
index df86a38..1fd8193 100644
--- a/luni/src/test/java/libcore/java/util/DateTest.java
+++ b/luni/src/test/java/libcore/java/util/DateTest.java
@@ -50,10 +50,19 @@
         c.clear();
         c.set(Calendar.YEAR, 21);
         assertEquals("Wed Jan 01 00:00:00 PST 21", c.getTime().toString());
-        assertEquals("1 Jan 21 08:00:00 GMT", c.getTime().toGMTString());
+        String actual21GmtString = c.getTime().toGMTString();
+        // zic <= 2014b data gives -08:00:00, later ones gives -07:52:58 instead. http://b/73719425
+        assertTrue("Actual: " + actual21GmtString,
+                "1 Jan 21 07:52:58 GMT".equals(actual21GmtString)
+                        || "1 Jan 21 08:00:00 GMT".equals(actual21GmtString));
+
         c.set(Calendar.YEAR, 321);
         assertEquals("Sun Jan 01 00:00:00 PST 321", c.getTime().toString());
-        assertEquals("1 Jan 321 08:00:00 GMT", c.getTime().toGMTString());
+        String actual321GmtString = c.getTime().toGMTString();
+        // zic <= 2014b data gives -08:00:00, later ones gives -07:52:58 instead. http://b/73719425
+        assertTrue("Actual: " + actual321GmtString,
+                "1 Jan 321 07:52:58 GMT".equals(actual321GmtString)
+                        || "1 Jan 321 08:00:00 GMT".equals(actual321GmtString));
     }
 
     public void test_toGMTString_nonUs() throws Exception {
@@ -64,10 +73,19 @@
         c.clear();
         c.set(Calendar.YEAR, 21);
         assertEquals("Wed Jan 01 00:00:00 PST 21", c.getTime().toString());
-        assertEquals("1 Jan 21 08:00:00 GMT", c.getTime().toGMTString());
+        String actual21GmtString = c.getTime().toGMTString();
+        // zic <= 2014b data gives -08:00:00, later ones gives -07:52:58 instead. http://b/73719425
+        assertTrue("Actual: " + actual21GmtString,
+                "1 Jan 21 07:52:58 GMT".equals(actual21GmtString)
+                        || "1 Jan 21 08:00:00 GMT".equals(actual21GmtString));
+
         c.set(Calendar.YEAR, 321);
         assertEquals("Sun Jan 01 00:00:00 PST 321", c.getTime().toString());
-        assertEquals("1 Jan 321 08:00:00 GMT", c.getTime().toGMTString());
+        String actual321GmtString = c.getTime().toGMTString();
+        // zic <= 2014b data gives -08:00:00, later ones gives -07:52:58 instead. http://b/73719425
+        assertTrue("Actual: " + actual321GmtString,
+                "1 Jan 321 07:52:58 GMT".equals(actual321GmtString)
+                        || "1 Jan 321 08:00:00 GMT".equals(actual321GmtString));
     }
 
     public void test_parse_timezones() {
diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index 68b6a54..6297080 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -58,7 +58,30 @@
     }
 
     // http://code.google.com/p/android/issues/detail?id=14395
-    public void testPreHistoricInDaylightTime() throws Exception {
+    public void testPreHistoricInDaylightTime() {
+        // A replacement for testPreHistoricInDaylightTime_old() using a zone that still lacks an
+        // explicit transition at Integer.MIN_VALUE with zic 2019a and 2019a data.
+        TimeZone tz = TimeZone.getTimeZone("CET");
+
+        long firstTransitionTimeMillis = -1693706400000L; // Apr 30, 1916 22:00:00 GMT
+        assertEquals(7200000L, tz.getOffset(firstTransitionTimeMillis));
+        assertTrue(tz.inDaylightTime(new Date(firstTransitionTimeMillis)));
+
+        long beforeFirstTransitionTimeMillis = firstTransitionTimeMillis - 1;
+        assertEquals(3600000L, tz.getOffset(beforeFirstTransitionTimeMillis));
+        assertFalse(tz.inDaylightTime(new Date(beforeFirstTransitionTimeMillis)));
+    }
+
+    // http://code.google.com/p/android/issues/detail?id=14395
+    public void testPreHistoricInDaylightTime_old() throws Exception {
+        // Originally this test was intended to assert what happens when the first transition for a
+        // time zone was a "to DST" transition. i.e. that the (implicit) offset / DST state before
+        // the first was treated as a non-DST state. Since zic version 2014c some zones have an
+        // explicit non-DST transition at time -2^31 seconds so it is no longer possible to test
+        // this with America/Los_Angeles.
+        // This regression test has been kept in case that changes again in future and to prove the
+        // behavior has remained consistent.
+
         Locale.setDefault(Locale.US);
         TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
         TimeZone.setDefault(tz);
@@ -69,7 +92,7 @@
         assertFalse(tz.inDaylightTime(date));
         assertEquals("Fri Oct 31 08:00:00 PST 1902", date.toString());
         assertEquals("31 Oct 1902 16:00:00 GMT", date.toGMTString());
-        // Any time before we have transition data is considered non-daylight, even in summer.
+        // For zic versions <= 2014b, this would be before the first transition.
         date = sdf.parse("1902-06-01T00:00:00.000+0800");
         assertEquals(-28800000, tz.getOffset(date.getTime()));
         assertFalse(tz.inDaylightTime(date));
@@ -84,17 +107,20 @@
     public void testPreHistoricOffsets() throws Exception {
         // Note: This test changed after P to account for previously incorrect handling of
         // prehistoric offsets. http://b/118835133
-        // "Africa/Bissau" has just a few transitions:
-        // Date, Offset, IsDst
-        // 1901-12-13 20:45:52,-3740,0 (Integer.MIN_VALUE, implicit with zic <= 2014b)
-        // 1912-01-01 01:00:00,-3600,0
-        // 1975-01-01 01:00:00,0,0
+        // "Africa/Bissau" has just a few known transitions:
+        // Transition time             : Offset    : DST / non-DST
+        // <Integer.MIN_VALUE secs>[1] : -01:02:20 : non-DST
+        // 1912-01-01 01:00:00 GMT     : -01:00:00 : non-DST
+        // 1975-01-01 01:00:00 GMT     :  00:00:00 : non-DST
+        //
+        // [1] This transition can be implicit or explicit depending on the version of zic used to
+        // generate the data. When implicit, the first non-DST type defn should be used.
         TimeZone tz = TimeZone.getTimeZone("Africa/Bissau");
 
-        // Before Integer.MIN_VALUE.
+        // Times before Integer.MIN_VALUE should assume we're using the first non-DST type.
         assertNonDaylightOffset(-3740, parseIsoTime("1900-01-01T00:00:00.0+0000"), tz);
 
-        // Times before 1912-01-01 01:00:00
+        // Time before 1912-01-01 01:00:00 but after Integer.MIN_VALUE.
         assertNonDaylightOffset(-3740, parseIsoTime("1911-01-01T00:00:00.0+0000"), tz);
 
         // Times after 1912-01-01 01:00:00 should use that transition.
@@ -104,19 +130,22 @@
         assertNonDaylightOffset(0, parseIsoTime("1980-01-01T00:00:00.0+0000"), tz);
     }
 
-    private static void assertNonDaylightOffset(int expectedOffsetSeconds, long epochSeconds, TimeZone tz) {
-        assertEquals(expectedOffsetSeconds, tz.getOffset(epochSeconds * 1000) / 1000);
-        assertFalse(tz.inDaylightTime(new Date(epochSeconds * 1000)));
+    private static void assertNonDaylightOffset(
+            int expectedOffsetSeconds, long epochMillis, TimeZone tz) {
+        assertEquals(expectedOffsetSeconds, tz.getOffset(epochMillis) / 1000);
+        assertFalse(tz.inDaylightTime(new Date(epochMillis)));
     }
 
+    /** Returns the millis elapsed since the beginning of the Unix epoch. */
     private static long parseIsoTime(String isoTime) throws Exception {
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
         Date date = sdf.parse(isoTime);
-        return date.getTime() / 1000;
+        return date.getTime();
     }
 
-    public void testZeroTransitionZones() throws Exception {
-        // Zones with no transitions historical or future seem ideal for testing.
+    public void testMinimalTransitionZones() throws Exception {
+        // Zones with minimal transitions, historical or future, seem ideal for testing.
+        // UTC is also included, although it may be implemented differently from the others.
         String[] ids = new String[] { "Africa/Bujumbura", "Indian/Cocos", "Pacific/Wake", "UTC" };
         for (String id : ids) {
             TimeZone tz = TimeZone.getTimeZone(id);
@@ -338,8 +367,7 @@
         final long lowerTimeMillis = beforeInt32Seconds * 1000L;
         final long upperTimeMillis = afterInt32Seconds * 1000L;
 
-        // This timezone didn't have any daylight savings prior to 1917 and this
-        // date is in 1900.
+        // This timezone didn't have any daylight savings prior to 1917 and this date is in 1900.
         assertFalse(tz.inDaylightTime(new Date(lowerTimeMillis)));
 
         // http://b/118835133:
@@ -347,7 +375,7 @@
         // 07:00:00 GMT) the offset was -18000000.
         // zic > 2014b produces data that suggests before Integer.MIN_VALUE seconds the offset was
         // -17762000 and between Integer.MIN_VALUE and -1633280400 it was -18000000. Once Android
-        // moves to zic > 2014b the -18000000 can be removed.
+        // moves to zic > 2014b the -18000000 can be removed. http://b/73719425
         int actualOffset = tz.getOffset(lowerTimeMillis);
         assertTrue(-18000000 == actualOffset || -17762000 == actualOffset);
 
diff --git a/luni/src/test/java/libcore/libcore/io/BlockGuardOsTest.java b/luni/src/test/java/libcore/libcore/io/BlockGuardOsTest.java
index 91d7c3a..471906f 100644
--- a/luni/src/test/java/libcore/libcore/io/BlockGuardOsTest.java
+++ b/luni/src/test/java/libcore/libcore/io/BlockGuardOsTest.java
@@ -24,6 +24,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import android.system.ErrnoException;
 import android.system.OsConstants;
 import android.system.StructAddrinfo;
 
@@ -58,22 +59,42 @@
 
     @Mock private Os mockOsDelegate;
     @Mock private BlockGuard.Policy mockThreadPolicy;
+    @Mock private BlockGuard.VmPolicy mockVmPolicy;
 
     private BlockGuard.Policy savedThreadPolicy;
+    private BlockGuard.VmPolicy savedVmPolicy;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         savedThreadPolicy = BlockGuard.getThreadPolicy();
+        savedVmPolicy = BlockGuard.getVmPolicy();
         BlockGuard.setThreadPolicy(mockThreadPolicy);
+        BlockGuard.setVmPolicy(mockVmPolicy);
     }
 
     @After
     public void tearDown() {
+        BlockGuard.setVmPolicy(savedVmPolicy);
         BlockGuard.setThreadPolicy(savedThreadPolicy);
     }
 
     @Test
+    public void test_blockguardOsIsNotifiedByDefault_rename() {
+        String oldPath = "BlockGuardOsTest/missing/old/path";
+        String newPath = "BlockGuardOsTest/missing/new/path";
+        try {
+            // We try not to be prescriptive about the exact default Os implementation.
+            // Whatever default Os is installed, we do expect BlockGuard to be called.
+            Os.getDefault().rename(oldPath, newPath);
+        } catch (ErrnoException ignored) {
+        }
+        verify(mockThreadPolicy).onWriteToDisk();
+        verify(mockVmPolicy).onPathAccess(oldPath);
+        verify(mockVmPolicy).onPathAccess(newPath);
+    }
+
+    @Test
     public void test_android_getaddrinfo_networkPolicy() {
         InetAddress[] addresses = new InetAddress[] { InetAddress.getLoopbackAddress() };
         when(mockOsDelegate.android_getaddrinfo(anyString(), any(), anyInt()))
diff --git a/luni/src/test/java/libcore/libcore/io/OsTest.java b/luni/src/test/java/libcore/libcore/io/OsTest.java
index 1bda0e6..6359f87 100644
--- a/luni/src/test/java/libcore/libcore/io/OsTest.java
+++ b/luni/src/test/java/libcore/libcore/io/OsTest.java
@@ -16,1451 +16,54 @@
 
 package libcore.libcore.io;
 
-import android.system.ErrnoException;
-import android.system.Int64Ref;
-import android.system.NetlinkSocketAddress;
-import android.system.OsConstants;
-import android.system.PacketSocketAddress;
-import android.system.StructRlimit;
-import android.system.StructStat;
-import android.system.StructTimeval;
-import android.system.StructUcred;
-import android.system.UnixSocketAddress;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.NetworkInterface;
-import java.net.ServerSocket;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.time.Duration;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.atomic.AtomicReference;
 import junit.framework.TestCase;
 
-import libcore.io.BlockGuardOs;
 import libcore.io.ForwardingOs;
-import libcore.io.IoUtils;
-import libcore.io.Libcore;
 import libcore.io.Os;
 
 import org.mockito.Mockito;
 
-import static android.system.OsConstants.*;
-import static libcore.libcore.io.OsTest.SendFileImpl.ANDROID_SYSTEM_OS_INT64_REF;
-import static libcore.libcore.io.OsTest.SendFileImpl.LIBCORE_OS;
-
 public class OsTest extends TestCase {
-  public void testIsSocket() throws Exception {
-    File f = new File("/dev/null");
-    FileInputStream fis = new FileInputStream(f);
-    assertFalse(S_ISSOCK(Libcore.os.fstat(fis.getFD()).st_mode));
-    fis.close();
 
-    ServerSocket s = new ServerSocket();
-    assertTrue(S_ISSOCK(Libcore.os.fstat(s.getImpl().getFD$()).st_mode));
-    s.close();
-  }
-
-  public void testFcntlInt() throws Exception {
-    File f = File.createTempFile("OsTest", "tst");
-    FileInputStream fis = null;
-    try {
-      fis = new FileInputStream(f);
-      Libcore.os.fcntlInt(fis.getFD(), F_SETFD, FD_CLOEXEC);
-      int flags = Libcore.os.fcntlVoid(fis.getFD(), F_GETFD);
-      assertTrue((flags & FD_CLOEXEC) != 0);
-    } finally {
-      IoUtils.closeQuietly(fis);
-      f.delete();
-    }
-  }
-
-  public void testUnixDomainSockets_in_file_system() throws Exception {
-    String path = System.getProperty("java.io.tmpdir") + "/test_unix_socket";
-    new File(path).delete();
-    checkUnixDomainSocket(UnixSocketAddress.createFileSystem(path), false);
-  }
-
-  public void testUnixDomainSocket_abstract_name() throws Exception {
-    // Linux treats a sun_path starting with a NUL byte as an abstract name. See unix(7).
-    checkUnixDomainSocket(UnixSocketAddress.createAbstract("/abstract_name_unix_socket"), true);
-  }
-
-  public void testUnixDomainSocket_unnamed() throws Exception {
-    final FileDescriptor fd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
-    // unix(7) says an unbound socket is unnamed.
-    checkNoSockName(fd);
-    Libcore.os.close(fd);
-  }
-
-  private void checkUnixDomainSocket(final UnixSocketAddress address, final boolean isAbstract)
-      throws Exception {
-    final FileDescriptor serverFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
-    Libcore.os.bind(serverFd, address);
-    Libcore.os.listen(serverFd, 5);
-
-    checkSockName(serverFd, isAbstract, address);
-
-    Thread server = new Thread(new Runnable() {
-      public void run() {
+    public void testCompareAndSetDefault_success() throws Exception {
+        Os defaultOs = Os.getDefault();
+        Os mockOs = Mockito.mock(Os.class);
         try {
-          UnixSocketAddress peerAddress = UnixSocketAddress.createUnnamed();
-          FileDescriptor clientFd = Libcore.os.accept(serverFd, peerAddress);
-          checkSockName(clientFd, isAbstract, address);
-          checkNoName(peerAddress);
+            // There shouldn't be any concurrent threads replacing the default Os.
+            assertTrue(Os.compareAndSetDefault(defaultOs, mockOs));
+            assertSame(mockOs, Os.getDefault());
 
-          checkNoPeerName(clientFd);
-
-          StructUcred credentials = Libcore.os.getsockoptUcred(clientFd, SOL_SOCKET, SO_PEERCRED);
-          assertEquals(Libcore.os.getpid(), credentials.pid);
-          assertEquals(Libcore.os.getuid(), credentials.uid);
-          assertEquals(Libcore.os.getgid(), credentials.gid);
-
-          byte[] request = new byte[256];
-          Libcore.os.read(clientFd, request, 0, request.length);
-
-          String s = new String(request, "UTF-8");
-          byte[] response = s.toUpperCase(Locale.ROOT).getBytes("UTF-8");
-          Libcore.os.write(clientFd, response, 0, response.length);
-
-          Libcore.os.close(clientFd);
-        } catch (Exception ex) {
-          throw new RuntimeException(ex);
+            // Calls to android.system.Os should now reach our custom Os instance.
+            android.system.Os.rename("/old/path", "/new/path");
+            Mockito.verify(mockOs).rename("/old/path", "/new/path");
+        } finally {
+            assertTrue(Os.compareAndSetDefault(mockOs, defaultOs));
+            assertSame(defaultOs, Os.getDefault());
         }
-      }
-    });
-    server.start();
-
-    FileDescriptor clientFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
-
-    Libcore.os.connect(clientFd, address);
-    checkNoSockName(clientFd);
-
-    String string = "hello, world!";
-
-    byte[] request = string.getBytes("UTF-8");
-    assertEquals(request.length, Libcore.os.write(clientFd, request, 0, request.length));
-
-    byte[] response = new byte[request.length];
-    assertEquals(response.length, Libcore.os.read(clientFd, response, 0, response.length));
-
-    assertEquals(string.toUpperCase(Locale.ROOT), new String(response, "UTF-8"));
-
-    Libcore.os.close(clientFd);
-  }
-
-  private static void checkSockName(FileDescriptor fd, boolean isAbstract,
-      UnixSocketAddress address) throws Exception {
-    UnixSocketAddress isa = (UnixSocketAddress) Libcore.os.getsockname(fd);
-    assertEquals(address, isa);
-    if (isAbstract) {
-      assertEquals(0, isa.getSunPath()[0]);
     }
-  }
 
-  private void checkNoName(UnixSocketAddress usa) {
-    assertEquals(0, usa.getSunPath().length);
-  }
-
-  private void checkNoPeerName(FileDescriptor fd) throws Exception {
-    checkNoName((UnixSocketAddress) Libcore.os.getpeername(fd));
-  }
-
-  private void checkNoSockName(FileDescriptor fd) throws Exception {
-    checkNoName((UnixSocketAddress) Libcore.os.getsockname(fd));
-  }
-
-  public void test_strsignal() throws Exception {
-    assertEquals("Killed", Libcore.os.strsignal(9));
-    assertEquals("Unknown signal -1", Libcore.os.strsignal(-1));
-  }
-
-  public void test_byteBufferPositions_write_pwrite() throws Exception {
-    FileOutputStream fos = new FileOutputStream(new File("/dev/null"));
-    FileDescriptor fd = fos.getFD();
-    final byte[] contents = new String("goodbye, cruel world").getBytes(StandardCharsets.US_ASCII);
-    ByteBuffer byteBuffer = ByteBuffer.wrap(contents);
-
-    byteBuffer.position(0);
-    int written = Libcore.os.write(fd, byteBuffer);
-    assertTrue(written > 0);
-    assertEquals(written, byteBuffer.position());
-
-    byteBuffer.position(4);
-    written = Libcore.os.write(fd, byteBuffer);
-    assertTrue(written > 0);
-    assertEquals(written + 4, byteBuffer.position());
-
-    byteBuffer.position(0);
-    written = Libcore.os.pwrite(fd, byteBuffer, 64 /* offset */);
-    assertTrue(written > 0);
-    assertEquals(written, byteBuffer.position());
-
-    byteBuffer.position(4);
-    written = Libcore.os.pwrite(fd, byteBuffer, 64 /* offset */);
-    assertTrue(written > 0);
-    assertEquals(written + 4, byteBuffer.position());
-
-    fos.close();
-  }
-
-  public void test_byteBufferPositions_read_pread() throws Exception {
-    FileInputStream fis = new FileInputStream(new File("/dev/zero"));
-    FileDescriptor fd = fis.getFD();
-    ByteBuffer byteBuffer = ByteBuffer.allocate(64);
-
-    byteBuffer.position(0);
-    int read = Libcore.os.read(fd, byteBuffer);
-    assertTrue(read > 0);
-    assertEquals(read, byteBuffer.position());
-
-    byteBuffer.position(4);
-    read = Libcore.os.read(fd, byteBuffer);
-    assertTrue(read > 0);
-    assertEquals(read + 4, byteBuffer.position());
-
-    byteBuffer.position(0);
-    read = Libcore.os.pread(fd, byteBuffer, 64 /* offset */);
-    assertTrue(read > 0);
-    assertEquals(read, byteBuffer.position());
-
-    byteBuffer.position(4);
-    read = Libcore.os.pread(fd, byteBuffer, 64 /* offset */);
-    assertTrue(read > 0);
-    assertEquals(read + 4, byteBuffer.position());
-
-    fis.close();
-  }
-
-  static void checkByteBufferPositions_sendto_recvfrom(
-      int family, InetAddress loopback) throws Exception {
-    final FileDescriptor serverFd = Libcore.os.socket(family, SOCK_STREAM, 0);
-    Libcore.os.bind(serverFd, loopback, 0);
-    Libcore.os.listen(serverFd, 5);
-
-    InetSocketAddress address = (InetSocketAddress) Libcore.os.getsockname(serverFd);
-
-    final Thread server = new Thread(new Runnable() {
-      public void run() {
+    public void testCompareandSetDefault_null() {
+        Os defaultOs = Os.getDefault();
+        // update == null is not allowed
         try {
-          InetSocketAddress peerAddress = new InetSocketAddress();
-          FileDescriptor clientFd = Libcore.os.accept(serverFd, peerAddress);
-
-          // Attempt to receive a maximum of 24 bytes from the client, and then
-          // close the connection.
-          ByteBuffer buffer = ByteBuffer.allocate(16);
-          int received = Libcore.os.recvfrom(clientFd, buffer, 0, null);
-          assertTrue(received > 0);
-          assertEquals(received, buffer.position());
-
-          ByteBuffer buffer2 = ByteBuffer.allocate(16);
-          buffer2.position(8);
-          received = Libcore.os.recvfrom(clientFd, buffer2, 0, null);
-          assertTrue(received > 0);
-          assertEquals(received + 8, buffer.position());
-
-          Libcore.os.close(clientFd);
-        } catch (Exception ex) {
-          throw new RuntimeException(ex);
-        }
-      }
-    });
-
-    server.start();
-
-    FileDescriptor clientFd = Libcore.os.socket(family, SOCK_STREAM, 0);
-    Libcore.os.connect(clientFd, address.getAddress(), address.getPort());
-
-    final byte[] bytes = "good bye, cruel black hole with fancy distortion"
-        .getBytes(StandardCharsets.US_ASCII);
-    assertTrue(bytes.length > 24);
-
-    ByteBuffer input = ByteBuffer.wrap(bytes);
-    input.position(0);
-    input.limit(16);
-
-    int sent = Libcore.os.sendto(clientFd, input, 0, address.getAddress(), address.getPort());
-    assertTrue(sent > 0);
-    assertEquals(sent, input.position());
-
-    input.position(16);
-    input.limit(24);
-    sent = Libcore.os.sendto(clientFd, input, 0, address.getAddress(), address.getPort());
-    assertTrue(sent > 0);
-    assertEquals(sent + 16, input.position());
-
-    Libcore.os.close(clientFd);
-  }
-
-  interface ExceptionalRunnable {
-    public void run() throws Exception;
-  }
-
-  /**
-   * Expects that the given Runnable will throw an exception of the specified class. If the class is
-   * ErrnoException, and expectedErrno is non-null, also checks that the errno is equal to
-   * expectedErrno.
-   */
-  private static void expectException(ExceptionalRunnable r, Class<? extends Exception> exClass,
-        Integer expectedErrno, String msg) {
-    try {
-      r.run();
-      fail(msg + " did not throw exception");
-    } catch (Exception e) {
-      assertEquals(msg + " threw unexpected exception", exClass, e.getClass());
-
-      if (expectedErrno != null) {
-        if (e instanceof ErrnoException) {
-          assertEquals(msg + "threw ErrnoException with unexpected error number",
-              (int) expectedErrno, ((ErrnoException) e).errno);
-        } else {
-          fail("Can only pass expectedErrno when expecting ErrnoException");
-        }
-      }
-
-    }
-  }
-
-  private static void expectBindException(FileDescriptor socket, SocketAddress addr,
-      Class exClass, Integer expectedErrno) {
-    String msg = String.format("bind(%s, %s)", socket, addr);
-    expectException(() -> { Libcore.os.bind(socket, addr); }, exClass, expectedErrno, msg);
-  }
-
-  private static void expectConnectException(FileDescriptor socket, SocketAddress addr,
-      Class exClass, Integer expectedErrno) {
-    String msg = String.format("connect(%s, %s)", socket, addr);
-    expectException(() -> { Libcore.os.connect(socket, addr); }, exClass, expectedErrno, msg);
-  }
-
-  private static void expectSendtoException(FileDescriptor socket, SocketAddress addr,
-      Class exClass, Integer expectedErrno) {
-    String msg = String.format("sendto(%s, %s)", socket, addr);
-    byte[] packet = new byte[42];
-    expectException(() -> { Libcore.os.sendto(socket, packet, 0, packet.length, 0, addr); },
-        exClass, expectedErrno, msg);
-  }
-
-  private static void expectBindConnectSendtoSuccess(FileDescriptor socket, String socketDesc,
-                                                     SocketAddress addr) {
-    String msg = socketDesc + " socket to " + addr.toString();
-
-    try {
-      try {
-        // Expect that bind throws when any of its arguments are null.
-        expectBindException(null, addr, ErrnoException.class, EBADF);
-        expectBindException(socket, null, NullPointerException.class, null);
-        expectBindException(null, null, NullPointerException.class, null);
-
-        // Expect bind to succeed.
-        Libcore.os.bind(socket, addr);
-
-        // Find out which port we're actually bound to, and use that in subsequent connect() and
-        // send() calls. We can't send to addr because that has a port of 0.
-        if (addr instanceof InetSocketAddress) {
-          InetSocketAddress addrISA = (InetSocketAddress) addr;
-          InetSocketAddress socknameISA = (InetSocketAddress) Libcore.os.getsockname(socket);
-
-          assertEquals(addrISA.getAddress(), socknameISA.getAddress());
-          assertEquals(0, addrISA.getPort());
-          assertFalse(0 == socknameISA.getPort());
-          addr = socknameISA;
-        }
-
-        // Expect sendto with a null address to throw because the socket is not connected, but to
-        // succeed with a non-null address.
-        byte[] packet = new byte[42];
-        Libcore.os.sendto(socket, packet, 0, packet.length, 0, addr);
-        // UNIX and IP sockets return different errors for this operation, so we can't check errno.
-        expectSendtoException(socket, null, ErrnoException.class, null);
-        expectSendtoException(null, null, ErrnoException.class, EBADF);
-
-        // Expect that connect throws when any of its arguments are null.
-        expectConnectException(null, addr, ErrnoException.class, EBADF);
-        expectConnectException(socket, null, NullPointerException.class, null);
-        expectConnectException(null, null, NullPointerException.class, null);
-
-        // Expect connect to succeed.
-        Libcore.os.connect(socket, addr);
-        assertEquals(Libcore.os.getsockname(socket), Libcore.os.getpeername(socket));
-
-        // Expect sendto to succeed both when given an explicit address and a null address.
-        Libcore.os.sendto(socket, packet, 0, packet.length, 0, addr);
-        Libcore.os.sendto(socket, packet, 0, packet.length, 0, null);
-      } catch (SocketException | ErrnoException e) {
-        fail("Expected success for " + msg + ", but got: " + e);
-      }
-
-    } finally {
-      IoUtils.closeQuietly(socket);
-    }
-  }
-
-  private static void expectBindConnectSendtoErrno(int bindErrno, int connectErrno, int sendtoErrno,
-                                                   FileDescriptor socket, String socketDesc,
-                                                   SocketAddress addr) {
-    try {
-
-      // Expect bind to fail with bindErrno.
-      String msg = "bind " + socketDesc + " socket to " + addr.toString();
-      try {
-        Libcore.os.bind(socket, addr);
-        fail("Expected to fail " + msg);
-      } catch (ErrnoException e) {
-        assertEquals("Expected errno " + bindErrno + " " + msg, bindErrno, e.errno);
-      } catch (SocketException e) {
-        fail("Unexpected SocketException " + msg);
-      }
-
-      // Expect connect to fail with connectErrno.
-      msg = "connect " + socketDesc + " socket to " + addr.toString();
-      try {
-        Libcore.os.connect(socket, addr);
-        fail("Expected to fail " + msg);
-      } catch (ErrnoException e) {
-        assertEquals("Expected errno " + connectErrno + " " + msg, connectErrno, e.errno);
-      } catch (SocketException e) {
-        fail("Unexpected SocketException " + msg);
-      }
-
-      // Expect sendto to fail with sendtoErrno.
-      byte[] packet = new byte[42];
-      msg = "sendto " + socketDesc + " socket to " + addr.toString();
-      try {
-        Libcore.os.sendto(socket, packet, 0, packet.length, 0, addr);
-        fail("Expected to fail " + msg);
-      } catch (ErrnoException e) {
-        assertEquals("Expected errno " + sendtoErrno + " " + msg, sendtoErrno, e.errno);
-      } catch (SocketException e) {
-        fail("Unexpected SocketException " + msg);
-      }
-
-    } finally {
-      // No matter what happened, close the socket.
-      IoUtils.closeQuietly(socket);
-    }
-  }
-
-  private FileDescriptor makeIpv4Socket() throws Exception {
-    return Libcore.os.socket(AF_INET, SOCK_DGRAM, 0);
-  }
-
-  private FileDescriptor makeIpv6Socket() throws Exception {
-    return Libcore.os.socket(AF_INET6, SOCK_DGRAM, 0);
-  }
-
-  private FileDescriptor makeUnixSocket() throws Exception {
-    return Libcore.os.socket(AF_UNIX, SOCK_DGRAM, 0);
-  }
-
-  public void testCrossFamilyBindConnectSendto() throws Exception {
-    SocketAddress addrIpv4 = new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 0);
-    SocketAddress addrIpv6 = new InetSocketAddress(InetAddress.getByName("::1"), 0);
-    SocketAddress addrUnix = UnixSocketAddress.createAbstract("/abstract_name_unix_socket");
-
-    expectBindConnectSendtoSuccess(makeIpv4Socket(), "ipv4", addrIpv4);
-    expectBindConnectSendtoErrno(EAFNOSUPPORT, EAFNOSUPPORT, EAFNOSUPPORT,
-                                 makeIpv4Socket(), "ipv4", addrIpv6);
-    expectBindConnectSendtoErrno(EAFNOSUPPORT, EAFNOSUPPORT, EAFNOSUPPORT,
-                                 makeIpv4Socket(), "ipv4", addrUnix);
-
-    // This succeeds because Java always uses dual-stack sockets and all InetAddress and
-    // InetSocketAddress objects represent IPv4 addresses using IPv4-mapped IPv6 addresses.
-    expectBindConnectSendtoSuccess(makeIpv6Socket(), "ipv6", addrIpv4);
-    expectBindConnectSendtoSuccess(makeIpv6Socket(), "ipv6", addrIpv6);
-    expectBindConnectSendtoErrno(EAFNOSUPPORT, EAFNOSUPPORT, EINVAL,
-                                 makeIpv6Socket(), "ipv6", addrUnix);
-
-    expectBindConnectSendtoErrno(EINVAL, EINVAL, EINVAL,
-                                 makeUnixSocket(), "unix", addrIpv4);
-    expectBindConnectSendtoErrno(EINVAL, EINVAL, EINVAL,
-                                 makeUnixSocket(), "unix", addrIpv6);
-    expectBindConnectSendtoSuccess(makeUnixSocket(), "unix", addrUnix);
-  }
-
-  public void testUnknownSocketAddressSubclass() throws Exception {
-    class MySocketAddress extends SocketAddress {}
-    MySocketAddress myaddr = new MySocketAddress();
-
-    for (int family : new int[]{AF_INET, AF_INET6, AF_NETLINK}) {
-      FileDescriptor s = Libcore.os.socket(family, SOCK_DGRAM, 0);
-      try {
-
-        try {
-          Libcore.os.bind(s, myaddr);
-          fail("bind socket family " + family + " to unknown SocketAddress subclass succeeded");
-        } catch (UnsupportedOperationException expected) {}
-
-        try {
-          Libcore.os.connect(s, myaddr);
-          fail("connect socket family " + family + " to unknown SocketAddress subclass succeeded");
-        } catch (UnsupportedOperationException expected) {}
-
-        byte[] msg = new byte[42];
-        try {
-          Libcore.os.sendto(s, msg, 0, msg.length, 0, myaddr);
-          fail("sendto socket family " + family + " to unknown SocketAddress subclass succeeded");
-        } catch (UnsupportedOperationException expected) {}
-
-      } finally {
-        Libcore.os.close(s);
-      }
-    }
-  }
-
-  public void test_NetlinkSocket() throws Exception {
-    FileDescriptor nlSocket = Libcore.os.socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
-    Libcore.os.bind(nlSocket, new NetlinkSocketAddress());
-    NetlinkSocketAddress address = (NetlinkSocketAddress) Libcore.os.getsockname(nlSocket);
-    assertTrue(address.getPortId() > 0);
-    assertEquals(0, address.getGroupsMask());
-
-    NetlinkSocketAddress nlKernel = new NetlinkSocketAddress();
-    Libcore.os.connect(nlSocket, nlKernel);
-    NetlinkSocketAddress nlPeer = (NetlinkSocketAddress) Libcore.os.getpeername(nlSocket);
-    assertEquals(0, nlPeer.getPortId());
-    assertEquals(0, nlPeer.getGroupsMask());
-    Libcore.os.close(nlSocket);
-  }
-
-  public void test_PacketSocketAddress() throws Exception {
-    NetworkInterface lo = NetworkInterface.getByName("lo");
-    FileDescriptor fd = Libcore.os.socket(AF_PACKET, SOCK_DGRAM, ETH_P_IPV6);
-    PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6, lo.getIndex());
-    Libcore.os.bind(fd, addr);
-
-    PacketSocketAddress bound = (PacketSocketAddress) Libcore.os.getsockname(fd);
-    assertEquals((short) ETH_P_IPV6, bound.sll_protocol);  // ETH_P_IPV6 is an int.
-    assertEquals(lo.getIndex(), bound.sll_ifindex);
-    assertEquals(ARPHRD_LOOPBACK, bound.sll_hatype);
-    assertEquals(0, bound.sll_pkttype);
-
-    // The loopback address is ETH_ALEN bytes long and is all zeros.
-    // http://lxr.free-electrons.com/source/drivers/net/loopback.c?v=3.10#L167
-    assertEquals(6, bound.sll_addr.length);
-    for (int i = 0; i < 6; i++) {
-      assertEquals(0, bound.sll_addr[i]);
-    }
-  }
-
-  public void test_byteBufferPositions_sendto_recvfrom_af_inet() throws Exception {
-    checkByteBufferPositions_sendto_recvfrom(AF_INET, InetAddress.getByName("127.0.0.1"));
-  }
-
-  public void test_byteBufferPositions_sendto_recvfrom_af_inet6() throws Exception {
-    checkByteBufferPositions_sendto_recvfrom(AF_INET6, InetAddress.getByName("::1"));
-  }
-
-  private void checkSendToSocketAddress(int family, InetAddress loopback) throws Exception {
-    FileDescriptor recvFd = Libcore.os.socket(family, SOCK_DGRAM, 0);
-    Libcore.os.bind(recvFd, loopback, 0);
-    StructTimeval tv = StructTimeval.fromMillis(20);
-    Libcore.os.setsockoptTimeval(recvFd, SOL_SOCKET, SO_RCVTIMEO, tv);
-
-    InetSocketAddress to = ((InetSocketAddress) Libcore.os.getsockname(recvFd));
-    FileDescriptor sendFd = Libcore.os.socket(family, SOCK_DGRAM, 0);
-    byte[] msg = ("Hello, I'm going to a socket address: " + to.toString()).getBytes("UTF-8");
-    int len = msg.length;
-
-    assertEquals(len, Libcore.os.sendto(sendFd, msg, 0, len, 0, to));
-    byte[] received = new byte[msg.length + 42];
-    InetSocketAddress from = new InetSocketAddress();
-    assertEquals(len, Libcore.os.recvfrom(recvFd, received, 0, received.length, 0, from));
-    assertEquals(loopback, from.getAddress());
-  }
-
-  public void test_sendtoSocketAddress_af_inet() throws Exception {
-    checkSendToSocketAddress(AF_INET, InetAddress.getByName("127.0.0.1"));
-  }
-
-  public void test_sendtoSocketAddress_af_inet6() throws Exception {
-    checkSendToSocketAddress(AF_INET6, InetAddress.getByName("::1"));
-  }
-
-  public void test_socketFamilies() throws Exception {
-    FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
-    Libcore.os.bind(fd, InetAddress.getByName("::"), 0);
-    InetSocketAddress localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
-    assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
-
-    fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
-    Libcore.os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
-    localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
-    assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
-
-    fd = Libcore.os.socket(AF_INET, SOCK_STREAM, 0);
-    Libcore.os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
-    localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
-    assertEquals(Inet4Address.ANY, localSocketAddress.getAddress());
-    try {
-      Libcore.os.bind(fd, InetAddress.getByName("::"), 0);
-      fail("Expected ErrnoException binding IPv4 socket to ::");
-    } catch (ErrnoException expected) {
-      assertEquals("Expected EAFNOSUPPORT binding IPv4 socket to ::", EAFNOSUPPORT, expected.errno);
-    }
-  }
-
-  private static void assertArrayEquals(byte[] expected, byte[] actual) {
-    assertTrue("Expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual),
-        Arrays.equals(expected, actual));
-  }
-
-  private static void checkSocketPing(FileDescriptor fd, InetAddress to, byte[] packet,
-      byte type, byte responseType, boolean useSendto) throws Exception {
-    int len = packet.length;
-    packet[0] = type;
-    if (useSendto) {
-      assertEquals(len, Libcore.os.sendto(fd, packet, 0, len, 0, to, 0));
-    } else {
-      Libcore.os.connect(fd, to, 0);
-      assertEquals(len, Libcore.os.sendto(fd, packet, 0, len, 0, null, 0));
-    }
-
-    int icmpId = ((InetSocketAddress) Libcore.os.getsockname(fd)).getPort();
-    byte[] received = new byte[4096];
-    InetSocketAddress srcAddress = new InetSocketAddress();
-    assertEquals(len, Libcore.os.recvfrom(fd, received, 0, received.length, 0, srcAddress));
-    assertEquals(to, srcAddress.getAddress());
-    assertEquals(responseType, received[0]);
-    assertEquals(received[4], (byte) (icmpId >> 8));
-    assertEquals(received[5], (byte) (icmpId & 0xff));
-
-    received = Arrays.copyOf(received, len);
-    received[0] = (byte) type;
-    received[2] = received[3] = 0;  // Checksum.
-    received[4] = received[5] = 0;  // ICMP ID.
-    assertArrayEquals(packet, received);
-  }
-
-  public void test_socketPing() throws Exception {
-    final byte ICMP_ECHO = 8, ICMP_ECHOREPLY = 0;
-    final byte ICMPV6_ECHO_REQUEST = (byte) 128, ICMPV6_ECHO_REPLY = (byte) 129;
-    final byte[] packet = ("\000\000\000\000" +  // ICMP type, code.
-        "\000\000\000\003" +  // ICMP ID (== port), sequence number.
-        "Hello myself").getBytes(StandardCharsets.US_ASCII);
-
-    FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
-    InetAddress ipv6Loopback = InetAddress.getByName("::1");
-    checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, true);
-    checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, false);
-
-    fd = Libcore.os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
-    InetAddress ipv4Loopback = InetAddress.getByName("127.0.0.1");
-    checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, true);
-    checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, false);
-  }
-
-  public void test_Ipv4Fallback() throws Exception {
-    // This number of iterations gives a ~60% chance of creating the conditions that caused
-    // http://b/23088314 without making test times too long. On a hammerhead running MRZ37C using
-    // vogar, this test takes about 4s.
-    final int ITERATIONS = 10000;
-    for (int i = 0; i < ITERATIONS; i++) {
-      FileDescriptor mUdpSock = Libcore.os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
-      try {
-          Libcore.os.bind(mUdpSock, Inet4Address.ANY, 0);
-      } catch(ErrnoException e) {
-          fail("ErrnoException after " + i + " iterations: " + e);
-      } finally {
-          Libcore.os.close(mUdpSock);
-      }
-    }
-  }
-
-  public void test_unlink() throws Exception {
-    File f = File.createTempFile("OsTest", "tst");
-    assertTrue(f.exists());
-    Libcore.os.unlink(f.getAbsolutePath());
-    assertFalse(f.exists());
-
-    try {
-      Libcore.os.unlink(f.getAbsolutePath());
-      fail();
-    } catch (ErrnoException e) {
-      assertEquals(OsConstants.ENOENT, e.errno);
-    }
-  }
-
-  // b/27294715
-  public void test_recvfrom_concurrentShutdown() throws Exception {
-      final FileDescriptor serverFd = Libcore.os.socket(AF_INET, SOCK_DGRAM, 0);
-      Libcore.os.bind(serverFd, InetAddress.getByName("127.0.0.1"), 0);
-      // Set 4s timeout
-      StructTimeval tv = StructTimeval.fromMillis(4000);
-      Libcore.os.setsockoptTimeval(serverFd, SOL_SOCKET, SO_RCVTIMEO, tv);
-
-      final AtomicReference<Exception> killerThreadException = new AtomicReference<Exception>(null);
-      final Thread killer = new Thread(new Runnable() {
-          public void run() {
-              try {
-                  Thread.sleep(2000);
-                  try {
-                      Libcore.os.shutdown(serverFd, SHUT_RDWR);
-                  } catch (ErrnoException expected) {
-                      if (OsConstants.ENOTCONN != expected.errno) {
-                          killerThreadException.set(expected);
-                      }
-                  }
-              } catch (Exception ex) {
-                  killerThreadException.set(ex);
-              }
-          }
-      });
-      killer.start();
-
-      ByteBuffer buffer = ByteBuffer.allocate(16);
-      InetSocketAddress srcAddress = new InetSocketAddress();
-      int received = Libcore.os.recvfrom(serverFd, buffer, 0, srcAddress);
-      assertTrue(received == 0);
-      Libcore.os.close(serverFd);
-
-      killer.join();
-      assertNull(killerThreadException.get());
-  }
-
-  public void test_xattr() throws Exception {
-    final String NAME_TEST = "user.meow";
-
-    final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
-    final byte[] VALUE_PIE = "pie".getBytes(StandardCharsets.UTF_8);
-
-    File file = File.createTempFile("xattr", "test");
-    String path = file.getAbsolutePath();
-
-    try {
-      try {
-        Libcore.os.getxattr(path, NAME_TEST);
-        fail("Expected ENODATA");
-      } catch (ErrnoException e) {
-        assertEquals(OsConstants.ENODATA, e.errno);
-      }
-      assertFalse(Arrays.asList(Libcore.os.listxattr(path)).contains(NAME_TEST));
-
-      Libcore.os.setxattr(path, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
-      byte[] xattr_create = Libcore.os.getxattr(path, NAME_TEST);
-      assertTrue(Arrays.asList(Libcore.os.listxattr(path)).contains(NAME_TEST));
-      assertEquals(VALUE_CAKE.length, xattr_create.length);
-      assertStartsWith(VALUE_CAKE, xattr_create);
-
-      try {
-        Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_CREATE);
-        fail("Expected EEXIST");
-      } catch (ErrnoException e) {
-        assertEquals(OsConstants.EEXIST, e.errno);
-      }
-
-      Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_REPLACE);
-      byte[] xattr_replace = Libcore.os.getxattr(path, NAME_TEST);
-      assertTrue(Arrays.asList(Libcore.os.listxattr(path)).contains(NAME_TEST));
-      assertEquals(VALUE_PIE.length, xattr_replace.length);
-      assertStartsWith(VALUE_PIE, xattr_replace);
-
-      Libcore.os.removexattr(path, NAME_TEST);
-      try {
-        Libcore.os.getxattr(path, NAME_TEST);
-        fail("Expected ENODATA");
-      } catch (ErrnoException e) {
-        assertEquals(OsConstants.ENODATA, e.errno);
-      }
-      assertFalse(Arrays.asList(Libcore.os.listxattr(path)).contains(NAME_TEST));
-
-    } finally {
-      file.delete();
-    }
-  }
-
-  public void test_xattr_NPE() throws Exception {
-    File file = File.createTempFile("xattr", "test");
-    final String path = file.getAbsolutePath();
-    final String NAME_TEST = "user.meow";
-    final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
-
-    // getxattr
-    try {
-      Libcore.os.getxattr(null, NAME_TEST);
-      fail();
-    } catch (NullPointerException expected) { }
-    try {
-      Libcore.os.getxattr(path, null);
-      fail();
-    } catch (NullPointerException expected) { }
-
-    // listxattr
-    try {
-      Libcore.os.listxattr(null);
-      fail();
-    } catch (NullPointerException expected) { }
-
-    // removexattr
-    try {
-      Libcore.os.removexattr(null, NAME_TEST);
-      fail();
-    } catch (NullPointerException expected) { }
-    try {
-      Libcore.os.removexattr(path, null);
-      fail();
-    } catch (NullPointerException expected) { }
-
-    // setxattr
-    try {
-      Libcore.os.setxattr(null, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
-      fail();
-    } catch (NullPointerException expected) { }
-    try {
-      Libcore.os.setxattr(path, null, VALUE_CAKE, OsConstants.XATTR_CREATE);
-      fail();
-    } catch (NullPointerException expected) { }
-    try {
-      Libcore.os.setxattr(path, NAME_TEST, null, OsConstants.XATTR_CREATE);
-      fail();
-    } catch (NullPointerException expected) { }
-  }
-
-  public void test_xattr_Errno() throws Exception {
-    final String NAME_TEST = "user.meow";
-    final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
-
-    // ENOENT, No such file or directory.
-    try {
-      Libcore.os.getxattr("", NAME_TEST);
-      fail();
-    } catch (ErrnoException e) {
-      assertEquals(ENOENT, e.errno);
-    }
-    try {
-      Libcore.os.listxattr("");
-      fail();
-    } catch (ErrnoException e) {
-      assertEquals(ENOENT, e.errno);
-    }
-    try {
-      Libcore.os.removexattr("", NAME_TEST);
-      fail();
-    } catch (ErrnoException e) {
-      assertEquals(ENOENT, e.errno);
-    }
-    try {
-      Libcore.os.setxattr("", NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
-      fail();
-    } catch (ErrnoException e) {
-      assertEquals(ENOENT, e.errno);
-    }
-
-    // ENOTSUP, Extended attributes are not supported by the filesystem, or are disabled.
-    // Since kernel version 4.9 (or some other version after 4.4), *xattr() methods
-    // may set errno to EACCESS instead. This behavior change is likely related to
-    // https://patchwork.kernel.org/patch/9294421/ which reimplemented getxattr, setxattr,
-    // and removexattr on top of generic handlers.
-    final String path = "/proc/self/stat";
-    try {
-      Libcore.os.setxattr(path, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
-      fail();
-    } catch (ErrnoException e) {
-      assertTrue("Unexpected errno: " + e.errno, e.errno == ENOTSUP || e.errno == EACCES);
-    }
-    try {
-      Libcore.os.getxattr(path, NAME_TEST);
-      fail();
-    } catch (ErrnoException e) {
-      assertEquals(ENOTSUP, e.errno);
-    }
-    try {
-      // Linux listxattr does not set errno.
-      Libcore.os.listxattr(path);
-    } catch (ErrnoException e) {
-      fail();
-    }
-    try {
-      Libcore.os.removexattr(path, NAME_TEST);
-      fail();
-    } catch (ErrnoException e) {
-      assertTrue("Unexpected errno: " + e.errno, e.errno == ENOTSUP || e.errno == EACCES);
-    }
-  }
-
-  public void test_realpath() throws Exception {
-      File tmpDir = new File(System.getProperty("java.io.tmpdir"));
-      // This is a chicken and egg problem. We have no way of knowing whether
-      // the temporary directory or one of its path elements were symlinked, so
-      // we'll need this call to realpath.
-      String canonicalTmpDir = Libcore.os.realpath(tmpDir.getAbsolutePath());
-
-      // Test that "." and ".." are resolved correctly.
-      assertEquals(canonicalTmpDir,
-          Libcore.os.realpath(canonicalTmpDir + "/./../" + tmpDir.getName()));
-
-      // Test that symlinks are resolved correctly.
-      File target = new File(tmpDir, "target");
-      File link = new File(tmpDir, "link");
-      try {
-          assertTrue(target.createNewFile());
-          Libcore.os.symlink(target.getAbsolutePath(), link.getAbsolutePath());
-
-          assertEquals(canonicalTmpDir + "/target",
-              Libcore.os.realpath(canonicalTmpDir + "/link"));
-      } finally {
-          boolean deletedTarget = target.delete();
-          boolean deletedLink = link.delete();
-          // Asserting this here to provide a definitive reason for
-          // a subsequent failure on the same run.
-          assertTrue("deletedTarget = " + deletedTarget + ", deletedLink =" + deletedLink,
-              deletedTarget && deletedLink);
-      }
-  }
-
-  /**
-   * Tests that TCP_USER_TIMEOUT can be set on a TCP socket, but doesn't test
-   * that it behaves as expected.
-   */
-  public void test_socket_tcpUserTimeout_setAndGet() throws Exception {
-    final FileDescriptor fd = Libcore.os.socket(AF_INET, SOCK_STREAM, 0);
-    try {
-      int v = Libcore.os.getsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_USER_TIMEOUT);
-      assertEquals(0, v); // system default value
-      int newValue = 3000;
-      Libcore.os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_USER_TIMEOUT,
-              newValue);
-      int actualValue = Libcore.os.getsockoptInt(fd, OsConstants.IPPROTO_TCP,
-              OsConstants.TCP_USER_TIMEOUT);
-      // The kernel can round the requested value based on the HZ setting. We allow up to 10ms
-      // difference.
-      assertTrue("Returned incorrect timeout:" + actualValue,
-              Math.abs(newValue - actualValue) <= 10);
-      // No need to reset the value to 0, since we're throwing the socket away
-    } finally {
-      Libcore.os.close(fd);
-    }
-  }
-
-  public void test_socket_tcpUserTimeout_doesNotWorkOnDatagramSocket() throws Exception {
-    final FileDescriptor fd = Libcore.os.socket(AF_INET, SOCK_DGRAM, 0);
-    try {
-      Libcore.os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_USER_TIMEOUT,
-              3000);
-      fail("datagram (connectionless) sockets shouldn't support TCP_USER_TIMEOUT");
-    } catch (ErrnoException expected) {
-      // expected
-    } finally {
-      Libcore.os.close(fd);
-    }
-  }
-
-  public void test_socket_sockoptTimeval_readWrite() throws Exception {
-    FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
-    try {
-      StructTimeval v = Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO);
-      assertEquals(0, v.toMillis()); // system default value
-
-      StructTimeval newValue = StructTimeval.fromMillis(3000);
-      Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, newValue);
-
-      StructTimeval actualValue = Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO);
-
-      // The kernel can round the requested value based on the HZ setting. We allow up to 10ms
-      // difference.
-      assertTrue("Returned incorrect timeout:" + actualValue,
-          Math.abs(newValue.toMillis() - actualValue.toMillis()) <= 10);
-      // No need to reset the value to 0, since we're throwing the socket away
-    } finally {
-      Libcore.os.close(fd);
-    }
-  }
-
-  public void test_socket_setSockoptTimeval_effective() throws Exception {
-    int timeoutValueMillis = 50;
-    int allowedTimeoutMillis = 500;
-
-    FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
-    try {
-      StructTimeval tv = StructTimeval.fromMillis(timeoutValueMillis);
-      Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv);
-      Libcore.os.bind(fd, InetAddress.getByName("::1"), 0);
-
-      byte[] request = new byte[1];
-      long startTime = System.nanoTime();
-      expectException(() -> Libcore.os.read(fd, request, 0, request.length),
-          ErrnoException.class, EAGAIN, "Expected timeout");
-      long endTime = System.nanoTime();
-      assertTrue(Duration.ofNanos(endTime - startTime).toMillis() < allowedTimeoutMillis);
-    } finally {
-      Libcore.os.close(fd);
-    }
-  }
-
-  public void test_socket_setSockoptTimeval_nullFd() throws Exception {
-    StructTimeval tv = StructTimeval.fromMillis(500);
-    expectException(
-        () -> Libcore.os.setsockoptTimeval(null, SOL_SOCKET, SO_RCVTIMEO, tv),
-        ErrnoException.class, EBADF, "setsockoptTimeval(null, ...)");
-  }
-
-  public void test_socket_setSockoptTimeval_fileFd() throws Exception {
-    File testFile = createTempFile("test_socket_setSockoptTimeval_invalidFd", "");
-    try (FileInputStream fis = new FileInputStream(testFile)) {
-      final FileDescriptor fd = fis.getFD();
-
-      StructTimeval tv = StructTimeval.fromMillis(500);
-      expectException(
-          () -> Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv),
-          ErrnoException.class, ENOTSOCK, "setsockoptTimeval(<file fd>, ...)");
-    }
-  }
-
-  public void test_socket_setSockoptTimeval_badFd() throws Exception {
-    StructTimeval tv = StructTimeval.fromMillis(500);
-    FileDescriptor invalidFd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
-    Libcore.os.close(invalidFd);
-
-    expectException(
-        () -> Libcore.os.setsockoptTimeval(invalidFd, SOL_SOCKET, SO_RCVTIMEO, tv),
-        ErrnoException.class, EBADF, "setsockoptTimeval(<closed fd>, ...)");
-  }
-
-  public void test_socket_setSockoptTimeval_invalidLevel() throws Exception {
-    StructTimeval tv = StructTimeval.fromMillis(500);
-    FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
-    try {
-      expectException(
-          () -> Libcore.os.setsockoptTimeval(fd, -1, SO_RCVTIMEO, tv),
-          ErrnoException.class, ENOPROTOOPT, "setsockoptTimeval(fd, <invalid level>, ...)");
-    } finally {
-      Libcore.os.close(fd);
-    }
-  }
-
-  public void test_socket_setSockoptTimeval_invalidOpt() throws Exception {
-    StructTimeval tv = StructTimeval.fromMillis(500);
-    FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
-    try {
-      expectException(
-          () -> Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, -1, tv),
-          ErrnoException.class, ENOPROTOOPT, "setsockoptTimeval(fd, <invalid level>, ...)");
-    } finally {
-      Libcore.os.close(fd);
-    }
-  }
-
-  public void test_socket_setSockoptTimeval_nullTimeVal() throws Exception {
-      FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
-      try {
-        expectException(
-            () -> Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, null),
-            NullPointerException.class, null, "setsockoptTimeval(..., null)");
-      } finally {
-          Libcore.os.close(fd);
-      }
-  }
-
-  public void test_socket_getSockoptTimeval_invalidOption() throws Exception {
-      FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
-      try {
-        expectException(
-            () -> Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_DEBUG),
-            IllegalArgumentException.class, null, "getsockoptTimeval(..., <non-timeval option>)");
-      } finally {
-          Libcore.os.close(fd);
-      }
-  }
-
-  public void test_if_nametoindex_if_indextoname() throws Exception {
-    List<NetworkInterface> nis = Collections.list(NetworkInterface.getNetworkInterfaces());
-
-    assertTrue(nis.size() > 0);
-    for (NetworkInterface ni : nis) {
-      int index = ni.getIndex();
-      String name = ni.getName();
-      assertEquals(index, Libcore.os.if_nametoindex(name));
-      assertTrue(Libcore.os.if_indextoname(index).equals(name));
-    }
-
-    assertEquals(0, Libcore.os.if_nametoindex("this-interface-does-not-exist"));
-    assertEquals(null, Libcore.os.if_indextoname(-1000));
-
-    try {
-      Libcore.os.if_nametoindex(null);
-      fail();
-    } catch (NullPointerException expected) { }
-  }
-
-  private static void assertStartsWith(byte[] expectedContents, byte[] container) {
-    for (int i = 0; i < expectedContents.length; i++) {
-      if (expectedContents[i] != container[i]) {
-        fail("Expected " + Arrays.toString(expectedContents) + " but found "
-            + Arrays.toString(expectedContents));
-      }
-    }
-  }
-
-  public void test_readlink() throws Exception {
-    File path = new File(IoUtils.createTemporaryDirectory("test_readlink"), "symlink");
-
-    // ext2 and ext4 have PAGE_SIZE limits on symlink targets.
-    // If file encryption is enabled, there's extra overhead to store the
-    // size of the encrypted symlink target. There's also an off-by-one
-    // in current kernels (and marlin/sailfish where we're seeing this
-    // failure are still on 3.18, far from current). Given that we don't
-    // really care here, just use 2048 instead. http://b/33306057.
-    int size = 2048;
-    String xs = "";
-    for (int i = 0; i < size - 1; ++i) xs += "x";
-
-    Libcore.os.symlink(xs, path.getPath());
-
-    assertEquals(xs, Libcore.os.readlink(path.getPath()));
-  }
-
-  // Address should be correctly set for empty packets. http://b/33481605
-  public void test_recvfrom_EmptyPacket() throws Exception {
-    try (DatagramSocket ds = new DatagramSocket();
-         DatagramSocket srcSock = new DatagramSocket()) {
-      srcSock.send(new DatagramPacket(new byte[0], 0, ds.getLocalSocketAddress()));
-
-      byte[] recvBuf = new byte[16];
-      InetSocketAddress address = new InetSocketAddress();
-      int recvCount =
-          android.system.Os.recvfrom(ds.getFileDescriptor$(), recvBuf, 0, 16, 0, address);
-      assertEquals(0, recvCount);
-      assertTrue(address.getAddress().isLoopbackAddress());
-      assertEquals(srcSock.getLocalPort(), address.getPort());
-    }
-  }
-
-  public void test_fstat_times() throws Exception {
-    File file = File.createTempFile("OsTest", "fstattest");
-    FileOutputStream fos = new FileOutputStream(file);
-    StructStat structStat1 = Libcore.os.fstat(fos.getFD());
-    assertEquals(structStat1.st_mtim.tv_sec, structStat1.st_mtime);
-    assertEquals(structStat1.st_ctim.tv_sec, structStat1.st_ctime);
-    assertEquals(structStat1.st_atim.tv_sec, structStat1.st_atime);
-    Thread.sleep(100);
-    fos.write(new byte[]{1,2,3});
-    fos.flush();
-    StructStat structStat2 = Libcore.os.fstat(fos.getFD());
-    fos.close();
-
-    assertEquals(-1, structStat1.st_mtim.compareTo(structStat2.st_mtim));
-    assertEquals(-1, structStat1.st_ctim.compareTo(structStat2.st_ctim));
-    assertEquals(0, structStat1.st_atim.compareTo(structStat2.st_atim));
-  }
-
-  public void test_getrlimit() throws Exception {
-    StructRlimit rlimit = Libcore.os.getrlimit(OsConstants.RLIMIT_NOFILE);
-    // We can't really make any assertions about these values since they might vary from
-    // device to device and even process to process. We do know that they will be greater
-    // than zero, though.
-    assertTrue(rlimit.rlim_cur > 0);
-    assertTrue(rlimit.rlim_max > 0);
-  }
-
-  // http://b/65051835
-  public void test_pipe2_errno() throws Exception {
-    try {
-        // flag=-1 is not a valid value for pip2, will EINVAL
-        Libcore.os.pipe2(-1);
-        fail();
-    } catch(ErrnoException expected) {
-    }
-  }
-
-  // http://b/65051835
-  public void test_sendfile_errno() throws Exception {
-    try {
-        // FileDescriptor.out is not open for input, will cause EBADF
-        Int64Ref offset = new Int64Ref(10);
-        Libcore.os.sendfile(FileDescriptor.out, FileDescriptor.out, offset, 10);
-        fail();
-    } catch(ErrnoException expected) {
-    }
-  }
-
-  public void test_sendfile_null() throws Exception {
-    File in = createTempFile("test_sendfile_null", "Hello, world!");
-    try {
-      int len = "Hello".length();
-      assertEquals("Hello", checkSendfile(ANDROID_SYSTEM_OS_INT64_REF, in, null, len, null));
-      assertEquals("Hello", checkSendfile(LIBCORE_OS, in, null, len, null));
-    } finally {
-      in.delete();
-    }
-  }
-
-  public void test_sendfile_offset() throws Exception {
-    File in = createTempFile("test_sendfile_offset", "Hello, world!");
-    try {
-      // checkSendfile(sendFileImplToUse, in, startOffset, maxBytes, expectedEndOffset)
-
-      assertEquals("Hello", checkSendfile(ANDROID_SYSTEM_OS_INT64_REF, in, 0L, 5, 5L));
-      assertEquals("Hello", checkSendfile(LIBCORE_OS, in, 0L, 5, 5L));
-
-      assertEquals("ello,", checkSendfile(ANDROID_SYSTEM_OS_INT64_REF, in, 1L, 5, 6L));
-      assertEquals("ello,", checkSendfile(LIBCORE_OS, in, 1L, 5, 6L));
-
-      // At offset 9, only 4 bytes/chars available, even though we're asking for 5.
-      assertEquals("rld!", checkSendfile(ANDROID_SYSTEM_OS_INT64_REF, in, 9L, 5, 13L));
-      assertEquals("rld!", checkSendfile(LIBCORE_OS, in, 9L, 5, 13L));
-
-      assertEquals("", checkSendfile(ANDROID_SYSTEM_OS_INT64_REF, in, 1L, 0, 1L));
-      assertEquals("", checkSendfile(LIBCORE_OS, in, 1L, 0, 1L));
-    } finally {
-      in.delete();
-    }
-  }
-
-  /** Which of the {@code sendfile()} implementations to use. */
-  enum SendFileImpl {
-    ANDROID_SYSTEM_OS_INT64_REF,
-    LIBCORE_OS
-  }
-
-  private static String checkSendfile(SendFileImpl sendFileImplToUse, File in, Long startOffset,
-          int maxBytes, Long expectedEndOffset) throws IOException, ErrnoException {
-    File out = File.createTempFile(OsTest.class.getSimpleName() + "_checkSendFile_" +
-            sendFileImplToUse, ".out");
-    try (FileInputStream inStream = new FileInputStream(in)) {
-      FileDescriptor inFd = inStream.getFD();
-      try (FileOutputStream outStream = new FileOutputStream(out)) {
-        FileDescriptor outFd = outStream.getFD();
-        switch (sendFileImplToUse) {
-          case ANDROID_SYSTEM_OS_INT64_REF: {
-            Int64Ref offset = (startOffset == null) ? null : new Int64Ref(startOffset);
-            android.system.Os.sendfile(outFd, inFd, offset, maxBytes);
-            assertEquals(expectedEndOffset, offset == null ? null : offset.value);
-            break;
-          }
-          case LIBCORE_OS: {
-            Int64Ref offset = (startOffset == null) ? null : new Int64Ref(startOffset);
-            libcore.io.Libcore.os.sendfile(outFd, inFd, offset, maxBytes);
-            assertEquals(expectedEndOffset, offset == null ? null : offset.value);
-            break;
-          }
-          default: {
+            Os.compareAndSetDefault(defaultOs, null);
             fail();
-            break;
-          }
+        } catch (NullPointerException expected) {
         }
-      }
-      return IoUtils.readFileAsString(out.getPath());
-    } finally {
-      out.delete();
-    }
-  }
-
-  private static File createTempFile(String namePart, String contents) throws IOException {
-    File f = File.createTempFile(OsTest.class.getSimpleName() + namePart, ".in");
-    try (FileWriter writer = new FileWriter(f)) {
-      writer.write(contents);
-    }
-    return f;
-  }
-
-  public void test_odirect() throws Exception {
-    File testFile = createTempFile("test_odirect", "");
-    try {
-      FileDescriptor fd =
-            Libcore.os.open(testFile.toString(), O_WRONLY | O_DIRECT, S_IRUSR | S_IWUSR);
-      assertNotNull(fd);
-      assertTrue(fd.valid());
-      int flags = Libcore.os.fcntlVoid(fd, F_GETFL);
-      assertTrue("Expected file flags to include " + O_DIRECT + ", actual value: " + flags,
-            0 != (flags & O_DIRECT));
-      Libcore.os.close(fd);
-    } finally {
-      testFile.delete();
-    }
-  }
-
-  public void test_splice() throws Exception {
-    FileDescriptor[] pipe = Libcore.os.pipe2(0);
-    File in = createTempFile("splice1", "foobar");
-    File out = createTempFile("splice2", "");
-
-    Int64Ref offIn = new Int64Ref(1);
-    Int64Ref offOut = new Int64Ref(0);
-
-    // Splice into pipe
-    try (FileInputStream streamIn = new FileInputStream(in)) {
-      FileDescriptor fdIn = streamIn.getFD();
-      long result = Libcore.os.splice(fdIn, offIn, pipe[1], null /* offOut */ , 10 /* len */, 0 /* flags */);
-      assertEquals(5, result);
-      assertEquals(6, offIn.value);
+        // value hasn't changed
+        assertSame(defaultOs, Os.getDefault());
     }
 
-    // Splice from pipe
-    try (FileOutputStream streamOut = new FileOutputStream(out)) {
-      FileDescriptor fdOut = streamOut.getFD();
-      long result = Libcore.os.splice(pipe[0], null /* offIn */, fdOut, offOut, 10 /* len */, 0 /* flags */);
-      assertEquals(5, result);
-      assertEquals(5, offOut.value);
+    public void testCompareAndSetDefault_comparisonFailure() throws Exception {
+        Os defaultOs = Os.getDefault();
+        Os otherOs = new ForwardingOs(defaultOs) { };
+
+        // current default is non-null, but expect is null
+        assertFalse(Os.compareAndSetDefault(null, otherOs));
+        assertSame(defaultOs, Os.getDefault());
+
+        // current default != expect (both non-null)
+        assertFalse(Os.compareAndSetDefault(otherOs, otherOs));
+        assertSame(defaultOs, Os.getDefault());
     }
-
-    assertEquals("oobar", IoUtils.readFileAsString(out.getPath()));
-
-    Libcore.os.close(pipe[0]);
-    Libcore.os.close(pipe[1]);
-  }
-
-  public void test_splice_errors() throws Exception {
-    File in = createTempFile("splice3", "");
-    File out = createTempFile("splice4", "");
-    FileDescriptor[] pipe = Libcore.os.pipe2(0);
-
-    //.fdIn == null
-    try {
-      Libcore.os.splice(null /* fdIn */, null /* offIn */, pipe[1],
-          null /*offOut*/, 10 /* len */, 0 /* flags */);
-      fail();
-    } catch(ErrnoException expected) {
-      assertEquals(EBADF, expected.errno);
-    }
-
-    //.fdOut == null
-    try {
-      Libcore.os.splice(pipe[0] /* fdIn */, null /* offIn */, null  /* fdOut */,
-          null /*offOut*/, 10 /* len */, 0 /* flags */);
-      fail();
-    } catch(ErrnoException expected) {
-      assertEquals(EBADF, expected.errno);
-    }
-
-    // No pipe fd
-    try (FileOutputStream streamOut = new FileOutputStream(out)) {
-      try (FileInputStream streamIn = new FileInputStream(in)) {
-        FileDescriptor fdIn = streamIn.getFD();
-        FileDescriptor fdOut = streamOut.getFD();
-        Libcore.os.splice(fdIn, null  /* offIn */, fdOut, null /* offOut */, 10 /* len */, 0 /* flags */);
-        fail();
-      } catch(ErrnoException expected) {
-        assertEquals(EINVAL, expected.errno);
-      }
-    }
-
-    Libcore.os.close(pipe[0]);
-    Libcore.os.close(pipe[1]);
-  }
-
-  public void testCloseNullFileDescriptor() throws Exception {
-    try {
-      Libcore.rawOs.close(null);
-      fail();
-    } catch (NullPointerException expected) {
-    }
-  }
-
-  public void testSocketpairNullFileDescriptor1() throws Exception {
-    try {
-      Libcore.rawOs.socketpair(AF_UNIX, SOCK_STREAM, 0, null, new FileDescriptor());
-      fail();
-    } catch (NullPointerException expected) {
-    }
-  }
-
-  public void testSocketpairNullFileDescriptor2() throws Exception {
-    try {
-      Libcore.rawOs.socketpair(AF_UNIX, SOCK_STREAM, 0, new FileDescriptor(), null);
-      fail();
-    } catch (NullPointerException expected) {
-    }
-  }
-
-  public void testSocketpairNullFileDescriptorBoth() throws Exception {
-    try {
-      Libcore.rawOs.socketpair(AF_UNIX, SOCK_STREAM, 0, null, null);
-      fail();
-    } catch (NullPointerException expected) {
-    }
-  }
-
-  public void testGetDefault_instanceofBlockguardOs() {
-    Os os = Os.getDefault();
-    assertTrue(os.getClass().toString(), os instanceof BlockGuardOs);
-  }
-
-  public void testCompareAndSetDefault_success() throws Exception {
-    Os defaultOs = Os.getDefault();
-    Os mockOs = Mockito.mock(Os.class);
-    try {
-      // There shouldn't be any concurrent threads replacing the default Os.
-      assertTrue(Os.compareAndSetDefault(defaultOs, mockOs));
-      assertSame(mockOs, Os.getDefault());
-
-      // Calls to android.system.Os should now reach our custom Os instance.
-      android.system.Os.rename("/old/path", "/new/path");
-      Mockito.verify(mockOs).rename("/old/path", "/new/path");
-    } finally {
-      assertTrue(Os.compareAndSetDefault(mockOs, defaultOs));
-      assertSame(defaultOs, Os.getDefault());
-    }
-  }
-
-  public void testCompareandSetDefault_null() {
-    Os defaultOs = Os.getDefault();
-    // update == null is not allowed
-    try {
-      Os.compareAndSetDefault(defaultOs, null);
-      fail();
-    } catch (NullPointerException expected) {
-    }
-    // value hasn't changed
-    assertSame(defaultOs, Os.getDefault());
-
-  }
-
-  public void testCompareAndSetDefault_comparisonFailure() throws Exception {
-    Os defaultOs = Os.getDefault();
-    Os otherOs = new ForwardingOs(defaultOs) { };
-
-    // current default is non-null, but expect is null
-    assertFalse(Os.compareAndSetDefault(null, otherOs));
-    assertSame(defaultOs, Os.getDefault());
-
-    // current default != expect (both non-null)
-    assertFalse(Os.compareAndSetDefault(otherOs, otherOs));
-    assertSame(defaultOs, Os.getDefault());
-  }
-
-  public void testInetPtonIpv4() {
-    String srcAddress = "127.0.0.1";
-    InetAddress inetAddress = Libcore.rawOs.inet_pton(AF_INET, srcAddress);
-    assertEquals(srcAddress, inetAddress.getHostAddress());
-  }
-
-  public void testInetPtonIpv6() {
-    String srcAddress = "1123:4567:89ab:cdef:fedc:ba98:7654:3210";
-    InetAddress inetAddress = Libcore.rawOs.inet_pton(AF_INET6, srcAddress);
-    assertEquals(srcAddress, inetAddress.getHostAddress());
-  }
-
-  public void testInetPtonInvalidFamily() {
-    String srcAddress = "127.0.0.1";
-    InetAddress inetAddress = Libcore.rawOs.inet_pton(AF_UNIX, srcAddress);
-    assertNull(inetAddress);
-  }
-
-  public void testInetPtonWrongFamily() {
-    String srcAddress = "127.0.0.1";
-    InetAddress inetAddress = Libcore.rawOs.inet_pton(AF_INET6, srcAddress);
-    assertNull(inetAddress);
-  }
-
-  public void testInetPtonInvalidData() {
-    String srcAddress = "10.1";
-    InetAddress inetAddress = Libcore.rawOs.inet_pton(AF_INET, srcAddress);
-    assertNull(inetAddress);
-  }
 }
diff --git a/luni/src/test/java/libcore/libcore/net/MimeUtilsTest.java b/luni/src/test/java/libcore/libcore/net/MimeUtilsTest.java
index cf1e233..26be32b 100644
--- a/luni/src/test/java/libcore/libcore/net/MimeUtilsTest.java
+++ b/luni/src/test/java/libcore/libcore/net/MimeUtilsTest.java
@@ -90,8 +90,13 @@
         assertEquals("video/ogg", MimeUtils.guessMimeTypeFromExtension("ogv"));
     }
 
-    public void test_70851634() {
-        assertEquals("application/vnd.youtube.yt", MimeUtils.guessMimeTypeFromExtension("yt"));
+    public void test_70851634_mimeTypeFromExtension() {
+        assertEquals("video/vnd.youtube.yt", MimeUtils.guessMimeTypeFromExtension("yt"));
+    }
+
+    public void test_70851634_extensionFromMimeType() {
+        assertEquals("yt", MimeUtils.guessExtensionFromMimeType("video/vnd.youtube.yt"));
+        assertEquals("yt", MimeUtils.guessExtensionFromMimeType("application/vnd.youtube.yt"));
     }
 
     public void test_112162449_audio() {
diff --git a/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java b/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java
index 4de1a01..800928f 100644
--- a/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java
+++ b/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java
@@ -18,28 +18,69 @@
 
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.Locale;
+
 import junit.framework.TestCase;
 import static libcore.util.HexEncoding.decode;
 import static libcore.util.HexEncoding.encode;
+import static libcore.util.HexEncoding.encodeToString;
 
 public class HexEncodingTest extends TestCase {
-  public void testEncode() {
-    final byte[] avocados = "avocados".getBytes(StandardCharsets.UTF_8);
 
-    assertArraysEqual("61766F6361646F73".toCharArray(), encode(avocados));
-    assertArraysEqual(avocados, decode(encode(avocados), false));
-    // Make sure we can handle lower case hex encodings as well.
-    assertArraysEqual(avocados, decode("61766f6361646f73".toCharArray(), false));
+  public void testEncodeByte() {
+    Object[][] testCases = new Object[][] {
+        { 0x01, "01" },
+        { 0x09, "09" },
+        { 0x0A, "0A" },
+        { 0x0F, "0F" },
+        { 0x10, "10" },
+        { 0x1F, "1F" },
+        { 0x20, "20" },
+        { 0x7F, "7F" },
+        { 0x80, "80" },
+        { 0xFF, "FF" },
+    };
+    for (Object[] testCase : testCases) {
+      Number toEncode = (Number) testCase[0];
+      String expected = (String) testCase[1];
+
+      String actualUpper = encodeToString(toEncode.byteValue(), true /* upperCase */);
+      assertEquals(upper(expected), actualUpper);
+
+      String actualLower = encodeToString(toEncode.byteValue(), false /* upperCase */);
+      assertEquals(lower(expected), actualLower);
+    }
+  }
+
+  public void testEncodeBytes() {
+    Object[][] testCases = new Object[][] {
+        { "avocados".getBytes(StandardCharsets.UTF_8), "61766F6361646F73" },
+    };
+
+    for (Object[] testCase : testCases) {
+      byte[] bytes = (byte[]) testCase[0];
+      String encodedLower = lower((String) testCase[1]);
+      String encodedUpper = upper((String) testCase[1]);
+
+      assertArraysEqual(encodedUpper.toCharArray(), encode(bytes));
+      assertArraysEqual(encodedUpper.toCharArray(), encode(bytes, true /* upperCase */));
+      assertArraysEqual(encodedLower.toCharArray(), encode(bytes, false /* upperCase */));
+
+      assertArraysEqual(bytes, decode(encode(bytes), false /* allowSingleChar */));
+
+      // Make sure we can handle lower case hex encodings as well.
+      assertArraysEqual(bytes, decode(encodedLower.toCharArray(), false /* allowSingleChar */));
+    }
   }
 
   public void testDecode_allow4Bit() {
     assertArraysEqual(new byte[] { 6 }, decode("6".toCharArray(), true));
-    assertArraysEqual(new byte[] { 6, 'v' }, decode("676".toCharArray(), true));
+    assertArraysEqual(new byte[] { 6, 0x76 }, decode("676".toCharArray(), true));
   }
 
   public void testDecode_disallow4Bit() {
     try {
-      decode("676".toCharArray(), false);
+      decode("676".toCharArray(), false /* allowSingleChar */);
       fail();
     } catch (IllegalArgumentException expected) {
     }
@@ -47,7 +88,7 @@
 
   public void testDecode_invalid() {
     try {
-      decode("DEADBARD".toCharArray(), false);
+      decode("DEADBARD".toCharArray(), false /* allowSingleChar */);
       fail();
     } catch (IllegalArgumentException expected) {
     }
@@ -56,13 +97,13 @@
     // commons uses Character.isDigit and would successfully decode a string with
     // arabic and devanagari characters.
     try {
-      decode("६१٧٥٥F6361646F73".toCharArray(), false);
+      decode("६१٧٥٥F6361646F73".toCharArray(), false /* allowSingleChar */);
       fail();
     } catch (IllegalArgumentException expected) {
     }
 
     try {
-      decode("#%6361646F73".toCharArray(), false);
+      decode("#%6361646F73".toCharArray(), false /* allowSingleChar */);
       fail();
     } catch (IllegalArgumentException expected) {
     }
@@ -75,4 +116,12 @@
   private static void assertArraysEqual(byte[] lhs, byte[] rhs) {
     assertEquals(Arrays.toString(lhs), Arrays.toString(rhs));
   }
+
+  private static String lower(String string) {
+    return string.toLowerCase(Locale.ROOT);
+  }
+
+  private static String upper(String string) {
+    return string.toUpperCase(Locale.ROOT);
+  }
 }
diff --git a/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java b/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java
index a151ee4..cddf41b 100644
--- a/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java
+++ b/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java
@@ -322,6 +322,7 @@
    * <p>Newer versions of zic after 2014b introduce an explicit transition at the earliest
    * representable time, which is Integer.MIN_VALUE for TZif version 1 files. Previously the type
    * used was left implicit and readers were expected to use the first non-DST type in the file.
+   * This extra transition mostly went away again with zic 2018f.
    *
    * <p>Testing newer zic versions demonstrated that Android had been mishandling the lookup of
    * offset for times before the first transition. The logic has been corrected. This test would
@@ -357,8 +358,8 @@
             { offsetToSeconds(type2Offset), 0 },
     };
 
-    // Creates a simulation of zic version <= 2014b where there is usually no explicit transition at
-    // Integer.MIN_VALUE seconds in TZif version 1 data.
+    // Creates a simulation of zic version <= 2014b or zic version >= 2018f where there is often
+    // no explicit transition at Integer.MIN_VALUE seconds in TZif version 1 data.
     {
       int[][] transitions = {
               { timeToSeconds(firstRealTransitionTime), 2 /* type 2 */ },
@@ -374,8 +375,8 @@
       assertOffsetAt(oldZoneInfo, type2Offset, afterFirstRealTransitionTimes);
     }
 
-    // Creates a simulation of zic version > 2014b where there is usually an explicit transition at
-    // Integer.MIN_VALUE seconds for TZif version 1 data.
+    // Creates a simulation of zic version > 2014b and zic version < 2018f where there is usually an
+    // explicit transition at Integer.MIN_VALUE seconds for TZif version 1 data.
     {
       int[][] transitions = {
               { Integer.MIN_VALUE, 1 /* type 1 */ }, // The extra transition added by zic.
diff --git a/mmodules/core_platform_api/api/platform/current-api.txt b/mmodules/core_platform_api/api/platform/current-api.txt
index ccfec23..ec28ddf 100644
--- a/mmodules/core_platform_api/api/platform/current-api.txt
+++ b/mmodules/core_platform_api/api/platform/current-api.txt
@@ -743,6 +743,7 @@
     method public void setHiddenApiExemptions(String[]);
     method public static void setHiddenApiUsageLogger(dalvik.system.VMRuntime.HiddenApiUsageLogger);
     method public static void setNonSdkApiUsageConsumer(java.util.function.Consumer<java.lang.String>);
+    method public static void setProcessDataDirectory(String);
     method public static void setProcessPackageName(String);
     method @dalvik.annotation.compat.UnsupportedAppUsage public float setTargetHeapUtilization(float);
     method public void setTargetSdkVersion(int);
@@ -1225,8 +1226,11 @@
     method public static byte[] decode(char[]) throws java.lang.IllegalArgumentException;
     method public static byte[] decode(char[], boolean) throws java.lang.IllegalArgumentException;
     method public static char[] encode(byte[]);
+    method public static char[] encode(byte[], boolean);
     method public static char[] encode(byte[], int, int);
+    method public static String encodeToString(byte, boolean);
     method public static String encodeToString(byte[]);
+    method public static String encodeToString(byte[], boolean);
   }
 
   public class NativeAllocationRegistry {
diff --git a/ojluni/src/main/java/java/lang/System.java b/ojluni/src/main/java/java/lang/System.java
index 8235bf7..d698446 100644
--- a/ojluni/src/main/java/java/lang/System.java
+++ b/ojluni/src/main/java/java/lang/System.java
@@ -509,13 +509,9 @@
      * The byte[] specialized version of arraycopy().
      * Note: This method is required for runtime ART compiler optimizations.
      * Do not remove or change the signature.
-     * Note: Unlike the others, this variant is public due to a dependency we
-     * are working on removing. b/74103559
-     *
-     * @hide
      */
     @SuppressWarnings("unused")
-    public static void arraycopy(byte[] src, int srcPos, byte[] dst, int dstPos, int length) {
+    private static void arraycopy(byte[] src, int srcPos, byte[] dst, int dstPos, int length) {
         if (src == null) {
             throw new NullPointerException("src == null");
         }
diff --git a/ojluni/src/main/java/java/net/Inet6AddressImpl.java b/ojluni/src/main/java/java/net/Inet6AddressImpl.java
index 6b22f8c..bb722f3 100644
--- a/ojluni/src/main/java/java/net/Inet6AddressImpl.java
+++ b/ojluni/src/main/java/java/net/Inet6AddressImpl.java
@@ -44,6 +44,7 @@
 import static android.system.OsConstants.AI_ADDRCONFIG;
 import static android.system.OsConstants.EACCES;
 import static android.system.OsConstants.ECONNREFUSED;
+import static android.system.OsConstants.EPERM;
 import static android.system.OsConstants.NI_NAMEREQD;
 import static android.system.OsConstants.ICMP6_ECHO_REPLY;
 import static android.system.OsConstants.ICMP_ECHOREPLY;
@@ -144,7 +145,8 @@
             // SecurityException to aid in debugging this common mistake.
             // http://code.google.com/p/android/issues/detail?id=15722
             if (gaiException.getCause() instanceof ErrnoException) {
-                if (((ErrnoException) gaiException.getCause()).errno == EACCES) {
+                int errno = ((ErrnoException) gaiException.getCause()).errno;
+                if (errno == EACCES || errno == EPERM) {
                     throw new SecurityException("Permission denied (missing INTERNET permission?)", gaiException);
                 }
             }
diff --git a/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java b/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java
index c569981..67507bc 100644
--- a/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java
+++ b/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java
@@ -1574,14 +1574,17 @@
             zoneOffsets = new int[2];
         }
         if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
-            // Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
+            // BEGIN Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
             // if (tz instanceof ZoneInfo) {
             //     zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets);
-            // } else {
+            if (tz instanceof libcore.util.ZoneInfo) {
+                zoneOffset = ((libcore.util.ZoneInfo)tz).getOffsetsByUtcTime(time, zoneOffsets);
+            // END Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
+            } else {
                 zoneOffset = tz.getOffset(time);
                 zoneOffsets[0] = tz.getRawOffset();
                 zoneOffsets[1] = zoneOffset - zoneOffsets[0];
-            // }
+            }
         }
         if (tzMask != 0) {
             if (isFieldSet(tzMask, ZONE_OFFSET)) {
diff --git a/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java b/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java
index 143fa5e..b71d11a 100644
--- a/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java
+++ b/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java
@@ -188,7 +188,6 @@
      */
     private static class NoPreloadHolder {
         public static HostnameVerifier defaultHostnameVerifier;
-        public static final Class<? extends HostnameVerifier> originalDefaultHostnameVerifierClass;
         static {
             try {
                 /**
@@ -200,7 +199,6 @@
                 defaultHostnameVerifier = (HostnameVerifier)
                         Class.forName("com.android.okhttp.internal.tls.OkHostnameVerifier")
                         .getField("INSTANCE").get(null);
-                originalDefaultHostnameVerifierClass = defaultHostnameVerifier.getClass();
             } catch (Exception e) {
                 throw new AssertionError("Failed to obtain okhttp HostnameVerifier", e);
             }
@@ -210,7 +208,7 @@
     /**
      * The <code>hostnameVerifier</code> for this object.
      */
-    protected HostnameVerifier hostnameVerifier;
+    protected HostnameVerifier hostnameVerifier = NoPreloadHolder.defaultHostnameVerifier;
     // END Android-changed: Use holder class idiom for a lazily-created OkHttp hostname verifier.
 
     // Android-changed: Modified the documentation to explain side effects / discourage method use.
@@ -329,15 +327,6 @@
      * @see #setDefaultHostnameVerifier(HostnameVerifier)
      */
     public HostnameVerifier getHostnameVerifier() {
-        // Android-added: Use the default verifier if none is set.
-        // Note that this also has the side effect of *setting* (if unset)
-        // hostnameVerifier to be the default one. It's not clear why this
-        // was done (commit abd00f0eaa46f71f98e75a631c268c812d1ec7c1) but
-        // we're keeping this behavior for lack of a strong reason to do
-        // otherwise.
-        if (hostnameVerifier == null) {
-            hostnameVerifier = NoPreloadHolder.defaultHostnameVerifier;
-        }
         return hostnameVerifier;
     }
 
diff --git a/ojluni/src/main/java/sun/util/calendar/AbstractCalendar.java b/ojluni/src/main/java/sun/util/calendar/AbstractCalendar.java
index ead18de..28b10c8 100644
--- a/ojluni/src/main/java/sun/util/calendar/AbstractCalendar.java
+++ b/ojluni/src/main/java/sun/util/calendar/AbstractCalendar.java
@@ -126,12 +126,14 @@
             // BEGIN Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
             // if (zi instanceof ZoneInfo) {
             //    zoneOffset = ((ZoneInfo)zi).getOffsets(millis, offsets);
-            // } else {
+            if (zi instanceof libcore.util.ZoneInfo) {
+                zoneOffset = ((libcore.util.ZoneInfo) zi).getOffsetsByUtcTime(millis, offsets);
+            // END Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
+            } else {
                 zoneOffset = zi.getOffset(millis);
                 offsets[0] = zi.getRawOffset();
                 offsets[1] = zoneOffset - offsets[0];
-            // }
-            // END Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
+            }
 
             // We need to calculate the given millis and time zone
             // offset separately for java.util.GregorianCalendar
diff --git a/support/src/test/java/libcore/testing/io/TestIoUtils.java b/support/src/test/java/libcore/testing/io/TestIoUtils.java
index 8e241df..34a2cf7 100644
--- a/support/src/test/java/libcore/testing/io/TestIoUtils.java
+++ b/support/src/test/java/libcore/testing/io/TestIoUtils.java
@@ -17,6 +17,10 @@
 package libcore.testing.io;
 
 import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.util.Random;
 
 public class TestIoUtils {
@@ -25,6 +29,13 @@
     private TestIoUtils() {}
 
     /**
+     * Returns the contents of 'path' as a string. The contents are assumed to be UTF-8.
+     */
+    public static String readFileAsString(String absolutePath) throws IOException {
+        return new String(Files.readAllBytes(Paths.get(absolutePath)), StandardCharsets.UTF_8);
+    }
+
+    /**
      * Creates a unique new temporary directory under "java.io.tmpdir".
      */
     public static File createTemporaryDirectory(String prefix) {