Merge "Add test cases inspired by Jetty-dependent Harmony tests." into dalvik-dev
diff --git a/dalvik/src/main/java/dalvik/system/BlockGuard.java b/dalvik/src/main/java/dalvik/system/BlockGuard.java
index c25b5be..cf9789e 100644
--- a/dalvik/src/main/java/dalvik/system/BlockGuard.java
+++ b/dalvik/src/main/java/dalvik/system/BlockGuard.java
@@ -29,7 +29,6 @@
 import libcore.io.Libcore;
 import libcore.io.StructLinger;
 import libcore.util.EmptyArray;
-import org.apache.harmony.luni.platform.INetworkSystem;
 import static libcore.io.OsConstants.*;
 
 /**
@@ -243,22 +242,4 @@
     }
 
     private BlockGuard() {}
-
-    /**
-     * A network wrapper that calls the policy check functions.
-     */
-    public static class WrappedNetworkSystem implements INetworkSystem {
-        private final INetworkSystem mNetwork;
-
-        public WrappedNetworkSystem(INetworkSystem network) {
-            mNetwork = network;
-        }
-
-        public void accept(FileDescriptor serverFd, SocketImpl newSocket,
-                FileDescriptor clientFd) throws IOException {
-            BlockGuard.getThreadPolicy().onNetwork();
-            mNetwork.accept(serverFd, newSocket, clientFd);
-            tagSocketFd(clientFd);
-        }
-    }
 }
diff --git a/luni/src/main/java/java/io/FileInputStream.java b/luni/src/main/java/java/io/FileInputStream.java
index 5b26792..6c2da1e 100644
--- a/luni/src/main/java/java/io/FileInputStream.java
+++ b/luni/src/main/java/java/io/FileInputStream.java
@@ -23,6 +23,7 @@
 import java.nio.channels.FileChannel;
 import java.util.Arrays;
 import libcore.io.ErrnoException;
+import libcore.io.IoBridge;
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
 import libcore.io.Streams;
@@ -76,7 +77,7 @@
         if (file == null) {
             throw new NullPointerException("file == null");
         }
-        this.fd = IoUtils.open(file.getAbsolutePath(), O_RDONLY);
+        this.fd = IoBridge.open(file.getAbsolutePath(), O_RDONLY);
         this.ownedFd = fd;
         guard.open("close");
     }
@@ -108,7 +109,7 @@
 
     @Override
     public int available() throws IOException {
-        return IoUtils.available(fd);
+        return IoBridge.available(fd);
     }
 
     @Override
@@ -171,7 +172,7 @@
     }
 
     @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
-        return IoUtils.read(fd, buffer, byteOffset, byteCount);
+        return IoBridge.read(fd, buffer, byteOffset, byteCount);
     }
 
     @Override
diff --git a/luni/src/main/java/java/io/FileOutputStream.java b/luni/src/main/java/java/io/FileOutputStream.java
index 8c61281..cc57571 100644
--- a/luni/src/main/java/java/io/FileOutputStream.java
+++ b/luni/src/main/java/java/io/FileOutputStream.java
@@ -21,6 +21,7 @@
 import java.nio.NioUtils;
 import java.nio.channels.FileChannel;
 import java.util.Arrays;
+import libcore.io.IoBridge;
 import libcore.io.IoUtils;
 import static libcore.io.OsConstants.*;
 
@@ -88,7 +89,7 @@
             throw new NullPointerException("file == null");
         }
         this.mode = O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC);
-        this.fd = IoUtils.open(file.getAbsolutePath(), mode);
+        this.fd = IoBridge.open(file.getAbsolutePath(), mode);
         this.ownedFd = fd;
         this.guard.open("close");
     }
@@ -175,7 +176,7 @@
 
     @Override
     public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
-        IoUtils.write(fd, buffer, byteOffset, byteCount);
+        IoBridge.write(fd, buffer, byteOffset, byteCount);
     }
 
     @Override
diff --git a/luni/src/main/java/java/io/RandomAccessFile.java b/luni/src/main/java/java/io/RandomAccessFile.java
index 8517604..dde779e 100644
--- a/luni/src/main/java/java/io/RandomAccessFile.java
+++ b/luni/src/main/java/java/io/RandomAccessFile.java
@@ -24,6 +24,7 @@
 import java.nio.charset.ModifiedUtf8;
 import java.util.Arrays;
 import libcore.io.ErrnoException;
+import libcore.io.IoBridge;
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
 import libcore.io.Memory;
@@ -114,7 +115,7 @@
             throw new IllegalArgumentException("Invalid mode: " + mode);
         }
         this.mode = flags;
-        this.fd = IoUtils.open(file.getAbsolutePath(), flags);
+        this.fd = IoBridge.open(file.getAbsolutePath(), flags);
 
         // if we are in "rws" mode, attempt to sync file+metadata
         if (syncMetadata) {
@@ -286,7 +287,7 @@
      *             if this file is closed or another I/O error occurs.
      */
     public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
-        return IoUtils.read(fd, buffer, byteOffset, byteCount);
+        return IoBridge.read(fd, buffer, byteOffset, byteCount);
     }
 
     /**
@@ -688,7 +689,7 @@
      *             if an I/O error occurs while writing to this file.
      */
     public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
-        IoUtils.write(fd, buffer, byteOffset, byteCount);
+        IoBridge.write(fd, buffer, byteOffset, byteCount);
         // if we are in "rws" mode, attempt to sync file+metadata
         if (syncMetadata) {
             fd.sync();
diff --git a/luni/src/main/java/java/lang/Double.java b/luni/src/main/java/java/lang/Double.java
index 1748128..9f8ce7e 100644
--- a/luni/src/main/java/java/lang/Double.java
+++ b/luni/src/main/java/java/lang/Double.java
@@ -166,31 +166,18 @@
     }
 
     /**
-     * Converts the specified double value to a binary representation conforming
-     * to the IEEE 754 floating-point double precision bit layout. All
-     * <em>Not-a-Number (NaN)</em> values are converted to a single NaN
-     * representation ({@code 0x7ff8000000000000L}).
-     *
-     * @param value
-     *            the double value to convert.
-     * @return the IEEE 754 floating-point double precision representation of
-     *         {@code value}.
-     * @see #doubleToRawLongBits(double)
-     * @see #longBitsToDouble(long)
+     * Returns an integer corresponding to the bits of the given
+     * <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> double precision
+     * {@code value}. All <em>Not-a-Number (NaN)</em> values are converted to a single NaN
+     * representation ({@code 0x7ff8000000000000L}) (compare to {@link #doubleToRawLongBits}).
      */
     public static native long doubleToLongBits(double value);
 
     /**
-     * Converts the specified double value to a binary representation conforming
-     * to the IEEE 754 floating-point double precision bit layout.
-     * <em>Not-a-Number (NaN)</em> values are preserved.
-     *
-     * @param value
-     *            the double value to convert.
-     * @return the IEEE 754 floating-point double precision representation of
-     *         {@code value}.
-     * @see #doubleToLongBits(double)
-     * @see #longBitsToDouble(long)
+     * Returns an integer corresponding to the bits of the given
+     * <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> double precision
+     * {@code value}. <em>Not-a-Number (NaN)</em> values are preserved (compare
+     * to {@link #doubleToLongBits}).
      */
     public static native long doubleToRawLongBits(double value);
 
@@ -286,15 +273,8 @@
     }
 
     /**
-     * Converts the specified IEEE 754 floating-point double precision bit
-     * pattern to a Java double value.
-     *
-     * @param bits
-     *            the IEEE 754 floating-point double precision representation of
-     *            a double value.
-     * @return the double value converted from {@code bits}.
-     * @see #doubleToLongBits(double)
-     * @see #doubleToRawLongBits(double)
+     * Returns the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a>
+     * double precision float corresponding to the given {@code bits}.
      */
     public static native double longBitsToDouble(long bits);
 
@@ -313,7 +293,7 @@
      *             if {@code string} cannot be parsed as a double value.
      */
     public static double parseDouble(String string) throws NumberFormatException {
-        return org.apache.harmony.luni.util.FloatingPointParser.parseDouble(string);
+        return StringToReal.parseDouble(string);
     }
 
     @Override
@@ -421,7 +401,7 @@
      */
     public static String toHexString(double d) {
         /*
-         * Reference: http://en.wikipedia.org/wiki/IEEE_754
+         * Reference: http://en.wikipedia.org/wiki/IEEE_754-1985
          */
         if (d != d) {
             return "NaN";
diff --git a/luni/src/main/java/java/lang/Float.java b/luni/src/main/java/java/lang/Float.java
index bbbb7f7..e4dc140 100644
--- a/luni/src/main/java/java/lang/Float.java
+++ b/luni/src/main/java/java/lang/Float.java
@@ -195,31 +195,18 @@
     }
 
     /**
-     * Converts the specified float value to a binary representation conforming
-     * to the IEEE 754 floating-point single precision bit layout. All
-     * <em>Not-a-Number (NaN)</em> values are converted to a single NaN
-     * representation ({@code 0x7fc00000}).
-     *
-     * @param value
-     *            the float value to convert.
-     * @return the IEEE 754 floating-point single precision representation of
-     *         {@code value}.
-     * @see #floatToRawIntBits(float)
-     * @see #intBitsToFloat(int)
+     * Returns an integer corresponding to the bits of the given
+     * <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> single precision
+     * float {@code value}. All <em>Not-a-Number (NaN)</em> values are converted to a single NaN
+     * representation ({@code 0x7fc00000}) (compare to {@link #floatToRawIntBits}).
      */
     public static native int floatToIntBits(float value);
 
     /**
-     * Converts the specified float value to a binary representation conforming
-     * to the IEEE 754 floating-point single precision bit layout.
-     * <em>Not-a-Number (NaN)</em> values are preserved.
-     *
-     * @param value
-     *            the float value to convert.
-     * @return the IEEE 754 floating-point single precision representation of
-     *         {@code value}.
-     * @see #floatToIntBits(float)
-     * @see #intBitsToFloat(int)
+     * Returns an integer corresponding to the bits of the given
+     * <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> single precision
+     * float {@code value}. <em>Not-a-Number (NaN)</em> values are preserved (compare
+     * to {@link #floatToIntBits}).
      */
     public static native int floatToRawIntBits(float value);
 
@@ -239,15 +226,8 @@
     }
 
     /**
-     * Converts the specified IEEE 754 floating-point single precision bit
-     * pattern to a Java float value.
-     *
-     * @param bits
-     *            the IEEE 754 floating-point single precision representation of
-     *            a float value.
-     * @return the float value converted from {@code bits}.
-     * @see #floatToIntBits(float)
-     * @see #floatToRawIntBits(float)
+     * Returns the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a>
+     * single precision float corresponding to the given {@code bits}.
      */
     public static native float intBitsToFloat(int bits);
 
@@ -318,8 +298,7 @@
      * @since 1.2
      */
     public static float parseFloat(String string) throws NumberFormatException {
-        return org.apache.harmony.luni.util.FloatingPointParser
-                .parseFloat(string);
+        return StringToReal.parseFloat(string);
     }
 
     @Override
@@ -429,7 +408,7 @@
      */
     public static String toHexString(float f) {
         /*
-         * Reference: http://en.wikipedia.org/wiki/IEEE_754
+         * Reference: http://en.wikipedia.org/wiki/IEEE_754-1985
          */
         if (f != f) {
             return "NaN";
diff --git a/luni/src/main/java/org/apache/harmony/luni/util/HexStringParser.java b/luni/src/main/java/java/lang/HexStringParser.java
similarity index 99%
rename from luni/src/main/java/org/apache/harmony/luni/util/HexStringParser.java
rename to luni/src/main/java/java/lang/HexStringParser.java
index 9e20a16..8593b99 100644
--- a/luni/src/main/java/org/apache/harmony/luni/util/HexStringParser.java
+++ b/luni/src/main/java/java/lang/HexStringParser.java
@@ -15,13 +15,17 @@
  *  limitations under the License.
  */
 
-package org.apache.harmony.luni.util;
+package java.lang;
 
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 /*
  * Parses hex string to a single or double precision floating point number.
+ *
+ * TODO: rewrite this!
+ *
+ * @hide
  */
 final class HexStringParser {
 
diff --git a/luni/src/main/java/java/lang/ProcessManager.java b/luni/src/main/java/java/lang/ProcessManager.java
index cdcbb8a..d65d04d 100644
--- a/luni/src/main/java/java/lang/ProcessManager.java
+++ b/luni/src/main/java/java/lang/ProcessManager.java
@@ -31,31 +31,13 @@
 import libcore.io.ErrnoException;
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
+import libcore.util.MutableInt;
 import static libcore.io.OsConstants.*;
 
 /**
  * Manages child processes.
  */
 final class ProcessManager {
-
-    /**
-     * constant communicated from native code indicating that a
-     * child died, but it was unable to determine the status
-     */
-    private static final int WAIT_STATUS_UNKNOWN = -1;
-
-    /**
-     * constant communicated from native code indicating that there
-     * are currently no children to wait for
-     */
-    private static final int WAIT_STATUS_NO_CHILDREN = -2;
-
-    /**
-     * constant communicated from native code indicating that a wait()
-     * call returned -1 and set an undocumented (and hence unexpected) errno
-     */
-    private static final int WAIT_STATUS_STRANGE_ERRNO = -3;
-
     /**
      * Map from pid to Process. We keep weak references to the Process objects
      * and clean up the entries when no more external references are left. The
@@ -71,13 +53,13 @@
 
     private ProcessManager() {
         // Spawn a thread to listen for signals from child processes.
-        Thread processThread = new Thread(ProcessManager.class.getName()) {
+        Thread reaperThread = new Thread(ProcessManager.class.getName()) {
             @Override public void run() {
-                watchChildren(ProcessManager.this);
+                watchChildren();
             }
         };
-        processThread.setDaemon(true);
-        processThread.start();
+        reaperThread.setDaemon(true);
+        reaperThread.start();
     }
 
     /**
@@ -94,10 +76,40 @@
     }
 
     /**
-     * Listens for signals from processes and calls back to
-     * {@link #onExit(int,int)}.
+     * Loops indefinitely and calls ProcessManager.onExit() when children exit.
      */
-    private static native void watchChildren(ProcessManager manager);
+    private void watchChildren() {
+        MutableInt status = new MutableInt(-1);
+        while (true) {
+            try {
+                // Wait for children in our process group.
+                int pid = Libcore.os.waitpid(0, status, 0);
+
+                // Work out what onExit wants to hear.
+                int exitValue;
+                if (WIFEXITED(status.value)) {
+                    exitValue = WEXITSTATUS(status.value);
+                } else if (WIFSIGNALED(status.value)) {
+                    exitValue = WTERMSIG(status.value);
+                } else if (WIFSTOPPED(status.value)) {
+                    exitValue = WSTOPSIG(status.value);
+                } else {
+                    throw new AssertionError("unexpected status from waitpid: " + status.value);
+                }
+
+                onExit(pid, exitValue);
+            } catch (ErrnoException errnoException) {
+                if (errnoException.errno == ECHILD) {
+                    // Expected errno: there are no children to wait for.
+                    // onExit will sleep until it is informed of another child coming to life.
+                    waitForMoreChildren();
+                    continue;
+                } else {
+                    throw errnoException;
+                }
+            }
+        }
+    }
 
     /**
      * Called by {@link #watchChildren()} when a child process exits.
@@ -107,38 +119,10 @@
      */
     private void onExit(int pid, int exitValue) {
         ProcessReference processReference = null;
-
         synchronized (processReferences) {
             cleanUp();
-            if (pid >= 0) {
-                processReference = processReferences.remove(pid);
-            } else if (exitValue == WAIT_STATUS_NO_CHILDREN) {
-                if (processReferences.isEmpty()) {
-                    /*
-                     * There are no eligible children; wait for one to be
-                     * added. The wait() will return due to the
-                     * notifyAll() call below.
-                     */
-                    try {
-                        processReferences.wait();
-                    } catch (InterruptedException ex) {
-                        // This should never happen.
-                        throw new AssertionError("unexpected interrupt");
-                    }
-                } else {
-                    /*
-                     * A new child was spawned just before we entered
-                     * the synchronized block. We can just fall through
-                     * without doing anything special and land back in
-                     * the native wait().
-                     */
-                }
-            } else {
-                // Something weird is happening; abort!
-                throw new AssertionError("unexpected wait() behavior");
-            }
+            processReference = processReferences.remove(pid);
         }
-
         if (processReference != null) {
             ProcessImpl process = processReference.get();
             if (process != null) {
@@ -147,6 +131,31 @@
         }
     }
 
+    private void waitForMoreChildren() {
+        synchronized (processReferences) {
+            if (processReferences.isEmpty()) {
+                /*
+                 * There are no eligible children; wait for one to be
+                 * added. The wait() will return due to the
+                 * notifyAll() call below.
+                 */
+                try {
+                    processReferences.wait();
+                } catch (InterruptedException ex) {
+                    // This should never happen.
+                    throw new AssertionError("unexpected interrupt");
+                }
+            } else {
+                /*
+                 * A new child was spawned just before we entered
+                 * the synchronized block. We can just fall through
+                 * without doing anything special and land back in
+                 * the native waitpid().
+                 */
+            }
+        }
+    }
+
     /**
      * Executes a native process. Fills in in, out, and err and returns the
      * new process ID upon success.
diff --git a/luni/src/main/java/org/apache/harmony/luni/util/FloatingPointParser.java b/luni/src/main/java/java/lang/StringToReal.java
similarity index 98%
rename from luni/src/main/java/org/apache/harmony/luni/util/FloatingPointParser.java
rename to luni/src/main/java/java/lang/StringToReal.java
index e08e560..97f6d6b 100644
--- a/luni/src/main/java/org/apache/harmony/luni/util/FloatingPointParser.java
+++ b/luni/src/main/java/java/lang/StringToReal.java
@@ -15,14 +15,14 @@
  *  limitations under the License.
  */
 
-package org.apache.harmony.luni.util;
-
+package java.lang;
 
 /**
  * Used to parse a string and return either a single or double precision
  * floating point number.
+ * @hide
  */
-public final class FloatingPointParser {
+final class StringToReal {
 
     private static final class StringExponentPair {
         String s;
diff --git a/luni/src/main/java/java/math/MathContext.java b/luni/src/main/java/java/math/MathContext.java
index 24ccb29..6f3f1ed 100644
--- a/luni/src/main/java/java/math/MathContext.java
+++ b/luni/src/main/java/java/math/MathContext.java
@@ -30,21 +30,21 @@
     private static final long serialVersionUID = 5579720004786848255L;
 
     /**
-     * A {@code MathContext} which corresponds to the IEEE 754r quadruple
+     * A {@code MathContext} which corresponds to the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> quadruple
      * decimal precision format: 34 digit precision and
      * {@link RoundingMode#HALF_EVEN} rounding.
      */
     public static final MathContext DECIMAL128 = new MathContext(34, RoundingMode.HALF_EVEN);
 
     /**
-     * A {@code MathContext} which corresponds to the IEEE 754r single decimal
+     * A {@code MathContext} which corresponds to the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> single decimal
      * precision format: 7 digit precision and {@link RoundingMode#HALF_EVEN}
      * rounding.
      */
     public static final MathContext DECIMAL32 = new MathContext(7, RoundingMode.HALF_EVEN);
 
     /**
-     * A {@code MathContext} which corresponds to the IEEE 754r double decimal
+     * A {@code MathContext} which corresponds to the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> double decimal
      * precision format: 16 digit precision and {@link RoundingMode#HALF_EVEN}
      * rounding.
      */
diff --git a/luni/src/main/java/java/net/DatagramSocketImpl.java b/luni/src/main/java/java/net/DatagramSocketImpl.java
index 4485b18..6e706c3 100644
--- a/luni/src/main/java/java/net/DatagramSocketImpl.java
+++ b/luni/src/main/java/java/net/DatagramSocketImpl.java
@@ -19,7 +19,7 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
 
 /**
  * The abstract superclass for datagram and multicast socket implementations.
@@ -85,7 +85,7 @@
      * Returns the local address to which the socket is bound.
      */
     InetAddress getLocalAddress() {
-        return IoUtils.getSocketLocalAddress(fd);
+        return IoBridge.getSocketLocalAddress(fd);
     }
 
     /**
diff --git a/luni/src/main/java/java/net/InetAddress.java b/luni/src/main/java/java/net/InetAddress.java
index 9541bb9..56eb1f4 100644
--- a/luni/src/main/java/java/net/InetAddress.java
+++ b/luni/src/main/java/java/net/InetAddress.java
@@ -34,7 +34,7 @@
 import libcore.io.ErrnoException;
 import libcore.io.GaiException;
 import libcore.io.Libcore;
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
 import libcore.io.Memory;
 import libcore.io.StructAddrinfo;
 import static libcore.io.OsConstants.*;
@@ -807,13 +807,13 @@
     }
 
     private boolean isReachableByTCP(InetAddress destination, InetAddress source, int timeout) throws IOException {
-        FileDescriptor fd = IoUtils.socket(true);
+        FileDescriptor fd = IoBridge.socket(true);
         boolean reached = false;
         try {
             if (source != null) {
-                IoUtils.bind(fd, source, 0);
+                IoBridge.bind(fd, source, 0);
             }
-            IoUtils.connect(fd, destination, 7, timeout);
+            IoBridge.connect(fd, destination, 7, timeout);
             reached = true;
         } catch (IOException e) {
             if (e.getCause() instanceof ErrnoException) {
@@ -822,7 +822,7 @@
             }
         }
 
-        IoUtils.closeSocket(fd);
+        IoBridge.closeSocket(fd);
 
         return reached;
     }
diff --git a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
index 4301f79..f27db94 100644
--- a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
@@ -28,7 +28,7 @@
 import java.net.SocketAddress;
 import java.net.SocketException;
 import java.net.UnknownHostException;
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
 import libcore.io.Libcore;
 import libcore.io.StructGroupReq;
 import libcore.util.EmptyArray;
@@ -63,13 +63,12 @@
         fd = new FileDescriptor();
     }
 
-    @Override
-    public void bind(int port, InetAddress address) throws SocketException {
-        IoUtils.bind(fd, address, port);
+    @Override public void bind(int port, InetAddress address) throws SocketException {
+        IoBridge.bind(fd, address, port);
         if (port != 0) {
             localPort = port;
         } else {
-            localPort = IoUtils.getSocketLocalPort(fd);
+            localPort = IoBridge.getSocketLocalPort(fd);
         }
         try {
             setOption(SocketOptions.SO_BROADCAST, Boolean.TRUE);
@@ -81,14 +80,14 @@
     public synchronized void close() {
         guard.close();
         try {
-            IoUtils.closeSocket(fd);
+            IoBridge.closeSocket(fd);
         } catch (IOException ignored) {
         }
     }
 
     @Override
     public void create() throws SocketException {
-        this.fd = IoUtils.socket(false);
+        this.fd = IoBridge.socket(false);
     }
 
     @Override protected void finalize() throws Throwable {
@@ -103,12 +102,12 @@
     }
 
     @Override public Object getOption(int option) throws SocketException {
-        return IoUtils.getSocketOption(fd, option);
+        return IoBridge.getSocketOption(fd, option);
     }
 
     @Override
     public int getTimeToLive() throws IOException {
-        return (Integer) getOption(IoUtils.JAVA_IP_MULTICAST_TTL);
+        return (Integer) getOption(IoBridge.JAVA_IP_MULTICAST_TTL);
     }
 
     @Override
@@ -123,27 +122,27 @@
 
     @Override
     public void join(InetAddress addr) throws IOException {
-        setOption(IoUtils.JAVA_MCAST_JOIN_GROUP, makeGroupReq(addr, null));
+        setOption(IoBridge.JAVA_MCAST_JOIN_GROUP, makeGroupReq(addr, null));
     }
 
     @Override
     public void joinGroup(SocketAddress addr, NetworkInterface netInterface) throws IOException {
         if (addr instanceof InetSocketAddress) {
             InetAddress groupAddr = ((InetSocketAddress) addr).getAddress();
-            setOption(IoUtils.JAVA_MCAST_JOIN_GROUP, makeGroupReq(groupAddr, netInterface));
+            setOption(IoBridge.JAVA_MCAST_JOIN_GROUP, makeGroupReq(groupAddr, netInterface));
         }
     }
 
     @Override
     public void leave(InetAddress addr) throws IOException {
-        setOption(IoUtils.JAVA_MCAST_LEAVE_GROUP, makeGroupReq(addr, null));
+        setOption(IoBridge.JAVA_MCAST_LEAVE_GROUP, makeGroupReq(addr, null));
     }
 
     @Override
     public void leaveGroup(SocketAddress addr, NetworkInterface netInterface) throws IOException {
         if (addr instanceof InetSocketAddress) {
             InetAddress groupAddr = ((InetSocketAddress) addr).getAddress();
-            setOption(IoUtils.JAVA_MCAST_LEAVE_GROUP, makeGroupReq(groupAddr, netInterface));
+            setOption(IoBridge.JAVA_MCAST_LEAVE_GROUP, makeGroupReq(groupAddr, netInterface));
         }
     }
 
@@ -158,7 +157,7 @@
     }
 
     private void doRecv(DatagramPacket pack, int flags) throws IOException {
-        IoUtils.recvfrom(false, fd, pack.getData(), pack.getOffset(), pack.getLength(), flags, pack, isNativeConnected);
+        IoBridge.recvfrom(false, fd, pack.getData(), pack.getOffset(), pack.getLength(), flags, pack, isNativeConnected);
         if (isNativeConnected) {
             updatePacketRecvAddress(pack);
         }
@@ -179,16 +178,16 @@
     public void send(DatagramPacket packet) throws IOException {
         int port = isNativeConnected ? 0 : packet.getPort();
         InetAddress address = isNativeConnected ? null : packet.getAddress();
-        IoUtils.sendto(fd, packet.getData(), packet.getOffset(), packet.getLength(), 0, address, port);
+        IoBridge.sendto(fd, packet.getData(), packet.getOffset(), packet.getLength(), 0, address, port);
     }
 
     public void setOption(int option, Object value) throws SocketException {
-        IoUtils.setSocketOption(fd, option, value);
+        IoBridge.setSocketOption(fd, option, value);
     }
 
     @Override
     public void setTimeToLive(int ttl) throws IOException {
-        setOption(IoUtils.JAVA_IP_MULTICAST_TTL, Integer.valueOf(ttl));
+        setOption(IoBridge.JAVA_IP_MULTICAST_TTL, Integer.valueOf(ttl));
     }
 
     @Override
@@ -198,7 +197,7 @@
 
     @Override
     public void connect(InetAddress inetAddr, int port) throws SocketException {
-        IoUtils.connect(fd, inetAddr, port); // Throws on failure.
+        IoBridge.connect(fd, inetAddr, port); // Throws on failure.
         try {
             connectedAddress = InetAddress.getByAddress(inetAddr.getAddress());
         } catch (UnknownHostException e) {
diff --git a/luni/src/main/java/java/net/PlainSocketImpl.java b/luni/src/main/java/java/net/PlainSocketImpl.java
index 1ab4cdf..20dfcc2 100644
--- a/luni/src/main/java/java/net/PlainSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainSocketImpl.java
@@ -34,11 +34,10 @@
 import java.nio.ByteOrder;
 import java.util.Arrays;
 import libcore.io.ErrnoException;
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
 import libcore.io.Libcore;
 import libcore.io.Memory;
 import libcore.io.Streams;
-import org.apache.harmony.luni.platform.Platform;
 import static libcore.io.OsConstants.*;
 
 /**
@@ -93,12 +92,28 @@
             ((PlainSocketImpl) newImpl).socksAccept();
             return;
         }
-        Platform.NETWORK.accept(fd, newImpl, newImpl.getFileDescriptor());
+
+        try {
+            InetSocketAddress peerAddress = new InetSocketAddress();
+            FileDescriptor clientFd = Libcore.os.accept(fd, peerAddress);
+
+            // TODO: we can't just set newImpl.fd to clientFd because a nio SocketChannel may
+            // be sharing the FileDescriptor. http://b//4452981.
+            newImpl.fd.setInt$(clientFd.getInt$());
+
+            newImpl.address = peerAddress.getAddress();
+            newImpl.port = peerAddress.getPort();
+        } catch (ErrnoException errnoException) {
+            if (errnoException.errno == EAGAIN || errnoException.errno == EWOULDBLOCK) {
+                throw new SocketTimeoutException(errnoException);
+            }
+            throw errnoException.rethrowAsSocketException();
+        }
 
         // Reset the client's inherited read timeout to the Java-specified default of 0.
         newImpl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(0));
 
-        newImpl.localport = IoUtils.getSocketLocalPort(newImpl.fd);
+        newImpl.localport = IoBridge.getSocketLocalPort(newImpl.fd);
     }
 
     private boolean usingSocks() {
@@ -128,24 +143,23 @@
         if (shutdownInput) {
             return 0;
         }
-        return IoUtils.available(fd);
+        return IoBridge.available(fd);
     }
 
-    @Override
-    protected void bind(InetAddress address, int port) throws IOException {
-        IoUtils.bind(fd, address, port);
+    @Override protected void bind(InetAddress address, int port) throws IOException {
+        IoBridge.bind(fd, address, port);
         this.address = address;
         if (port != 0) {
             this.localport = port;
         } else {
-            this.localport = IoUtils.getSocketLocalPort(fd);
+            this.localport = IoBridge.getSocketLocalPort(fd);
         }
     }
 
     @Override
     protected synchronized void close() throws IOException {
         guard.close();
-        IoUtils.closeSocket(fd);
+        IoBridge.closeSocket(fd);
     }
 
     @Override
@@ -175,7 +189,7 @@
         if (streaming && usingSocks()) {
             socksConnect(anAddr, aPort, 0);
         } else {
-            IoUtils.connect(fd, normalAddr, aPort, timeout);
+            IoBridge.connect(fd, normalAddr, aPort, timeout);
         }
         super.address = normalAddr;
         super.port = aPort;
@@ -184,7 +198,7 @@
     @Override
     protected void create(boolean streaming) throws IOException {
         this.streaming = streaming;
-        this.fd = IoUtils.socket(streaming);
+        this.fd = IoBridge.socket(streaming);
     }
 
     @Override protected void finalize() throws Throwable {
@@ -228,7 +242,7 @@
     }
 
     @Override public Object getOption(int option) throws SocketException {
-        return IoUtils.getSocketOption(fd, option);
+        return IoBridge.getSocketOption(fd, option);
     }
 
     @Override protected synchronized OutputStream getOutputStream() throws IOException {
@@ -272,7 +286,7 @@
 
     @Override
     public void setOption(int option, Object value) throws SocketException {
-        IoUtils.setSocketOption(fd, option, value);
+        IoBridge.setSocketOption(fd, option, value);
     }
 
     /**
@@ -307,7 +321,7 @@
      */
     private void socksConnect(InetAddress applicationServerAddress, int applicationServerPort, int timeout) throws IOException {
         try {
-            IoUtils.connect(fd, socksGetServerAddress(), socksGetServerPort(), timeout);
+            IoBridge.connect(fd, socksGetServerAddress(), socksGetServerPort(), timeout);
         } catch (Exception e) {
             throw new SocketException("SOCKS connection failed", e);
         }
@@ -372,7 +386,7 @@
      */
     private void socksBind() throws IOException {
         try {
-            IoUtils.connect(fd, socksGetServerAddress(), socksGetServerPort());
+            IoBridge.connect(fd, socksGetServerAddress(), socksGetServerPort());
         } catch (Exception e) {
             throw new IOException("Unable to connect to SOCKS server", e);
         }
@@ -471,7 +485,7 @@
         if (shutdownInput) {
             return -1;
         }
-        int readCount = IoUtils.recvfrom(true, fd, buffer, offset, byteCount, 0, null, false);
+        int readCount = IoBridge.recvfrom(true, fd, buffer, offset, byteCount, 0, null, false);
         // Return of zero bytes for a blocking socket means a timeout occurred
         if (readCount == 0) {
             throw new SocketTimeoutException();
@@ -490,7 +504,7 @@
         Arrays.checkOffsetAndCount(buffer.length, offset, byteCount);
         if (streaming) {
             while (byteCount > 0) {
-                int bytesWritten = IoUtils.sendto(fd, buffer, offset, byteCount, 0, null, 0);
+                int bytesWritten = IoBridge.sendto(fd, buffer, offset, byteCount, 0, null, 0);
                 byteCount -= bytesWritten;
                 offset += bytesWritten;
             }
@@ -498,7 +512,7 @@
             // Unlike writes to a streaming socket, writes to a datagram
             // socket are all-or-nothing, so we don't need a loop here.
             // http://code.google.com/p/android/issues/detail?id=15304
-            IoUtils.sendto(fd, buffer, offset, byteCount, 0, address, port);
+            IoBridge.sendto(fd, buffer, offset, byteCount, 0, address, port);
         }
     }
 }
diff --git a/luni/src/main/java/java/net/Socket.java b/luni/src/main/java/java/net/Socket.java
index 06e1ab3..f7788b5 100644
--- a/luni/src/main/java/java/net/Socket.java
+++ b/luni/src/main/java/java/net/Socket.java
@@ -22,7 +22,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.channels.SocketChannel;
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
 
 /**
  * Provides a client-side TCP socket.
@@ -947,7 +947,7 @@
     }
 
     private void cacheLocalAddress() {
-        this.localAddress = IoUtils.getSocketLocalAddress(impl.fd);
+        this.localAddress = IoBridge.getSocketLocalAddress(impl.fd);
     }
 
     /**
diff --git a/luni/src/main/java/java/net/SocketTimeoutException.java b/luni/src/main/java/java/net/SocketTimeoutException.java
index 30417d5..8f8ef82 100644
--- a/luni/src/main/java/java/net/SocketTimeoutException.java
+++ b/luni/src/main/java/java/net/SocketTimeoutException.java
@@ -41,6 +41,14 @@
     }
 
     /**
+     * Constructs a new instance with given cause.
+     * @hide internal use only
+     */
+    public SocketTimeoutException(Throwable cause) {
+        super(null, cause);
+    }
+
+    /**
      * Constructs a new instance with given detail message and cause.
      * @hide internal use only
      */
diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java
index f0b9eb0..7874af6 100644
--- a/luni/src/main/java/java/nio/DatagramChannelImpl.java
+++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java
@@ -37,6 +37,7 @@
 import java.nio.channels.spi.SelectorProvider;
 import java.util.Arrays;
 import libcore.io.ErrnoException;
+import libcore.io.IoBridge;
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
 import libcore.util.EmptyArray;
@@ -71,7 +72,7 @@
      */
     protected DatagramChannelImpl(SelectorProvider selectorProvider) throws IOException {
         super(selectorProvider);
-        fd = IoUtils.socket(false);
+        fd = IoBridge.socket(false);
     }
 
     /*
@@ -100,7 +101,7 @@
      * Returns the local address to which the socket is bound.
      */
     InetAddress getLocalAddress() {
-        return IoUtils.getSocketLocalAddress(fd);
+        return IoBridge.getSocketLocalAddress(fd);
     }
 
     /**
@@ -127,7 +128,7 @@
         InetSocketAddress inetSocketAddress = SocketChannelImpl.validateAddress(address);
         try {
             begin();
-            IoUtils.connect(fd, inetSocketAddress.getAddress(), inetSocketAddress.getPort());
+            IoBridge.connect(fd, inetSocketAddress.getAddress(), inetSocketAddress.getPort());
         } catch (ConnectException e) {
             // ConnectException means connect fail, not exception
         } finally {
@@ -205,7 +206,7 @@
             receivePacket = new DatagramPacket(new byte[target.remaining()], target.remaining());
         }
         do {
-            received = IoUtils.recvfrom(false, fd, receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength(), 0, receivePacket, isConnected());
+            received = IoBridge.recvfrom(false, fd, receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength(), 0, receivePacket, isConnected());
             if (receivePacket != null && receivePacket.getAddress() != null) {
                 if (received > 0) {
                     if (target.hasArray()) {
@@ -228,7 +229,7 @@
         int oldposition = target.position();
         int received = 0;
         do {
-            received = IoUtils.recvfrom(false, fd, target, 0, receivePacket, isConnected());
+            received = IoBridge.recvfrom(false, fd, target, 0, receivePacket, isConnected());
             if (receivePacket != null && receivePacket.getAddress() != null) {
                 // copy the data of received packet
                 if (received > 0) {
@@ -260,7 +261,7 @@
             try {
                 begin();
                 int oldPosition = source.position();
-                sendCount = IoUtils.sendto(fd, source, 0, isa.getAddress(), isa.getPort());
+                sendCount = IoBridge.sendto(fd, source, 0, isa.getAddress(), isa.getPort());
                 source.position(oldPosition + sendCount);
             } finally {
                 end(sendCount >= 0);
@@ -333,7 +334,7 @@
             int readCount = 0;
             try {
                 begin();
-                readCount = IoUtils.recvfrom(false, fd, dst, 0, null, isConnected());
+                readCount = IoBridge.recvfrom(false, fd, dst, 0, null, isConnected());
             } catch (InterruptedIOException e) {
                 // InterruptedIOException will be thrown when timeout.
                 return 0;
@@ -396,7 +397,7 @@
             int result = 0;
             try {
                 begin();
-                result = IoUtils.sendto(fd, buf, 0, null, 0);
+                result = IoBridge.sendto(fd, buf, 0, null, 0);
             } finally {
                 end(result > 0);
             }
@@ -409,7 +410,7 @@
         if (socket != null && !socket.isClosed()) {
             socket.close();
         } else {
-            IoUtils.closeSocket(fd);
+            IoBridge.closeSocket(fd);
         }
     }
 
diff --git a/luni/src/main/java/java/nio/FileChannelImpl.java b/luni/src/main/java/java/nio/FileChannelImpl.java
index acc3725..c1eb8c2 100644
--- a/luni/src/main/java/java/nio/FileChannelImpl.java
+++ b/luni/src/main/java/java/nio/FileChannelImpl.java
@@ -238,9 +238,6 @@
 
     public long position() throws IOException {
         checkOpen();
-        if ((mode & O_APPEND) != 0) {
-            return size();
-        }
         try {
             return Libcore.os.lseek(fd, 0L, SEEK_CUR);
         } catch (ErrnoException errnoException) {
@@ -482,9 +479,6 @@
     public int write(ByteBuffer buffer) throws IOException {
         checkOpen();
         checkWritable();
-        if ((mode & O_APPEND) != 0) {
-            position(size());
-        }
         return writeImpl(buffer);
     }
 
diff --git a/luni/src/main/java/java/nio/SelectorImpl.java b/luni/src/main/java/java/nio/SelectorImpl.java
index d1df600..dfd3270 100644
--- a/luni/src/main/java/java/nio/SelectorImpl.java
+++ b/luni/src/main/java/java/nio/SelectorImpl.java
@@ -36,6 +36,7 @@
 import java.util.Set;
 import java.util.UnsafeArrayList;
 import libcore.io.ErrnoException;
+import libcore.io.IoBridge;
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
 import libcore.io.StructPollfd;
@@ -227,7 +228,7 @@
         if (pollFds.get(0).revents == POLLIN) {
             // Read bytes from the wakeup pipe until the pipe is empty.
             byte[] buffer = new byte[8];
-            while (IoUtils.read(wakeupIn, buffer, 0, 1) > 0) {
+            while (IoBridge.read(wakeupIn, buffer, 0, 1) > 0) {
             }
         }
 
diff --git a/luni/src/main/java/java/nio/SocketChannelImpl.java b/luni/src/main/java/java/nio/SocketChannelImpl.java
index 56c5709..c659299 100644
--- a/luni/src/main/java/java/nio/SocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/SocketChannelImpl.java
@@ -44,6 +44,7 @@
 import java.util.Arrays;
 import libcore.io.ErrnoException;
 import libcore.io.Libcore;
+import libcore.io.IoBridge;
 import libcore.io.IoUtils;
 import static libcore.io.OsConstants.*;
 
@@ -98,7 +99,7 @@
     public SocketChannelImpl(SelectorProvider selectorProvider, boolean connect) throws IOException {
         super(selectorProvider);
         status = SOCKET_STATUS_UNCONNECTED;
-        fd = (connect ? IoUtils.socket(true) : new FileDescriptor());
+        fd = (connect ? IoBridge.socket(true) : new FileDescriptor());
     }
 
     /*
@@ -164,7 +165,7 @@
             if (isBlocking()) {
                 begin();
             }
-            finished = IoUtils.connect(fd, normalAddr, port);
+            finished = IoBridge.connect(fd, normalAddr, port);
             isBound = finished;
         } catch (IOException e) {
             if (e instanceof ConnectException && !isBlocking()) {
@@ -228,7 +229,7 @@
             begin();
             InetAddress inetAddress = connectAddress.getAddress();
             int port = connectAddress.getPort();
-            finished = IoUtils.isConnected(fd, inetAddress, port, 0, 0); // Return immediately.
+            finished = IoBridge.isConnected(fd, inetAddress, port, 0, 0); // Return immediately.
             isBound = finished;
         } catch (ConnectException e) {
             if (isOpen()) {
@@ -296,7 +297,7 @@
                 if (isBlocking()) {
                     begin();
                 }
-                readCount = IoUtils.recvfrom(true, fd, dst, 0, null, false);
+                readCount = IoBridge.recvfrom(true, fd, dst, 0, null, false);
                 dst.position(dst.position() + readCount);
             } finally {
                 if (isBlocking()) {
@@ -358,7 +359,7 @@
                 if (isBlocking()) {
                     begin();
                 }
-                writeCount = IoUtils.sendto(fd, src, 0, null, 0);
+                writeCount = IoBridge.sendto(fd, src, 0, null, 0);
                 src.position(src.position() + writeCount);
             } finally {
                 if (isBlocking()) {
@@ -431,7 +432,7 @@
             if (socket != null && !socket.isClosed()) {
                 socket.close();
             } else {
-                IoUtils.closeSocket(fd);
+                IoBridge.closeSocket(fd);
             }
         }
     }
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
index 014b1cd..e77e0bb 100644
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -32,6 +32,20 @@
         super(os);
     }
 
+    private FileDescriptor tagSocket(FileDescriptor fd) {
+        try {
+            BlockGuard.tagSocketFd(fd);
+            return fd;
+        } catch (SocketException e) {
+            throw new ErrnoException("socket", EINVAL, e);
+        }
+    }
+
+    @Override public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException {
+        BlockGuard.getThreadPolicy().onNetwork();
+        return tagSocket(os.accept(fd, peerAddress));
+    }
+
     @Override public void close(FileDescriptor fd) throws ErrnoException {
         // TODO: is there a way to avoid calling getsockopt(2) on non-socket fds?
         if (isLingerSocket(fd)) {
@@ -108,14 +122,14 @@
         return os.readv(fd, buffers, offsets, byteCounts);
     }
 
-    @Override public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress inetSocketAddress) throws ErrnoException {
+    @Override public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException {
         BlockGuard.getThreadPolicy().onNetwork();
-        return os.recvfrom(fd, buffer, flags, inetSocketAddress);
+        return os.recvfrom(fd, buffer, flags, srcAddress);
     }
 
-    @Override public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress inetSocketAddress) throws ErrnoException {
+    @Override public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException {
         BlockGuard.getThreadPolicy().onNetwork();
-        return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, inetSocketAddress);
+        return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
     }
 
     @Override public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException {
@@ -132,13 +146,7 @@
     }
 
     @Override public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException {
-        final FileDescriptor fd = os.socket(domain, type, protocol);
-        try {
-            BlockGuard.tagSocketFd(fd);
-        } catch (SocketException e) {
-            throw new ErrnoException("socket", EINVAL, e);
-        }
-        return fd;
+        return tagSocket(os.socket(domain, type, protocol));
     }
 
     @Override public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index 7e807ef..f205994 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -34,6 +34,7 @@
         this.os = os;
     }
 
+    public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException { return os.accept(fd, peerAddress); }
     public boolean access(String path, int mode) throws ErrnoException { return os.access(path, mode); }
     public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException { os.bind(fd, address, port); }
     public void chmod(String path, int mode) throws ErrnoException { os.chmod(path, mode); }
@@ -90,8 +91,8 @@
     public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { return os.read(fd, buffer); }
     public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException { return os.read(fd, bytes, byteOffset, byteCount); }
     public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException { return os.readv(fd, buffers, offsets, byteCounts); }
-    public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress inetSocketAddress) throws ErrnoException { return os.recvfrom(fd, buffer, flags, inetSocketAddress); }
-    public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress inetSocketAddress) throws ErrnoException { return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, inetSocketAddress); }
+    public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException { return os.recvfrom(fd, buffer, flags, srcAddress); }
+    public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException { return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
     public void remove(String path) throws ErrnoException { os.remove(path); }
     public void rename(String oldPath, String newPath) throws ErrnoException { os.rename(oldPath, newPath); }
     public long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException { return os.sendfile(outFd, inFd, inOffset, byteCount); }
@@ -116,6 +117,7 @@
     public void symlink(String oldPath, String newPath) throws ErrnoException { os.symlink(oldPath, newPath); }
     public long sysconf(int name) { return os.sysconf(name); }
     public StructUtsname uname() throws ErrnoException { return os.uname(); }
+    public int waitpid(int pid, MutableInt status, int options) throws ErrnoException { return os.waitpid(pid, status, options); }
     public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { return os.write(fd, buffer); }
     public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException { return os.write(fd, bytes, byteOffset, byteCount); }
     public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException { return os.writev(fd, buffers, offsets, byteCounts); }
diff --git a/luni/src/main/java/libcore/io/IoBridge.java b/luni/src/main/java/libcore/io/IoBridge.java
new file mode 100644
index 0000000..c26b565
--- /dev/null
+++ b/luni/src/main/java/libcore/io/IoBridge.java
@@ -0,0 +1,593 @@
+/*
+ * 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.io;
+
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.BindException;
+import java.net.ConnectException;
+import java.net.DatagramPacket;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.PortUnreachableException;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.SocketOptions;
+import java.net.SocketTimeoutException;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+import libcore.util.MutableInt;
+import static libcore.io.OsConstants.*;
+
+/**
+ * Implements java.io/java.net/java.nio semantics in terms of the underlying POSIX system calls.
+ */
+public final class IoBridge {
+
+    private IoBridge() {
+    }
+
+    public static int available(FileDescriptor fd) throws IOException {
+        try {
+            MutableInt available = new MutableInt(0);
+            int rc = Libcore.os.ioctlInt(fd, FIONREAD, available);
+            if (available.value < 0) {
+                // If the fd refers to a regular file, the result is the difference between
+                // the file size and the file position. This may be negative if the position
+                // is past the end of the file. If the fd refers to a special file masquerading
+                // as a regular file, the result may be negative because the special file
+                // may appear to have zero size and yet a previous read call may have
+                // read some amount of data and caused the file position to be advanced.
+                available.value = 0;
+            }
+            return available.value;
+        } catch (ErrnoException errnoException) {
+            if (errnoException.errno == ENOTTY) {
+                // The fd is unwilling to opine about its read buffer.
+                return 0;
+            }
+            throw errnoException.rethrowAsIOException();
+        }
+    }
+
+
+    public static void bind(FileDescriptor fd, InetAddress address, int port) throws SocketException {
+        if (address instanceof Inet6Address && ((Inet6Address) address).getScopeId() == 0) {
+            // Linux won't let you bind a link-local address without a scope id. Find one.
+            NetworkInterface nif = NetworkInterface.getByInetAddress(address);
+            if (nif == null) {
+                throw new SocketException("Can't bind to a link-local address without a scope id: " + address);
+            }
+            try {
+                address = Inet6Address.getByAddress(address.getHostName(), address.getAddress(), nif.getIndex());
+            } catch (UnknownHostException ex) {
+                throw new AssertionError(ex); // Can't happen.
+            }
+        }
+        try {
+            Libcore.os.bind(fd, address, port);
+        } catch (ErrnoException errnoException) {
+            throw new BindException(errnoException.getMessage(), errnoException);
+        }
+    }
+
+
+    /**
+     * Connects socket 'fd' to 'inetAddress' on 'port', with no timeout. The lack of a timeout
+     * means this method won't throw SocketTimeoutException.
+     */
+    public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException {
+        try {
+            return IoBridge.connect(fd, inetAddress, port, 0);
+        } catch (SocketTimeoutException ex) {
+            throw new AssertionError(ex); // Can't happen for a connect without a timeout.
+        }
+    }
+
+    /**
+     * Connects socket 'fd' to 'inetAddress' on 'port', with a the given 'timeoutMs'.
+     * Use timeoutMs == 0 for a blocking connect with no timeout.
+     */
+    public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException {
+        try {
+            return connectErrno(fd, inetAddress, port, timeoutMs);
+        } catch (ErrnoException errnoException) {
+            throw new ConnectException(connectDetail(inetAddress, port, timeoutMs, errnoException), errnoException);
+        } catch (SocketException ex) {
+            throw ex; // We don't want to doubly wrap these.
+        } catch (SocketTimeoutException ex) {
+            throw ex; // We don't want to doubly wrap these.
+        } catch (IOException ex) {
+            throw new SocketException(ex);
+        }
+    }
+
+    private static boolean connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws IOException {
+        // With no timeout, just call connect(2) directly.
+        if (timeoutMs == 0) {
+            Libcore.os.connect(fd, inetAddress, port);
+            return true;
+        }
+
+        // With a timeout, we set the socket to non-blocking, connect(2), and then loop
+        // using poll(2) to decide whether we're connected, whether we should keep waiting,
+        // or whether we've seen a permanent failure and should give up.
+        long finishTimeMs = System.currentTimeMillis() + timeoutMs;
+        IoUtils.setBlocking(fd, false);
+        try {
+            try {
+                Libcore.os.connect(fd, inetAddress, port);
+                return true; // We connected immediately.
+            } catch (ErrnoException errnoException) {
+                if (errnoException.errno != EINPROGRESS) {
+                    throw errnoException;
+                }
+                // EINPROGRESS means we should keep trying...
+            }
+            int remainingTimeoutMs;
+            do {
+                remainingTimeoutMs = (int) (finishTimeMs - System.currentTimeMillis());
+                if (remainingTimeoutMs <= 0) {
+                    throw new SocketTimeoutException(connectDetail(inetAddress, port, timeoutMs, null));
+                }
+            } while (!IoBridge.isConnected(fd, inetAddress, port, timeoutMs, remainingTimeoutMs));
+            return true; // Or we'd have thrown.
+        } finally {
+            IoUtils.setBlocking(fd, true);
+        }
+    }
+
+    private static String connectDetail(InetAddress inetAddress, int port, int timeoutMs, ErrnoException cause) {
+        String detail = "failed to connect to " + inetAddress + " (port " + port + ")";
+        if (timeoutMs > 0) {
+            detail += " after " + timeoutMs + "ms";
+        }
+        if (cause != null) {
+            detail += ": " + cause.getMessage();
+        }
+        return detail;
+    }
+
+    public static void closeSocket(FileDescriptor fd) throws IOException {
+        if (!fd.valid()) {
+            // Socket.close doesn't throw if you try to close an already-closed socket.
+            return;
+        }
+        int intFd = fd.getInt$();
+        fd.setInt$(-1);
+        FileDescriptor oldFd = new FileDescriptor();
+        oldFd.setInt$(intFd);
+        AsynchronousCloseMonitor.signalBlockedThreads(oldFd);
+        try {
+            Libcore.os.close(oldFd);
+        } catch (ErrnoException errnoException) {
+            // TODO: are there any cases in which we should throw?
+        }
+    }
+
+    public static boolean isConnected(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs, int remainingTimeoutMs) throws IOException {
+        ErrnoException cause = null;
+        try {
+            StructPollfd[] pollFds = new StructPollfd[] { new StructPollfd() };
+            pollFds[0].fd = fd;
+            pollFds[0].events = (short) POLLOUT;
+            int rc = Libcore.os.poll(pollFds, remainingTimeoutMs);
+            if (rc == 0) {
+                return false; // Timeout.
+            }
+            int connectError = Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_ERROR);
+            if (connectError == 0) {
+                return true; // Success!
+            }
+            throw new ErrnoException("isConnected", connectError); // The connect(2) failed.
+        } catch (ErrnoException errnoException) {
+            if (errnoException.errno == EINTR) {
+                return false; // Punt and ask the caller to try again.
+            } else {
+                cause = errnoException;
+            }
+        }
+        // TODO: is it really helpful/necessary to throw so many different exceptions?
+        String detail = connectDetail(inetAddress, port, timeoutMs, cause);
+        if (cause.errno == ECONNRESET || cause.errno == ECONNREFUSED ||
+        cause.errno == EADDRNOTAVAIL || cause.errno == EADDRINUSE ||
+        cause.errno == ENETUNREACH) {
+            throw new ConnectException(detail, cause);
+        } else if (cause.errno == EACCES) {
+            throw new SecurityException(detail, cause);
+        } else if (cause.errno == ETIMEDOUT) {
+            throw new SocketTimeoutException(detail, cause);
+        }
+        throw new SocketException(detail, cause);
+    }
+
+    // Socket options used by java.net but not exposed in SocketOptions.
+    public static final int JAVA_MCAST_JOIN_GROUP = 19;
+    public static final int JAVA_MCAST_LEAVE_GROUP = 20;
+    public static final int JAVA_IP_MULTICAST_TTL = 17;
+
+    /**
+     * java.net has its own socket options similar to the underlying Unix ones. We paper over the
+     * differences here.
+     */
+    public static Object getSocketOption(FileDescriptor fd, int option) throws SocketException {
+        try {
+            return getSocketOptionErrno(fd, option);
+        } catch (ErrnoException errnoException) {
+            throw errnoException.rethrowAsSocketException();
+        }
+    }
+
+    private static Object getSocketOptionErrno(FileDescriptor fd, int option) throws SocketException {
+        switch (option) {
+        case SocketOptions.IP_MULTICAST_IF:
+            // This is IPv4-only.
+            return Libcore.os.getsockoptInAddr(fd, IPPROTO_IP, IP_MULTICAST_IF);
+        case SocketOptions.IP_MULTICAST_IF2:
+            // This is IPv6-only.
+            return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF);
+        case SocketOptions.IP_MULTICAST_LOOP:
+            // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
+            // it doesn't matter which we return.
+            return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP));
+        case IoBridge.JAVA_IP_MULTICAST_TTL:
+            // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
+            // it doesn't matter which we return.
+            return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
+        case SocketOptions.IP_TOS:
+            // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
+            // it doesn't matter which we return.
+            return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS);
+        case SocketOptions.SO_BROADCAST:
+            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_BROADCAST));
+        case SocketOptions.SO_KEEPALIVE:
+            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE));
+        case SocketOptions.SO_LINGER:
+            StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER);
+            if (!linger.isOn()) {
+                return false;
+            }
+            return linger.l_linger;
+        case SocketOptions.SO_OOBINLINE:
+            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE));
+        case SocketOptions.SO_RCVBUF:
+            return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF);
+        case SocketOptions.SO_REUSEADDR:
+            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR));
+        case SocketOptions.SO_SNDBUF:
+            return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF);
+        case SocketOptions.SO_TIMEOUT:
+            return (int) Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO).toMillis();
+        case SocketOptions.TCP_NODELAY:
+            return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY));
+        default:
+            throw new SocketException("Unknown socket option: " + option);
+        }
+    }
+
+    private static boolean booleanFromInt(int i) {
+        return (i != 0);
+    }
+
+    private static int booleanToInt(boolean b) {
+        return b ? 1 : 0;
+    }
+
+    /**
+     * java.net has its own socket options similar to the underlying Unix ones. We paper over the
+     * differences here.
+     */
+    public static void setSocketOption(FileDescriptor fd, int option, Object value) throws SocketException {
+        try {
+            setSocketOptionErrno(fd, option, value);
+        } catch (ErrnoException errnoException) {
+            throw errnoException.rethrowAsSocketException();
+        }
+    }
+
+    private static void setSocketOptionErrno(FileDescriptor fd, int option, Object value) throws SocketException {
+        switch (option) {
+        case SocketOptions.IP_MULTICAST_IF:
+            throw new UnsupportedOperationException("Use IP_MULTICAST_IF2 on Android");
+        case SocketOptions.IP_MULTICAST_IF2:
+            // Although IPv6 was cleaned up to use int, IPv4 uses an ip_mreqn containing an int.
+            Libcore.os.setsockoptIpMreqn(fd, IPPROTO_IP, IP_MULTICAST_IF, (Integer) value);
+            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (Integer) value);
+            return;
+        case SocketOptions.IP_MULTICAST_LOOP:
+            // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte.
+            Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_LOOP, booleanToInt((Boolean) value));
+            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, booleanToInt((Boolean) value));
+            return;
+        case IoBridge.JAVA_IP_MULTICAST_TTL:
+            // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int,
+            // IPv4 multicast TTL uses a byte.
+            Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_TTL, (Integer) value);
+            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (Integer) value);
+            return;
+        case SocketOptions.IP_TOS:
+            Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_TOS, (Integer) value);
+            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS, (Integer) value);
+            return;
+        case SocketOptions.SO_BROADCAST:
+            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_BROADCAST, booleanToInt((Boolean) value));
+            return;
+        case SocketOptions.SO_KEEPALIVE:
+            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE, booleanToInt((Boolean) value));
+            return;
+        case SocketOptions.SO_LINGER:
+            boolean on = false;
+            int seconds = 0;
+            if (value instanceof Integer) {
+                on = true;
+                seconds = Math.min((Integer) value, 65535);
+            }
+            StructLinger linger = new StructLinger(booleanToInt(on), seconds);
+            Libcore.os.setsockoptLinger(fd, SOL_SOCKET, SO_LINGER, linger);
+            return;
+        case SocketOptions.SO_OOBINLINE:
+            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE, booleanToInt((Boolean) value));
+            return;
+        case SocketOptions.SO_RCVBUF:
+            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, (Integer) value);
+            return;
+        case SocketOptions.SO_REUSEADDR:
+            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR, booleanToInt((Boolean) value));
+            return;
+        case SocketOptions.SO_SNDBUF:
+            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_SNDBUF, (Integer) value);
+            return;
+        case SocketOptions.SO_TIMEOUT:
+            int millis = (Integer) value;
+            StructTimeval tv = StructTimeval.fromMillis(millis);
+            Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv);
+            return;
+        case SocketOptions.TCP_NODELAY:
+            Libcore.os.setsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY, booleanToInt((Boolean) value));
+            return;
+        case IoBridge.JAVA_MCAST_JOIN_GROUP:
+        case IoBridge.JAVA_MCAST_LEAVE_GROUP:
+            StructGroupReq groupReq = (StructGroupReq) value;
+            int level = (groupReq.gr_group instanceof Inet4Address) ? IPPROTO_IP : IPPROTO_IPV6;
+            int op = (option == JAVA_MCAST_JOIN_GROUP) ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP;
+            Libcore.os.setsockoptGroupReq(fd, level, op, groupReq);
+            return;
+        default:
+            throw new SocketException("Unknown socket option: " + option);
+        }
+    }
+
+    /**
+     * java.io only throws FileNotFoundException when opening files, regardless of what actually
+     * went wrong. Additionally, java.io is more restrictive than POSIX when it comes to opening
+     * directories: POSIX says read-only is okay, but java.io doesn't even allow that. We also
+     * have an Android-specific hack to alter the default permissions.
+     */
+    public static FileDescriptor open(String path, int flags) throws FileNotFoundException {
+        FileDescriptor fd = null;
+        try {
+            // On Android, we don't want default permissions to allow global access.
+            int mode = ((flags & O_ACCMODE) == O_RDONLY) ? 0 : 0600;
+            fd = Libcore.os.open(path, flags, mode);
+            if (fd.valid()) {
+                // Posix open(2) fails with EISDIR only if you ask for write permission.
+                // Java disallows reading directories too.
+                boolean isDirectory = false;
+                if (S_ISDIR(Libcore.os.fstat(fd).st_mode)) {
+                    throw new ErrnoException("open", EISDIR);
+                }
+            }
+            return fd;
+        } catch (ErrnoException errnoException) {
+            try {
+                if (fd != null) {
+                    IoUtils.close(fd);
+                }
+            } catch (IOException ignored) {
+            }
+            FileNotFoundException ex = new FileNotFoundException(path + ": " + errnoException.getMessage());
+            ex.initCause(errnoException);
+            throw ex;
+        }
+    }
+
+    /**
+     * java.io thinks that a read at EOF is an error and should return -1, contrary to traditional
+     * Unix practice where you'd read until you got 0 bytes (and any future read would return -1).
+     */
+    public static int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
+        Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
+        if (byteCount == 0) {
+            return 0;
+        }
+        try {
+            int readCount = Libcore.os.read(fd, bytes, byteOffset, byteCount);
+            if (readCount == 0) {
+                return -1;
+            }
+            return readCount;
+        } catch (ErrnoException errnoException) {
+            if (errnoException.errno == EAGAIN) {
+                // We return 0 rather than throw if we try to read from an empty non-blocking pipe.
+                return 0;
+            }
+            throw errnoException.rethrowAsIOException();
+        }
+    }
+
+    /**
+     * java.io always writes every byte it's asked to, or fails with an error. (That is, unlike
+     * Unix it never just writes as many bytes as happens to be convenient.)
+     */
+    public static void write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
+        Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
+        if (byteCount == 0) {
+            return;
+        }
+        try {
+            while (byteCount > 0) {
+                int bytesWritten = Libcore.os.write(fd, bytes, byteOffset, byteCount);
+                byteCount -= bytesWritten;
+                byteOffset += bytesWritten;
+            }
+        } catch (ErrnoException errnoException) {
+            throw errnoException.rethrowAsIOException();
+        }
+    }
+
+    public static int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws IOException {
+        boolean isDatagram = (inetAddress != null);
+        if (!isDatagram && byteCount <= 0) {
+            return 0;
+        }
+        int result;
+        try {
+            result = Libcore.os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port);
+        } catch (ErrnoException errnoException) {
+            result = maybeThrowAfterSendto(isDatagram, errnoException);
+        }
+        return result;
+    }
+
+    public static int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws IOException {
+        boolean isDatagram = (inetAddress != null);
+        if (!isDatagram && buffer.remaining() == 0) {
+            return 0;
+        }
+        int result;
+        try {
+            result = Libcore.os.sendto(fd, buffer, flags, inetAddress, port);
+        } catch (ErrnoException errnoException) {
+            result = maybeThrowAfterSendto(isDatagram, errnoException);
+        }
+        return result;
+    }
+
+    private static int maybeThrowAfterSendto(boolean isDatagram, ErrnoException errnoException) throws SocketException {
+        if (isDatagram) {
+            if (errnoException.errno == ECONNRESET || errnoException.errno == ECONNREFUSED) {
+                return 0;
+            }
+        } else {
+            if (errnoException.errno == EAGAIN || errnoException.errno == EWOULDBLOCK) {
+                // We were asked to write to a non-blocking socket, but were told
+                // it would block, so report "no bytes written".
+                return 0;
+            }
+        }
+        throw errnoException.rethrowAsSocketException();
+    }
+
+    public static int recvfrom(boolean isRead, FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
+        int result;
+        try {
+            InetSocketAddress srcAddress = (packet != null && !isConnected) ? new InetSocketAddress() : null;
+            result = Libcore.os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
+            result = postRecvfrom(isRead, packet, isConnected, srcAddress, result);
+        } catch (ErrnoException errnoException) {
+            result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException);
+        }
+        return result;
+    }
+
+    public static int recvfrom(boolean isRead, FileDescriptor fd, ByteBuffer buffer, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
+        int result;
+        try {
+            InetSocketAddress srcAddress = (packet != null && !isConnected) ? new InetSocketAddress() : null;
+            result = Libcore.os.recvfrom(fd, buffer, flags, srcAddress);
+            result = postRecvfrom(isRead, packet, isConnected, srcAddress, result);
+        } catch (ErrnoException errnoException) {
+            result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException);
+        }
+        return result;
+    }
+
+    private static int postRecvfrom(boolean isRead, DatagramPacket packet, boolean isConnected, InetSocketAddress srcAddress, int byteCount) {
+        if (isRead && byteCount == 0) {
+            return -1;
+        }
+        if (packet != null) {
+            packet.setLength(byteCount);
+            if (!isConnected) {
+                packet.setAddress(srcAddress.getAddress());
+                packet.setPort(srcAddress.getPort());
+            }
+        }
+        return byteCount;
+    }
+
+    private static int maybeThrowAfterRecvfrom(boolean isRead, boolean isConnected, ErrnoException errnoException) throws SocketException, SocketTimeoutException {
+        if (isRead) {
+            if (errnoException.errno == EAGAIN || errnoException.errno == EWOULDBLOCK) {
+                return 0;
+            } else {
+                throw errnoException.rethrowAsSocketException();
+            }
+        } else {
+            if (isConnected && errnoException.errno == ECONNREFUSED) {
+                throw new PortUnreachableException("", errnoException);
+            } else if (errnoException.errno == EAGAIN || errnoException.errno == EWOULDBLOCK) {
+                throw new SocketTimeoutException(errnoException);
+            } else {
+                throw errnoException.rethrowAsSocketException();
+            }
+        }
+    }
+
+    public static FileDescriptor socket(boolean stream) throws SocketException {
+        FileDescriptor fd;
+        try {
+            fd = Libcore.os.socket(AF_INET6, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
+
+            // The RFC (http://www.ietf.org/rfc/rfc3493.txt) says that IPV6_MULTICAST_HOPS defaults
+            // to 1. The Linux kernel (at least up to 2.6.38) accidentally defaults to 64 (which
+            // would be correct for the *unicast* hop limit).
+            // See http://www.spinics.net/lists/netdev/msg129022.html, though no patch appears to
+            // have been applied as a result of that discussion. If that bug is ever fixed, we can
+            // remove this code. Until then, we manually set the hop limit on IPv6 datagram sockets.
+            // (IPv4 is already correct.)
+            if (!stream) {
+                Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1);
+            }
+
+            return fd;
+        } catch (ErrnoException errnoException) {
+            throw errnoException.rethrowAsSocketException();
+        }
+    }
+
+    public static InetAddress getSocketLocalAddress(FileDescriptor fd) {
+        SocketAddress sa = Libcore.os.getsockname(fd);
+        InetSocketAddress isa = (InetSocketAddress) sa;
+        return isa.getAddress();
+    }
+
+    public static int getSocketLocalPort(FileDescriptor fd) {
+        SocketAddress sa = Libcore.os.getsockname(fd);
+        InetSocketAddress isa = (InetSocketAddress) sa;
+        return isa.getPort();
+    }
+
+}
diff --git a/luni/src/main/java/libcore/io/IoUtils.java b/luni/src/main/java/libcore/io/IoUtils.java
index 4a9772e..6680882 100644
--- a/luni/src/main/java/libcore/io/IoUtils.java
+++ b/luni/src/main/java/libcore/io/IoUtils.java
@@ -19,28 +19,10 @@
 import java.io.Closeable;
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.RandomAccessFile;
-import java.net.BindException;
-import java.net.ConnectException;
-import java.net.DatagramPacket;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.NetworkInterface;
-import java.net.PortUnreachableException;
 import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.net.SocketOptions;
-import java.net.SocketTimeoutException;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
 import java.nio.charset.Charsets;
-import java.util.Arrays;
-import libcore.util.MutableInt;
 import static libcore.io.OsConstants.*;
 
 public final class IoUtils {
@@ -48,229 +30,6 @@
     }
 
     /**
-     * Implements java.io/java.net "available" semantics.
-     */
-    public static int available(FileDescriptor fd) throws IOException {
-        try {
-            MutableInt available = new MutableInt(0);
-            int rc = Libcore.os.ioctlInt(fd, FIONREAD, available);
-            if (available.value < 0) {
-                // If the fd refers to a regular file, the result is the difference between
-                // the file size and the file position. This may be negative if the position
-                // is past the end of the file. If the fd refers to a special file masquerading
-                // as a regular file, the result may be negative because the special file
-                // may appear to have zero size and yet a previous read call may have
-                // read some amount of data and caused the file position to be advanced.
-                available.value = 0;
-            }
-            return available.value;
-        } catch (ErrnoException errnoException) {
-            if (errnoException.errno == ENOTTY) {
-                // The fd is unwilling to opine about its read buffer.
-                return 0;
-            }
-            throw errnoException.rethrowAsIOException();
-        }
-    }
-
-    /**
-     * java.io only throws FileNotFoundException when opening files, regardless of what actually
-     * went wrong. Additionally, java.io is more restrictive than POSIX when it comes to opening
-     * directories: POSIX says read-only is okay, but java.io doesn't even allow that. We also
-     * have an Android-specific hack to alter the default permissions.
-     */
-    public static FileDescriptor open(String path, int flags) throws FileNotFoundException {
-        FileDescriptor fd = null;
-        try {
-            // On Android, we don't want default permissions to allow global access.
-            int mode = ((flags & O_ACCMODE) == O_RDONLY) ? 0 : 0600;
-            fd = Libcore.os.open(path, flags, mode);
-            if (fd.valid()) {
-                // Posix open(2) fails with EISDIR only if you ask for write permission.
-                // Java disallows reading directories too.
-                boolean isDirectory = false;
-                if (S_ISDIR(Libcore.os.fstat(fd).st_mode)) {
-                    throw new ErrnoException("open", EISDIR);
-                }
-            }
-            return fd;
-        } catch (ErrnoException errnoException) {
-            try {
-                if (fd != null) {
-                    close(fd);
-                }
-            } catch (IOException ignored) {
-            }
-            FileNotFoundException ex = new FileNotFoundException(path + ": " + errnoException.getMessage());
-            ex.initCause(errnoException);
-            throw ex;
-        }
-    }
-
-    /**
-     * java.io thinks that a read at EOF is an error and should return -1, contrary to traditional
-     * Unix practice where you'd read until you got 0 bytes (and any future read would return -1).
-     */
-    public static int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
-        Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
-        if (byteCount == 0) {
-            return 0;
-        }
-        try {
-            int readCount = Libcore.os.read(fd, bytes, byteOffset, byteCount);
-            if (readCount == 0) {
-                return -1;
-            }
-            return readCount;
-        } catch (ErrnoException errnoException) {
-            if (errnoException.errno == EAGAIN) {
-                // We return 0 rather than throw if we try to read from an empty non-blocking pipe.
-                return 0;
-            }
-            throw errnoException.rethrowAsIOException();
-        }
-    }
-
-    /**
-     * java.io always writes every byte it's asked to, or fails with an error. (That is, unlike
-     * Unix it never just writes as many bytes as happens to be convenient.)
-     */
-    public static void write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
-        Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
-        if (byteCount == 0) {
-            return;
-        }
-        try {
-            while (byteCount > 0) {
-                int bytesWritten = Libcore.os.write(fd, bytes, byteOffset, byteCount);
-                byteCount -= bytesWritten;
-                byteOffset += bytesWritten;
-            }
-        } catch (ErrnoException errnoException) {
-            throw errnoException.rethrowAsIOException();
-        }
-    }
-
-    public static int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws IOException {
-        boolean isDatagram = (inetAddress != null);
-        if (!isDatagram && byteCount <= 0) {
-            return 0;
-        }
-        int result;
-        try {
-            result = Libcore.os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port);
-        } catch (ErrnoException errnoException) {
-            result = maybeThrowAfterSendto(isDatagram, errnoException);
-        }
-        return result;
-    }
-
-    public static int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws IOException {
-        boolean isDatagram = (inetAddress != null);
-        if (!isDatagram && buffer.remaining() == 0) {
-            return 0;
-        }
-        int result;
-        try {
-            result = Libcore.os.sendto(fd, buffer, flags, inetAddress, port);
-        } catch (ErrnoException errnoException) {
-            result = maybeThrowAfterSendto(isDatagram, errnoException);
-        }
-        return result;
-    }
-
-    private static int maybeThrowAfterSendto(boolean isDatagram, ErrnoException errnoException) throws SocketException {
-        if (isDatagram) {
-            if (errnoException.errno == ECONNRESET || errnoException.errno == ECONNREFUSED) {
-                return 0;
-            }
-        } else {
-            if (errnoException.errno == EAGAIN || errnoException.errno == EWOULDBLOCK) {
-                // We were asked to write to a non-blocking socket, but were told
-                // it would block, so report "no bytes written".
-                return 0;
-            }
-        }
-        throw errnoException.rethrowAsSocketException();
-    }
-
-    public static int recvfrom(boolean isRead, FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
-        int result;
-        try {
-            InetSocketAddress isa = (packet != null && !isConnected) ? new InetSocketAddress() : null;
-            result = Libcore.os.recvfrom(fd, bytes, byteOffset, byteCount, flags, isa);
-            result = postRecvfrom(isRead, packet, isConnected, isa, result);
-        } catch (ErrnoException errnoException) {
-            result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException);
-        }
-        return result;
-    }
-
-    public static int recvfrom(boolean isRead, FileDescriptor fd, ByteBuffer buffer, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
-        int result;
-        try {
-            InetSocketAddress isa = (packet != null && !isConnected) ? new InetSocketAddress() : null;
-            result = Libcore.os.recvfrom(fd, buffer, flags, isa);
-            result = postRecvfrom(isRead, packet, isConnected, isa, result);
-        } catch (ErrnoException errnoException) {
-            result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException);
-        }
-        return result;
-    }
-
-    private static int postRecvfrom(boolean isRead, DatagramPacket packet, boolean isConnected, InetSocketAddress isa, int byteCount) {
-        if (isRead && byteCount == 0) {
-            return -1;
-        }
-        if (packet != null) {
-            packet.setLength(byteCount);
-            if (!isConnected) {
-                packet.setAddress(isa.getAddress());
-                packet.setPort(isa.getPort());
-            }
-        }
-        return byteCount;
-    }
-
-    private static int maybeThrowAfterRecvfrom(boolean isRead, boolean isConnected, ErrnoException errnoException) throws SocketException, SocketTimeoutException {
-        if (isRead) {
-            if (errnoException.errno == EAGAIN || errnoException.errno == EWOULDBLOCK) {
-                return 0;
-            } else {
-                throw errnoException.rethrowAsSocketException();
-            }
-        } else {
-            if (isConnected && errnoException.errno == ECONNREFUSED) {
-                throw new PortUnreachableException("", errnoException);
-            } else if (errnoException.errno == EAGAIN || errnoException.errno == EWOULDBLOCK) {
-                throw new SocketTimeoutException("", errnoException);
-            } else {
-                throw errnoException.rethrowAsSocketException();
-            }
-        }
-    }
-
-    public static void bind(FileDescriptor fd, InetAddress address, int port) throws SocketException {
-        if (address instanceof Inet6Address && ((Inet6Address) address).getScopeId() == 0) {
-            // Linux won't let you bind a link-local address without a scope id. Find one.
-            NetworkInterface nif = NetworkInterface.getByInetAddress(address);
-            if (nif == null) {
-                throw new SocketException("Can't bind to a link-local address without a scope id: " + address);
-            }
-            try {
-                address = Inet6Address.getByAddress(address.getHostName(), address.getAddress(), nif.getIndex());
-            } catch (UnknownHostException ex) {
-                throw new AssertionError(ex); // Can't happen.
-            }
-        }
-        try {
-            Libcore.os.bind(fd, address, port);
-        } catch (ErrnoException errnoException) {
-            throw new BindException(errnoException.getMessage(), errnoException);
-        }
-    }
-
-    /**
      * Calls close(2) on 'fd'. Also resets the internal int to -1. Does nothing if 'fd' is null
      * or invalid.
      */
@@ -318,135 +77,6 @@
         }
     }
 
-    public static void closeSocket(FileDescriptor fd) throws IOException {
-        if (!fd.valid()) {
-            // Socket.close doesn't throw if you try to close an already-closed socket.
-            return;
-        }
-        int intFd = fd.getInt$();
-        fd.setInt$(-1);
-        FileDescriptor oldFd = new FileDescriptor();
-        oldFd.setInt$(intFd);
-        AsynchronousCloseMonitor.signalBlockedThreads(oldFd);
-        try {
-            Libcore.os.close(oldFd);
-        } catch (ErrnoException errnoException) {
-            // TODO: are there any cases in which we should throw?
-        }
-    }
-
-    /**
-     * Connects socket 'fd' to 'inetAddress' on 'port', with no timeout. The lack of a timeout
-     * means this method won't throw SocketTimeoutException.
-     */
-    public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException {
-        try {
-            return IoUtils.connect(fd, inetAddress, port, 0);
-        } catch (SocketTimeoutException ex) {
-            throw new AssertionError(ex); // Can't happen for a connect without a timeout.
-        }
-    }
-
-    /**
-     * Connects socket 'fd' to 'inetAddress' on 'port', with a the given 'timeoutMs'.
-     * Use timeoutMs == 0 for a blocking connect with no timeout.
-     */
-    public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException {
-        try {
-            return connectErrno(fd, inetAddress, port, timeoutMs);
-        } catch (ErrnoException errnoException) {
-            throw new ConnectException(connectDetail(inetAddress, port, timeoutMs, errnoException), errnoException);
-        } catch (SocketException ex) {
-            throw ex; // We don't want to doubly wrap these.
-        } catch (SocketTimeoutException ex) {
-            throw ex; // We don't want to doubly wrap these.
-        } catch (IOException ex) {
-            throw new SocketException(ex);
-        }
-    }
-
-    private static boolean connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws IOException {
-        // With no timeout, just call connect(2) directly.
-        if (timeoutMs == 0) {
-            Libcore.os.connect(fd, inetAddress, port);
-            return true;
-        }
-
-        // With a timeout, we set the socket to non-blocking, connect(2), and then loop
-        // using poll(2) to decide whether we're connected, whether we should keep waiting,
-        // or whether we've seen a permanent failure and should give up.
-        long finishTimeMs = System.currentTimeMillis() + timeoutMs;
-        IoUtils.setBlocking(fd, false);
-        try {
-            try {
-                Libcore.os.connect(fd, inetAddress, port);
-                return true; // We connected immediately.
-            } catch (ErrnoException errnoException) {
-                if (errnoException.errno != EINPROGRESS) {
-                    throw errnoException;
-                }
-                // EINPROGRESS means we should keep trying...
-            }
-            int remainingTimeoutMs;
-            do {
-                remainingTimeoutMs = (int) (finishTimeMs - System.currentTimeMillis());
-                if (remainingTimeoutMs <= 0) {
-                    throw new SocketTimeoutException(connectDetail(inetAddress, port, timeoutMs, null));
-                }
-            } while (!IoUtils.isConnected(fd, inetAddress, port, timeoutMs, remainingTimeoutMs));
-            return true; // Or we'd have thrown.
-        } finally {
-            IoUtils.setBlocking(fd, true);
-        }
-    }
-
-    private static String connectDetail(InetAddress inetAddress, int port, int timeoutMs, ErrnoException cause) {
-        String detail = "failed to connect to " + inetAddress + " (port " + port + ")";
-        if (timeoutMs > 0) {
-            detail += " after " + timeoutMs + "ms";
-        }
-        if (cause != null) {
-            detail += ": " + cause.getMessage();
-        }
-        return detail;
-    }
-
-    public static boolean isConnected(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs, int remainingTimeoutMs) throws IOException {
-        ErrnoException cause = null;
-        try {
-            StructPollfd[] pollFds = new StructPollfd[] { new StructPollfd() };
-            pollFds[0].fd = fd;
-            pollFds[0].events = (short) POLLOUT;
-            int rc = Libcore.os.poll(pollFds, remainingTimeoutMs);
-            if (rc == 0) {
-                return false; // Timeout.
-            }
-            int connectError = Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_ERROR);
-            if (connectError == 0) {
-                return true; // Success!
-            }
-            throw new ErrnoException("isConnected", connectError); // The connect(2) failed.
-        } catch (ErrnoException errnoException) {
-            if (errnoException.errno == EINTR) {
-                return false; // Punt and ask the caller to try again.
-            } else {
-                cause = errnoException;
-            }
-        }
-        // TODO: is it really helpful/necessary to throw so many different exceptions?
-        String detail = connectDetail(inetAddress, port, timeoutMs, cause);
-        if (cause.errno == ECONNRESET || cause.errno == ECONNREFUSED ||
-                cause.errno == EADDRNOTAVAIL || cause.errno == EADDRINUSE ||
-                cause.errno == ENETUNREACH) {
-            throw new ConnectException(detail, cause);
-        } else if (cause.errno == EACCES) {
-            throw new SecurityException(detail, cause);
-        } else if (cause.errno == ETIMEDOUT) {
-            throw new SocketTimeoutException(detail, cause);
-        }
-        throw new SocketException(detail, cause);
-    }
-
     /**
      * Sets 'fd' to be blocking or non-blocking, according to the state of 'blocking'.
      */
@@ -464,196 +94,6 @@
         }
     }
 
-    public static FileDescriptor socket(boolean stream) throws SocketException {
-        FileDescriptor fd;
-        try {
-            fd = Libcore.os.socket(AF_INET6, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
-
-            // The RFC (http://www.ietf.org/rfc/rfc3493.txt) says that IPV6_MULTICAST_HOPS defaults
-            // to 1. The Linux kernel (at least up to 2.6.38) accidentally defaults to 64 (which
-            // would be correct for the *unicast* hop limit).
-            // See http://www.spinics.net/lists/netdev/msg129022.html, though no patch appears to
-            // have been applied as a result of that discussion. If that bug is ever fixed, we can
-            // remove this code. Until then, we manually set the hop limit on IPv6 datagram sockets.
-            // (IPv4 is already correct.)
-            if (!stream) {
-                Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1);
-            }
-
-            return fd;
-        } catch (ErrnoException errnoException) {
-            throw errnoException.rethrowAsSocketException();
-        }
-    }
-
-    // Socket options used by java.net but not exposed in SocketOptions.
-    public static final int JAVA_MCAST_JOIN_GROUP = 19;
-    public static final int JAVA_MCAST_LEAVE_GROUP = 20;
-    public static final int JAVA_IP_MULTICAST_TTL = 17;
-
-    /**
-     * java.net has its own socket options similar to the underlying Unix ones. We paper over the
-     * differences here.
-     */
-    public static Object getSocketOption(FileDescriptor fd, int option) throws SocketException {
-        try {
-            return getSocketOptionErrno(fd, option);
-        } catch (ErrnoException errnoException) {
-            throw errnoException.rethrowAsSocketException();
-        }
-    }
-
-    private static Object getSocketOptionErrno(FileDescriptor fd, int option) throws SocketException {
-        switch (option) {
-        case SocketOptions.IP_MULTICAST_IF:
-            // This is IPv4-only.
-            return Libcore.os.getsockoptInAddr(fd, IPPROTO_IP, IP_MULTICAST_IF);
-        case SocketOptions.IP_MULTICAST_IF2:
-            // This is IPv6-only.
-            return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF);
-        case SocketOptions.IP_MULTICAST_LOOP:
-            // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
-            // it doesn't matter which we return.
-            return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP));
-        case IoUtils.JAVA_IP_MULTICAST_TTL:
-            // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
-            // it doesn't matter which we return.
-            return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
-        case SocketOptions.IP_TOS:
-            // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
-            // it doesn't matter which we return.
-            return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS);
-        case SocketOptions.SO_BROADCAST:
-            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_BROADCAST));
-        case SocketOptions.SO_KEEPALIVE:
-            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE));
-        case SocketOptions.SO_LINGER:
-            StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER);
-            if (!linger.isOn()) {
-                return false;
-            }
-            return linger.l_linger;
-        case SocketOptions.SO_OOBINLINE:
-            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE));
-        case SocketOptions.SO_RCVBUF:
-            return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF);
-        case SocketOptions.SO_REUSEADDR:
-            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR));
-        case SocketOptions.SO_SNDBUF:
-            return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF);
-        case SocketOptions.SO_TIMEOUT:
-            return (int) Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO).toMillis();
-        case SocketOptions.TCP_NODELAY:
-            return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY));
-        default:
-            throw new SocketException("Unknown socket option: " + option);
-        }
-    }
-
-    private static boolean booleanFromInt(int i) {
-        return (i != 0);
-    }
-
-    private static int booleanToInt(boolean b) {
-        return b ? 1 : 0;
-    }
-
-    /**
-     * java.net has its own socket options similar to the underlying Unix ones. We paper over the
-     * differences here.
-     */
-    public static void setSocketOption(FileDescriptor fd, int option, Object value) throws SocketException {
-        try {
-            setSocketOptionErrno(fd, option, value);
-        } catch (ErrnoException errnoException) {
-            throw errnoException.rethrowAsSocketException();
-        }
-    }
-
-    private static void setSocketOptionErrno(FileDescriptor fd, int option, Object value) throws SocketException {
-        switch (option) {
-        case SocketOptions.IP_MULTICAST_IF:
-            throw new UnsupportedOperationException("Use IP_MULTICAST_IF2 on Android");
-        case SocketOptions.IP_MULTICAST_IF2:
-            // Although IPv6 was cleaned up to use int, IPv4 uses an ip_mreqn containing an int.
-            Libcore.os.setsockoptIpMreqn(fd, IPPROTO_IP, IP_MULTICAST_IF, (Integer) value);
-            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (Integer) value);
-            return;
-        case SocketOptions.IP_MULTICAST_LOOP:
-            // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte.
-            Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_LOOP, booleanToInt((Boolean) value));
-            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, booleanToInt((Boolean) value));
-            return;
-        case IoUtils.JAVA_IP_MULTICAST_TTL:
-            // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int,
-            // IPv4 multicast TTL uses a byte.
-            Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_TTL, (Integer) value);
-            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (Integer) value);
-            return;
-        case SocketOptions.IP_TOS:
-            Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_TOS, (Integer) value);
-            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS, (Integer) value);
-            return;
-        case SocketOptions.SO_BROADCAST:
-            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_BROADCAST, booleanToInt((Boolean) value));
-            return;
-        case SocketOptions.SO_KEEPALIVE:
-            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE, booleanToInt((Boolean) value));
-            return;
-        case SocketOptions.SO_LINGER:
-            boolean on = false;
-            int seconds = 0;
-            if (value instanceof Integer) {
-                on = true;
-                seconds = Math.min((Integer) value, 65535);
-            }
-            StructLinger linger = new StructLinger(booleanToInt(on), seconds);
-            Libcore.os.setsockoptLinger(fd, SOL_SOCKET, SO_LINGER, linger);
-            return;
-        case SocketOptions.SO_OOBINLINE:
-            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE, booleanToInt((Boolean) value));
-            return;
-        case SocketOptions.SO_RCVBUF:
-            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, (Integer) value);
-            return;
-        case SocketOptions.SO_REUSEADDR:
-            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR, booleanToInt((Boolean) value));
-            return;
-        case SocketOptions.SO_SNDBUF:
-            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_SNDBUF, (Integer) value);
-            return;
-        case SocketOptions.SO_TIMEOUT:
-            int millis = (Integer) value;
-            StructTimeval tv = StructTimeval.fromMillis(millis);
-            Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv);
-            return;
-        case SocketOptions.TCP_NODELAY:
-            Libcore.os.setsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY, booleanToInt((Boolean) value));
-            return;
-        case IoUtils.JAVA_MCAST_JOIN_GROUP:
-        case IoUtils.JAVA_MCAST_LEAVE_GROUP:
-            StructGroupReq groupReq = (StructGroupReq) value;
-            int level = (groupReq.gr_group instanceof Inet4Address) ? IPPROTO_IP : IPPROTO_IPV6;
-            int op = (option == JAVA_MCAST_JOIN_GROUP) ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP;
-            Libcore.os.setsockoptGroupReq(fd, level, op, groupReq);
-            return;
-        default:
-            throw new SocketException("Unknown socket option: " + option);
-        }
-    }
-
-    public static InetAddress getSocketLocalAddress(FileDescriptor fd) {
-        SocketAddress sa = Libcore.os.getsockname(fd);
-        InetSocketAddress isa = (InetSocketAddress) sa;
-        return isa.getAddress();
-    }
-
-    public static int getSocketLocalPort(FileDescriptor fd) {
-        SocketAddress sa = Libcore.os.getsockname(fd);
-        InetSocketAddress isa = (InetSocketAddress) sa;
-        return isa.getPort();
-    }
-
     /**
      * Returns the contents of 'path' as a byte array.
      */
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index e44cfb9..bd50998 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -25,6 +25,7 @@
 import libcore.util.MutableLong;
 
 public interface Os {
+    public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException;
     public boolean access(String path, int mode) throws ErrnoException;
     public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException;
     public void chmod(String path, int mode) throws ErrnoException;
@@ -83,8 +84,8 @@
     public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
     public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException;
     public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
-    public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress inetSocketAddress) throws ErrnoException;
-    public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress inetSocketAddress) throws ErrnoException;
+    public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException;
+    public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException;
     public void remove(String path) throws ErrnoException;
     public void rename(String oldPath, String newPath) throws ErrnoException;
     public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException;
@@ -110,6 +111,7 @@
     public void symlink(String oldPath, String newPath) throws ErrnoException;
     public long sysconf(int name);
     public StructUtsname uname() throws ErrnoException;
+    public int waitpid(int pid, MutableInt status, int options) throws ErrnoException;
     public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
     public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException;
     public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index 2f52d752..f976209 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -28,6 +28,7 @@
 public final class Posix implements Os {
     Posix() { }
 
+    public native FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException;
     public native boolean access(String path, int mode) throws ErrnoException;
     public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException;
     public native void chmod(String path, int mode) throws ErrnoException;
@@ -94,18 +95,18 @@
     }
     private native int readBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException;
     public native int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
-    public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress inetSocketAddress) throws ErrnoException {
+    public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException {
         if (buffer.isDirect()) {
-            return recvfromBytes(fd, buffer, buffer.position(), buffer.remaining(), flags, inetSocketAddress);
+            return recvfromBytes(fd, buffer, buffer.position(), buffer.remaining(), flags, srcAddress);
         } else {
-            return recvfromBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), flags, inetSocketAddress);
+            return recvfromBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), flags, srcAddress);
         }
     }
-    public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress inetSocketAddress) throws ErrnoException {
+    public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException {
         // This indirection isn't strictly necessary, but ensures that our public interface is type safe.
-        return recvfromBytes(fd, bytes, byteOffset, byteCount, flags, inetSocketAddress);
+        return recvfromBytes(fd, bytes, byteOffset, byteCount, flags, srcAddress);
     }
-    private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress inetSocketAddress) throws ErrnoException;
+    private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException;
     public native void remove(String path) throws ErrnoException;
     public native void rename(String oldPath, String newPath) throws ErrnoException;
     public native long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException;
@@ -140,6 +141,7 @@
     public native void symlink(String oldPath, String newPath) throws ErrnoException;
     public native long sysconf(int name);
     public native StructUtsname uname() throws ErrnoException;
+    public native int waitpid(int pid, MutableInt status, int options) throws ErrnoException;
     public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
         if (buffer.isDirect()) {
             return writeBytes(fd, buffer, buffer.position(), buffer.remaining());
diff --git a/luni/src/main/java/libcore/net/RawSocket.java b/luni/src/main/java/libcore/net/RawSocket.java
index 2907964..bb29fb0 100644
--- a/luni/src/main/java/libcore/net/RawSocket.java
+++ b/luni/src/main/java/libcore/net/RawSocket.java
@@ -22,7 +22,7 @@
 import java.io.IOException;
 import java.net.SocketException;
 import java.util.Arrays;
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
 
 /**
  * This class allows raw L2 packets to be sent and received via the
@@ -111,7 +111,7 @@
      */
     public void close() throws IOException {
         guard.close();
-        IoUtils.closeSocket(fd);
+        IoBridge.closeSocket(fd);
     }
 
     @Override protected void finalize() throws Throwable {
diff --git a/luni/src/main/java/libcore/net/http/HttpEngine.java b/luni/src/main/java/libcore/net/http/HttpEngine.java
index 01090f1..d95e21c 100644
--- a/luni/src/main/java/libcore/net/http/HttpEngine.java
+++ b/luni/src/main/java/libcore/net/http/HttpEngine.java
@@ -789,9 +789,9 @@
                 if (responseCache instanceof HttpResponseCache) {
                     ((HttpResponseCache) responseCache).trackConditionalCacheHit();
                 }
-                // discard the network response and use the cache response
+                // Discard the network response body. Combine the headers.
                 release(true);
-                setResponse(cachedResponseHeaders, cachedResponseBody);
+                setResponse(cachedResponseHeaders.combine(responseHeaders), cachedResponseBody);
                 return;
             } else {
                 IoUtils.closeQuietly(cachedResponseBody);
diff --git a/luni/src/main/java/libcore/net/http/RawHeaders.java b/luni/src/main/java/libcore/net/http/RawHeaders.java
index be39ef4..75a1392a 100644
--- a/luni/src/main/java/libcore/net/http/RawHeaders.java
+++ b/luni/src/main/java/libcore/net/http/RawHeaders.java
@@ -42,9 +42,9 @@
  * <p>This class trims whitespace from values. It never returns values with
  * leading or trailing whitespace.
  */
-final class RawHeaders implements Cloneable {
-
+final class RawHeaders {
     private static final Comparator<String> FIELD_NAME_COMPARATOR = new Comparator<String>() {
+        @FindBugsSuppressWarnings("ES_COMPARING_PARAMETER_STRING_WITH_EQ")
         @Override public int compare(String a, String b) {
             if (a == b) {
                 return 0;
diff --git a/luni/src/main/java/libcore/net/http/RequestHeaders.java b/luni/src/main/java/libcore/net/http/RequestHeaders.java
index 9f7f763..b143f81 100644
--- a/luni/src/main/java/libcore/net/http/RequestHeaders.java
+++ b/luni/src/main/java/libcore/net/http/RequestHeaders.java
@@ -42,7 +42,6 @@
      * header is set.
      */
     boolean onlyIfCached;
-    String noCacheField;
 
     /**
      * True if the request contains an authorization field. Although this isn't
@@ -70,7 +69,6 @@
             @Override public void handle(String directive, String parameter) {
                 if (directive.equalsIgnoreCase("no-cache")) {
                     noCache = true;
-                    noCacheField = parameter;
                 } else if (directive.equalsIgnoreCase("max-age")) {
                     maxAgeSeconds = HeaderParser.parseSeconds(parameter);
                 } else if (directive.equalsIgnoreCase("max-stale")) {
diff --git a/luni/src/main/java/libcore/net/http/ResponseHeaders.java b/luni/src/main/java/libcore/net/http/ResponseHeaders.java
index a1b4fc4..0764a53 100644
--- a/luni/src/main/java/libcore/net/http/ResponseHeaders.java
+++ b/luni/src/main/java/libcore/net/http/ResponseHeaders.java
@@ -97,7 +97,6 @@
      * not permitted if this header is set.
      */
     boolean isPublic;
-    String privateField;
     boolean mustRevalidate;
     String etag;
     int ageSeconds = -1;
@@ -128,8 +127,6 @@
                     sMaxAgeSeconds = HeaderParser.parseSeconds(parameter);
                 } else if (directive.equalsIgnoreCase("public")) {
                     isPublic = true;
-                } else if (directive.equalsIgnoreCase("private")) {
-                    privateField = parameter;
                 } else if (directive.equalsIgnoreCase("must-revalidate")) {
                     mustRevalidate = true;
                 }
@@ -400,4 +397,47 @@
 
         return false;
     }
+
+    /**
+     * Combines this cached header with a network header as defined by RFC 2616,
+     * 13.5.3.
+     */
+    public ResponseHeaders combine(ResponseHeaders network) {
+        RawHeaders result = new RawHeaders();
+
+        for (int i = 0; i < headers.length(); i++) {
+            String fieldName = headers.getFieldName(i);
+            String value = headers.getValue(i);
+            if (fieldName.equals("Warning") && value.startsWith("1")) {
+                continue; // drop 100-level freshness warnings
+            }
+            if (!isEndToEnd(fieldName) || network.headers.get(fieldName) == null) {
+                result.add(fieldName, value);
+            }
+        }
+
+        for (int i = 0; i < network.headers.length(); i++) {
+            String fieldName = network.headers.getFieldName(i);
+            if (isEndToEnd(fieldName)) {
+                result.add(fieldName, network.headers.getValue(i));
+            }
+        }
+
+        return new ResponseHeaders(uri, result);
+    }
+
+    /**
+     * Returns true if {@code fieldName} is an end-to-end HTTP header, as
+     * defined by RFC 2616, 13.5.1.
+     */
+    private static boolean isEndToEnd(String fieldName) {
+        return !fieldName.equalsIgnoreCase("Connection")
+                && !fieldName.equalsIgnoreCase("Keep-Alive")
+                && !fieldName.equalsIgnoreCase("Proxy-Authenticate")
+                && !fieldName.equalsIgnoreCase("Proxy-Authorization")
+                && !fieldName.equalsIgnoreCase("TE")
+                && !fieldName.equalsIgnoreCase("Trailers")
+                && !fieldName.equalsIgnoreCase("Transfer-Encoding")
+                && !fieldName.equalsIgnoreCase("Upgrade");
+    }
 }
diff --git a/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java b/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java
deleted file mode 100644
index 1623e2e..0000000
--- a/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 org.apache.harmony.luni.platform;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.SocketImpl;
-
-/*
- * The interface for network methods.
- */
-public interface INetworkSystem {
-    public void accept(FileDescriptor serverFd, SocketImpl newSocket, FileDescriptor clientFd) throws IOException;
-}
diff --git a/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java b/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java
deleted file mode 100644
index 4db39ff..0000000
--- a/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 org.apache.harmony.luni.platform;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.SocketImpl;
-
-/**
- * This wraps native code that implements the INetworkSystem interface.
- * Address length was changed from long to int for performance reasons.
- */
-final class OSNetworkSystem implements INetworkSystem {
-    private static final OSNetworkSystem singleton = new OSNetworkSystem();
-
-    public static OSNetworkSystem getOSNetworkSystem() {
-        return singleton;
-    }
-
-    private OSNetworkSystem() {
-    }
-
-    public native void accept(FileDescriptor serverFd, SocketImpl newSocket, FileDescriptor clientFd) throws IOException;
-}
diff --git a/luni/src/main/java/org/apache/harmony/luni/platform/Platform.java b/luni/src/main/java/org/apache/harmony/luni/platform/Platform.java
deleted file mode 100644
index db79a59..0000000
--- a/luni/src/main/java/org/apache/harmony/luni/platform/Platform.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.harmony.luni.platform;
-
-import dalvik.system.BlockGuard;
-import dalvik.system.VMStack;
-
-/**
- * The Platform class gives access to the low-level underlying capabilities of
- * the operating system.
- *
- * The platform is structured into operations on the process heap memory,
- * network subsystem, and file system through different OS components.
- *
- * OS components are 'dangerous' in that they pass through the calls and
- * arguments to the OS with very little checking, and as such may cause fatal
- * exceptions in the runtime. Access to the OS components is restricted to
- * trusted code running on the system classpath.
- *
- * @see INetworkSystem
- */
-public class Platform {
-    // BlockGuard-policy-free threads should have no extra overhead, but for
-    // now they do because ThreadLocal lookups will be done on most operations, which
-    // should be relatively less than the speed of the operation.
-    // TODO: measure & fix if needed.
-    public static final INetworkSystem NETWORK =
-            new BlockGuard.WrappedNetworkSystem(OSNetworkSystem.getOSNetworkSystem());
-}
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index 402ac7e..a637a73 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -29,6 +29,7 @@
 extern int register_java_lang_ProcessManager(JNIEnv* env);
 extern int register_java_lang_RealToString(JNIEnv* env);
 extern int register_java_lang_StrictMath(JNIEnv* env);
+extern int register_java_lang_StringToReal(JNIEnv* env);
 extern int register_java_lang_System(JNIEnv* env);
 extern int register_java_math_NativeBN(JNIEnv* env);
 extern int register_java_nio_ByteOrder(JNIEnv* env);
@@ -55,8 +56,6 @@
 extern int register_libcore_io_Posix(JNIEnv* env);
 extern int register_libcore_net_RawSocket(JNIEnv* env);
 extern int register_org_apache_harmony_dalvik_NativeTestTarget(JNIEnv* env);
-extern int register_org_apache_harmony_luni_platform_OSNetworkSystem(JNIEnv* env);
-extern int register_org_apache_harmony_luni_util_FloatingPointParser(JNIEnv* env);
 extern int register_org_apache_harmony_xml_ExpatParser(JNIEnv* env);
 extern int register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(JNIEnv* env);
 
@@ -75,6 +74,7 @@
             register_java_lang_ProcessManager(env) != -1 &&
             register_java_lang_RealToString(env) != -1 &&
             register_java_lang_StrictMath(env) != -1 &&
+            register_java_lang_StringToReal(env) != -1 &&
             register_java_lang_System(env) != -1 &&
             register_java_math_NativeBN(env) != -1 &&
             register_java_nio_ByteOrder(env) != -1 &&
@@ -101,8 +101,6 @@
             register_libcore_io_Posix(env) != -1 &&
             register_libcore_net_RawSocket(env) != -1 &&
             register_org_apache_harmony_dalvik_NativeTestTarget(env) != -1 &&
-            register_org_apache_harmony_luni_platform_OSNetworkSystem(env) != -1 &&
-            register_org_apache_harmony_luni_util_FloatingPointParser(env) != -1 &&
             register_org_apache_harmony_xml_ExpatParser(env) != -1 &&
             register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(env) != -1 &&
             true;
diff --git a/luni/src/main/native/java_lang_ProcessManager.cpp b/luni/src/main/native/java_lang_ProcessManager.cpp
index 4d2fe72..3648a8f 100644
--- a/luni/src/main/native/java_lang_ProcessManager.cpp
+++ b/luni/src/main/native/java_lang_ProcessManager.cpp
@@ -18,10 +18,8 @@
 
 #include <sys/resource.h>
 #include <sys/types.h>
-#include <sys/wait.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <signal.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
@@ -32,88 +30,6 @@
 #include "ScopedLocalRef.h"
 #include "utils/Log.h"
 
-/*
- * These are constants shared with the higher level code in
- * ProcessManager.java.
- */
-#define WAIT_STATUS_UNKNOWN (-1)       // unknown child status
-#define WAIT_STATUS_NO_CHILDREN (-2)   // no children to wait for
-#define WAIT_STATUS_STRANGE_ERRNO (-3) // observed an undocumented errno
-
-/**
- * Loops indefinitely and calls ProcessManager.onExit() when children exit.
- */
-static void ProcessManager_watchChildren(JNIEnv* env, jclass processManagerClass, jobject processManager) {
-    static jmethodID onExitMethod = env->GetMethodID(processManagerClass, "onExit", "(II)V");
-    if (onExitMethod == NULL) {
-        return;
-    }
-
-    while (true) {
-        // Wait for children in our process group.
-        int status;
-        pid_t pid = waitpid(0, &status, 0);
-
-        if (pid >= 0) {
-            // Extract real status.
-            if (WIFEXITED(status)) {
-                status = WEXITSTATUS(status);
-            } else if (WIFSIGNALED(status)) {
-                status = WTERMSIG(status);
-            } else if (WIFSTOPPED(status)) {
-                status = WSTOPSIG(status);
-            } else {
-                status = WAIT_STATUS_UNKNOWN;
-            }
-        } else {
-            /*
-             * The pid should be -1 already, but force it here just in case
-             * we somehow end up with some other negative value.
-             */
-            pid = -1;
-
-            switch (errno) {
-                case ECHILD: {
-                    /*
-                     * Expected errno: There are no children to wait()
-                     * for. The callback will sleep until it is
-                     * informed of another child coming to life.
-                     */
-                    status = WAIT_STATUS_NO_CHILDREN;
-                    break;
-                }
-                case EINTR: {
-                    /*
-                     * An unblocked signal came in while waiting; just
-                     * retry the wait().
-                     */
-                    continue;
-                }
-                default: {
-                    /*
-                     * Unexpected errno, so squawk! Note: Per the
-                     * Linux docs, there are no errnos defined for
-                     * wait() other than the two that are handled
-                     * immediately above.
-                     */
-                    LOGE("Error %d calling wait(): %s", errno, strerror(errno));
-                    status = WAIT_STATUS_STRANGE_ERRNO;
-                    break;
-                }
-            }
-        }
-
-        env->CallVoidMethod(processManager, onExitMethod, pid, status);
-        if (env->ExceptionOccurred()) {
-            /*
-             * The callback threw, so break out of the loop and return,
-             * letting the exception percolate up.
-             */
-            break;
-        }
-    }
-}
-
 /** Close all open fds > 2 (i.e. everything but stdin/out/err), != skipFd. */
 static void closeNonStandardFds(int skipFd1, int skipFd2) {
     // TODO: rather than close all these non-open files, we could look in /proc/self/fd.
@@ -347,7 +263,6 @@
 }
 
 static JNINativeMethod methods[] = {
-    NATIVE_METHOD(ProcessManager, watchChildren, "(Ljava/lang/ProcessManager;)V"),
     NATIVE_METHOD(ProcessManager, exec, "([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Z)I"),
 };
 int register_java_lang_ProcessManager(JNIEnv* env) {
diff --git a/luni/src/main/native/org_apache_harmony_luni_util_FloatingPointParser.cpp b/luni/src/main/native/java_lang_StringToReal.cpp
similarity index 97%
rename from luni/src/main/native/org_apache_harmony_luni_util_FloatingPointParser.cpp
rename to luni/src/main/native/java_lang_StringToReal.cpp
index 48c0a77..06aa076 100644
--- a/luni/src/main/native/org_apache_harmony_luni_util_FloatingPointParser.cpp
+++ b/luni/src/main/native/java_lang_StringToReal.cpp
@@ -989,7 +989,7 @@
   return z;
 }
 
-static jfloat FloatingPointParser_parseFltImpl(JNIEnv* env, jclass, jstring s, jint e) {
+static jfloat StringToReal_parseFltImpl(JNIEnv* env, jclass, jstring s, jint e) {
     ScopedUtfChars str(env, s);
     if (str.c_str() == NULL) {
         return 0.0;
@@ -997,7 +997,7 @@
     return createFloat(env, str.c_str(), e);
 }
 
-static jdouble FloatingPointParser_parseDblImpl(JNIEnv* env, jclass, jstring s, jint e) {
+static jdouble StringToReal_parseDblImpl(JNIEnv* env, jclass, jstring s, jint e) {
     ScopedUtfChars str(env, s);
     if (str.c_str() == NULL) {
         return 0.0;
@@ -1006,10 +1006,9 @@
 }
 
 static JNINativeMethod gMethods[] = {
-    NATIVE_METHOD(FloatingPointParser, parseFltImpl, "(Ljava/lang/String;I)F"),
-    NATIVE_METHOD(FloatingPointParser, parseDblImpl, "(Ljava/lang/String;I)D"),
+    NATIVE_METHOD(StringToReal, parseFltImpl, "(Ljava/lang/String;I)F"),
+    NATIVE_METHOD(StringToReal, parseDblImpl, "(Ljava/lang/String;I)D"),
 };
-int register_org_apache_harmony_luni_util_FloatingPointParser(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "org/apache/harmony/luni/util/FloatingPointParser",
-                gMethods, NELEM(gMethods));
+int register_java_lang_StringToReal(JNIEnv* env) {
+    return jniRegisterNativeMethods(env, "java/lang/StringToReal", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index 22b440c..5b02862 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -51,6 +51,7 @@
 #include <sys/uio.h>
 #include <sys/utsname.h>
 #include <sys/vfs.h> // Bionic doesn't have <sys/statvfs.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
 #define TO_JAVA_STRING(NAME, EXP) \
@@ -298,6 +299,23 @@
     return true;
 }
 
+static bool fillInetSocketAddress(JNIEnv* env, jint rc, jobject javaInetSocketAddress, const sockaddr_storage* ss) {
+    if (rc == -1 || javaInetSocketAddress == NULL) {
+        return true;
+    }
+    // Fill out the passed-in InetSocketAddress with the sender's IP address and port number.
+    jint port;
+    jobject sender = sockaddrToInetAddress(env, ss, &port);
+    if (sender == NULL) {
+        return false;
+    }
+    static jfieldID addressFid = env->GetFieldID(JniConstants::inetSocketAddressClass, "addr", "Ljava/net/InetAddress;");
+    static jfieldID portFid = env->GetFieldID(JniConstants::inetSocketAddressClass, "port", "I");
+    env->SetObjectField(javaInetSocketAddress, addressFid, sender);
+    env->SetIntField(javaInetSocketAddress, portFid, port);
+    return true;
+}
+
 static jobject doStat(JNIEnv* env, jstring javaPath, bool isLstat) {
     ScopedUtfChars path(env, javaPath);
     if (path.c_str() == NULL) {
@@ -354,6 +372,21 @@
     struct passwd* mResult;
 };
 
+static jobject Posix_accept(JNIEnv* env, jobject, jobject javaFd, jobject javaInetSocketAddress) {
+    sockaddr_storage ss;
+    socklen_t sl = sizeof(ss);
+    memset(&ss, 0, sizeof(ss));
+    int fd;
+    sockaddr* peer = (javaInetSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
+    socklen_t* peerLength = (javaInetSocketAddress != NULL) ? &sl : 0;
+    jint clientFd = NET_FAILURE_RETRY("accept", accept(fd, peer, peerLength));
+    if (clientFd == -1 || !fillInetSocketAddress(env, clientFd, javaInetSocketAddress, &ss)) {
+        close(clientFd);
+        return NULL;
+    }
+    return (clientFd != -1) ? jniCreateFileDescriptor(env, clientFd) : NULL;
+}
+
 static jboolean Posix_access(JNIEnv* env, jobject, jstring javaPath, jint mode) {
     ScopedUtfChars path(env, javaPath);
     if (path.c_str() == NULL) {
@@ -921,18 +954,7 @@
     sockaddr* from = (javaInetSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
     socklen_t* fromLength = (javaInetSocketAddress != NULL) ? &sl : 0;
     jint recvCount = NET_FAILURE_RETRY("recvfrom", recvfrom(fd, bytes.get() + byteOffset, byteCount, flags, from, fromLength));
-    if (recvCount != -1 && javaInetSocketAddress != NULL) {
-        // Fill out the passed-in InetSocketAddress with the sender's IP address and port number.
-        jint port;
-        jobject sender = sockaddrToInetAddress(env, &ss, &port);
-        if (sender == NULL) {
-            return -1;
-        }
-        static jfieldID addressFid = env->GetFieldID(JniConstants::inetSocketAddressClass, "addr", "Ljava/net/InetAddress;");
-        static jfieldID portFid = env->GetFieldID(JniConstants::inetSocketAddressClass, "port", "I");
-        env->SetObjectField(javaInetSocketAddress, addressFid, sender);
-        env->SetIntField(javaInetSocketAddress, portFid, port);
-    }
+    fillInetSocketAddress(env, recvCount, javaInetSocketAddress, &ss);
     return recvCount;
 }
 
@@ -1147,6 +1169,16 @@
     return makeStructUtsname(env, buf);
 }
 
+static jint Posix_waitpid(JNIEnv* env, jobject, jint pid, jobject javaStatus, jint options) {
+    int status;
+    int rc = throwIfMinusOne(env, "waitpid", TEMP_FAILURE_RETRY(waitpid(pid, &status, options)));
+    if (rc != -1) {
+        static jfieldID valueFid = env->GetFieldID(JniConstants::mutableIntClass, "value", "I");
+        env->SetIntField(javaStatus, valueFid, status);
+    }
+    return rc;
+}
+
 static jint Posix_writeBytes(JNIEnv* env, jobject, jobject javaFd, jbyteArray javaBytes, jint byteOffset, jint byteCount) {
     ScopedBytesRO bytes(env, javaBytes);
     if (bytes.get() == NULL) {
@@ -1166,6 +1198,7 @@
 }
 
 static JNINativeMethod gMethods[] = {
+    NATIVE_METHOD(Posix, accept, "(Ljava/io/FileDescriptor;Ljava/net/InetSocketAddress;)Ljava/io/FileDescriptor;"),
     NATIVE_METHOD(Posix, access, "(Ljava/lang/String;I)Z"),
     NATIVE_METHOD(Posix, bind, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"),
     NATIVE_METHOD(Posix, chmod, "(Ljava/lang/String;I)V"),
@@ -1245,6 +1278,7 @@
     NATIVE_METHOD(Posix, symlink, "(Ljava/lang/String;Ljava/lang/String;)V"),
     NATIVE_METHOD(Posix, sysconf, "(I)J"),
     NATIVE_METHOD(Posix, uname, "()Llibcore/io/StructUtsname;"),
+    NATIVE_METHOD(Posix, waitpid, "(ILlibcore/util/MutableInt;I)I"),
     NATIVE_METHOD(Posix, writeBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;II)I"),
     NATIVE_METHOD(Posix, writev, "(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I"),
 };
diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
deleted file mode 100644
index cc2dc49..0000000
--- a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-#define LOG_TAG "OSNetworkSystem"
-
-#include "AsynchronousSocketCloseMonitor.h"
-#include "JNIHelp.h"
-#include "JniConstants.h"
-#include "JniException.h"
-#include "NetFd.h"
-#include "NetworkUtilities.h"
-#include "ScopedLocalRef.h"
-#include "ScopedPrimitiveArray.h"
-#include "jni.h"
-#include "valueOf.h"
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <net/if.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <unistd.h>
-
-static void OSNetworkSystem_accept(JNIEnv* env, jobject, jobject serverFileDescriptor,
-        jobject newSocket, jobject clientFileDescriptor) {
-
-    if (newSocket == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return;
-    }
-
-    NetFd serverFd(env, serverFileDescriptor);
-    if (serverFd.isClosed()) {
-        return;
-    }
-
-    sockaddr_storage ss;
-    socklen_t addrLen = sizeof(ss);
-    sockaddr* sa = reinterpret_cast<sockaddr*>(&ss);
-
-    int clientFd;
-    {
-        int intFd = serverFd.get();
-        AsynchronousSocketCloseMonitor monitor(intFd);
-        clientFd = NET_FAILURE_RETRY(serverFd, accept(intFd, sa, &addrLen));
-    }
-    if (env->ExceptionOccurred()) {
-        return;
-    }
-    if (clientFd == -1) {
-        if (errno == EAGAIN || errno == EWOULDBLOCK) {
-            jniThrowExceptionWithErrno(env, "java/net/SocketTimeoutException", errno);
-        } else {
-            jniThrowSocketException(env, errno);
-        }
-        return;
-    }
-
-    /*
-     * For network sockets, put the peer address and port in instance variables.
-     * We don't bother to do this for UNIX domain sockets, since most peers are
-     * anonymous anyway.
-     */
-    if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) {
-        jint remotePort;
-        jobject remoteAddress = sockaddrToInetAddress(env, &ss, &remotePort);
-        if (remoteAddress == NULL) {
-            close(clientFd);
-            return;
-        }
-        static jfieldID addressFid = env->GetFieldID(JniConstants::socketImplClass, "address", "Ljava/net/InetAddress;");
-        static jfieldID portFid = env->GetFieldID(JniConstants::socketImplClass, "port", "I");
-        env->SetObjectField(newSocket, addressFid, remoteAddress);
-        env->SetIntField(newSocket, portFid, remotePort);
-    }
-
-    jniSetFileDescriptorOfFD(env, clientFileDescriptor, clientFd);
-}
-
-static JNINativeMethod gMethods[] = {
-    NATIVE_METHOD(OSNetworkSystem, accept, "(Ljava/io/FileDescriptor;Ljava/net/SocketImpl;Ljava/io/FileDescriptor;)V"),
-};
-
-int register_org_apache_harmony_luni_platform_OSNetworkSystem(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "org/apache/harmony/luni/platform/OSNetworkSystem", gMethods, NELEM(gMethods));
-}
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index c944319..9535ce6 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -18,6 +18,7 @@
 	java_lang_ProcessManager.cpp \
 	java_lang_RealToString.cpp \
 	java_lang_StrictMath.cpp \
+	java_lang_StringToReal.cpp \
 	java_lang_System.cpp \
 	java_math_NativeBN.cpp \
 	java_nio_ByteOrder.cpp \
@@ -43,8 +44,6 @@
 	libcore_io_OsConstants.cpp \
 	libcore_io_Posix.cpp \
 	libcore_net_RawSocket.cpp \
-	org_apache_harmony_luni_platform_OSNetworkSystem.cpp \
-	org_apache_harmony_luni_util_FloatingPointParser.cpp \
 	org_apache_harmony_xml_ExpatParser.cpp \
 	org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp \
 	readlink.cpp \
diff --git a/luni/src/test/java/libcore/java/nio/channels/FileChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/FileChannelTest.java
index e221675..415ae0a 100644
--- a/luni/src/test/java/libcore/java/nio/channels/FileChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/FileChannelTest.java
@@ -92,6 +92,22 @@
         assertEquals(8, fc.write(buffers));
         fc.close();
         assertEquals(8, tmp.length());
-        assertEquals("abcdABCD", new String(IoUtils.readFileAsByteArray(tmp.getPath()), "US-ASCII"));
+        assertEquals("abcdABCD", new String(IoUtils.readFileAsString(tmp.getPath())));
+    }
+
+    public void test_append() throws Exception {
+        File tmp = File.createTempFile("FileChannelTest", "tmp");
+        FileOutputStream fos = new FileOutputStream(tmp, true);
+        FileChannel fc = fos.getChannel();
+
+        fc.write(ByteBuffer.wrap("hello".getBytes("US-ASCII")));
+        fc.position(0);
+        // The RI reports whatever position you set...
+        assertEquals(0, fc.position());
+        // ...but writes to the end of the file.
+        fc.write(ByteBuffer.wrap(" world".getBytes("US-ASCII")));
+        fos.close();
+
+        assertEquals("hello world", new String(IoUtils.readFileAsString(tmp.getPath())));
     }
 }
diff --git a/luni/src/test/java/libcore/java/nio/channels/OldFileChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/OldFileChannelTest.java
index 9388888..7e2310b 100644
--- a/luni/src/test/java/libcore/java/nio/channels/OldFileChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/OldFileChannelTest.java
@@ -156,11 +156,13 @@
 
         writeOnlyFileChannel.write(writeBuffer);
         writeOnlyFileChannel.force(false);
+        fis.close();
 
         readBuffer = new byte[CONTENT_AS_BYTES_LENGTH];
         fis = new FileInputStream(fileOfWriteOnlyFileChannel);
         fis.read(readBuffer);
         assertTrue(Arrays.equals(CONTENT_AS_BYTES, readBuffer));
+        fis.close();
     }
 
 
diff --git a/luni/src/test/java/libcore/net/http/HttpResponseCacheTest.java b/luni/src/test/java/libcore/net/http/HttpResponseCacheTest.java
index a5673ff..5981b0b 100644
--- a/luni/src/test/java/libcore/net/http/HttpResponseCacheTest.java
+++ b/luni/src/test/java/libcore/net/http/HttpResponseCacheTest.java
@@ -27,6 +27,9 @@
 import java.lang.reflect.InvocationHandler;
 import java.net.CacheRequest;
 import java.net.CacheResponse;
+import java.net.CookieHandler;
+import java.net.CookieManager;
+import java.net.HttpCookie;
 import java.net.HttpURLConnection;
 import java.net.ResponseCache;
 import java.net.SecureCacheResponse;
@@ -63,13 +66,10 @@
 import tests.io.MockOs;
 
 public final class HttpResponseCacheTest extends TestCase {
-
-    // TODO: test cache + cookies
-    // TODO: test cache + user-provided Range header
-
     private MockWebServer server = new MockWebServer();
     private HttpResponseCache cache;
-    final MockOs mockOs = new MockOs();
+    private final MockOs mockOs = new MockOs();
+    private final CookieManager cookieManager = new CookieManager();
 
     @Override protected void setUp() throws Exception {
         super.setUp();
@@ -79,6 +79,7 @@
         cache = new HttpResponseCache(cacheDir, Integer.MAX_VALUE);
         ResponseCache.setDefault(cache);
         mockOs.install();
+        CookieHandler.setDefault(cookieManager);
     }
 
     @Override protected void tearDown() throws Exception {
@@ -86,6 +87,7 @@
         server.shutdown();
         ResponseCache.setDefault(null);
         cache.getCache().delete();
+        CookieHandler.setDefault(null);
         super.tearDown();
     }
 
@@ -1499,6 +1501,118 @@
         System.out.println("Exercising the cache performs " + (i - 1) + " reads.");
     }
 
+    public void testCachePlusCookies() throws Exception {
+        server.enqueue(new MockResponse()
+                .addHeader("Set-Cookie: a=FIRST; domain=.local;")
+                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
+                .addHeader("Cache-Control: max-age=0")
+                .setBody("A"));
+        server.enqueue(new MockResponse()
+                .addHeader("Set-Cookie: a=SECOND; domain=.local;")
+                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+        server.play();
+
+        URL url = server.getUrl("/");
+        assertEquals("A", readAscii(url.openConnection()));
+        assertCookies(url, "a=FIRST");
+        assertEquals("A", readAscii(url.openConnection()));
+        assertCookies(url, "a=SECOND");
+    }
+
+    public void testGetHeadersReturnsNetworkEndToEndHeaders() throws Exception {
+        server.enqueue(new MockResponse()
+                .addHeader("Allow: GET, HEAD")
+                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
+                .addHeader("Cache-Control: max-age=0")
+                .setBody("A"));
+        server.enqueue(new MockResponse()
+                .addHeader("Allow: GET, HEAD, PUT")
+                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+        server.play();
+
+        URLConnection connection1 = server.getUrl("/").openConnection();
+        assertEquals("A", readAscii(connection1));
+        assertEquals("GET, HEAD", connection1.getHeaderField("Allow"));
+
+        URLConnection connection2 = server.getUrl("/").openConnection();
+        assertEquals("A", readAscii(connection2));
+        assertEquals("GET, HEAD, PUT", connection2.getHeaderField("Allow"));
+    }
+
+    public void testGetHeadersReturnsCachedHopByHopHeaders() throws Exception {
+        server.enqueue(new MockResponse()
+                .addHeader("Transfer-Encoding: identity")
+                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
+                .addHeader("Cache-Control: max-age=0")
+                .setBody("A"));
+        server.enqueue(new MockResponse()
+                .addHeader("Transfer-Encoding: none")
+                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+        server.play();
+
+        URLConnection connection1 = server.getUrl("/").openConnection();
+        assertEquals("A", readAscii(connection1));
+        assertEquals("identity", connection1.getHeaderField("Transfer-Encoding"));
+
+        URLConnection connection2 = server.getUrl("/").openConnection();
+        assertEquals("A", readAscii(connection2));
+        assertEquals("identity", connection2.getHeaderField("Transfer-Encoding"));
+    }
+
+    public void testGetHeadersDeletesCached100LevelWarnings() throws Exception {
+        server.enqueue(new MockResponse()
+                .addHeader("Warning: 199 test danger")
+                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
+                .addHeader("Cache-Control: max-age=0")
+                .setBody("A"));
+        server.enqueue(new MockResponse()
+                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+        server.play();
+
+        URLConnection connection1 = server.getUrl("/").openConnection();
+        assertEquals("A", readAscii(connection1));
+        assertEquals("199 test danger", connection1.getHeaderField("Warning"));
+
+        URLConnection connection2 = server.getUrl("/").openConnection();
+        assertEquals("A", readAscii(connection2));
+        assertEquals(null, connection2.getHeaderField("Warning"));
+    }
+
+    public void testGetHeadersRetainsCached200LevelWarnings() throws Exception {
+        server.enqueue(new MockResponse()
+                .addHeader("Warning: 299 test danger")
+                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
+                .addHeader("Cache-Control: max-age=0")
+                .setBody("A"));
+        server.enqueue(new MockResponse()
+                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+        server.play();
+
+        URLConnection connection1 = server.getUrl("/").openConnection();
+        assertEquals("A", readAscii(connection1));
+        assertEquals("299 test danger", connection1.getHeaderField("Warning"));
+
+        URLConnection connection2 = server.getUrl("/").openConnection();
+        assertEquals("A", readAscii(connection2));
+        assertEquals("299 test danger", connection2.getHeaderField("Warning"));
+    }
+
+    public void assertCookies(URL url, String... expectedCookies) throws Exception {
+        List<String> actualCookies = new ArrayList<String>();
+        for (HttpCookie cookie : cookieManager.getCookieStore().get(url.toURI())) {
+            actualCookies.add(cookie.toString());
+        }
+        assertEquals(Arrays.asList(expectedCookies), actualCookies);
+    }
+
+    public void testCachePlusRange() throws Exception {
+        assertNotCached(new MockResponse()
+                .setResponseCode(HttpURLConnection.HTTP_PARTIAL)
+                .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
+                .addHeader("Content-Range: bytes 100-100/200")
+                .addHeader("Cache-Control: max-age=60"));
+    }
+
     /**
      * @param delta the offset from the current date to use. Negative
      *     values yield dates in the past; positive values yield dates in the
diff --git a/luni/src/test/java/libcore/net/http/ParsedHeadersTest.java b/luni/src/test/java/libcore/net/http/ParsedHeadersTest.java
index 0f38a5d..ae684c3 100644
--- a/luni/src/test/java/libcore/net/http/ParsedHeadersTest.java
+++ b/luni/src/test/java/libcore/net/http/ParsedHeadersTest.java
@@ -57,16 +57,15 @@
 
     public void testQuotedFieldName() {
         RawHeaders headers = new RawHeaders();
-        headers.add("Cache-Control", "private=\"Set-Cookie\"");
+        headers.add("Cache-Control", "private=\"Set-Cookie\", no-store");
         ResponseHeaders parsedHeaders = new ResponseHeaders(uri, headers);
-        assertEquals("Set-Cookie", parsedHeaders.privateField);
+        assertTrue(parsedHeaders.noStore);
     }
 
     public void testUnquotedValue() {
         RawHeaders headers = new RawHeaders();
         headers.add("Cache-Control", "private=Set-Cookie, no-store");
         ResponseHeaders parsedHeaders = new ResponseHeaders(uri, headers);
-        assertEquals("Set-Cookie", parsedHeaders.privateField);
         assertTrue(parsedHeaders.noStore);
     }
 
@@ -74,7 +73,6 @@
         RawHeaders headers = new RawHeaders();
         headers.add("Cache-Control", "private=\" a, no-cache, c \", no-store");
         ResponseHeaders parsedHeaders = new ResponseHeaders(uri, headers);
-        assertEquals(" a, no-cache, c ", parsedHeaders.privateField);
         assertTrue(parsedHeaders.noStore);
         assertFalse(parsedHeaders.noCache);
     }
@@ -83,7 +81,6 @@
         RawHeaders headers = new RawHeaders();
         headers.add("Cache-Control", "private=\"a, no-cache, c");
         ResponseHeaders parsedHeaders = new ResponseHeaders(uri, headers);
-        assertEquals("a, no-cache, c", parsedHeaders.privateField);
         assertFalse(parsedHeaders.noCache);
     }
 
@@ -92,14 +89,12 @@
         headers.add("Cache-Control", "public,");
         ResponseHeaders parsedHeaders = new ResponseHeaders(uri, headers);
         assertTrue(parsedHeaders.isPublic);
-        assertNull(parsedHeaders.privateField);
     }
 
     public void testTrailingEquals() {
         RawHeaders headers = new RawHeaders();
         headers.add("Cache-Control", "private=");
         ResponseHeaders parsedHeaders = new ResponseHeaders(uri, headers);
-        assertEquals("", parsedHeaders.privateField);
     }
 
     public void testSpaceBeforeEquals() {
@@ -145,7 +140,6 @@
         headers.add("Cache-Control", "MAX-AGE=60");
         headers.add("Cache-Control", "S-MAXAGE=70");
         headers.add("Cache-Control", "PUBLIC");
-        headers.add("Cache-Control", "PRIVATE=a");
         headers.add("Cache-Control", "MUST-REVALIDATE");
         ResponseHeaders parsedHeaders = new ResponseHeaders(uri, headers);
         assertTrue(parsedHeaders.noCache);
@@ -153,7 +147,6 @@
         assertEquals(60, parsedHeaders.maxAgeSeconds);
         assertEquals(70, parsedHeaders.sMaxAgeSeconds);
         assertTrue(parsedHeaders.isPublic);
-        assertEquals("a", parsedHeaders.privateField);
         assertTrue(parsedHeaders.mustRevalidate);
     }