Merge change I57dd1032

* changes:
  Supporting expected results for the jtreg test runner.
diff --git a/logging/src/main/java/java/util/logging/FileHandler.java b/logging/src/main/java/java/util/logging/FileHandler.java
index e1eba9e..dd7790e 100644
--- a/logging/src/main/java/java/util/logging/FileHandler.java
+++ b/logging/src/main/java/java/util/logging/FileHandler.java
@@ -571,7 +571,7 @@
      *            the log record to publish.
      */
     @Override
-    public void publish(LogRecord record) {
+    public synchronized void publish(LogRecord record) {
         super.publish(record);
         flush();
         if (limit > 0 && output.getLength() >= limit) {
diff --git a/logging/src/main/java/java/util/logging/LogManager.java b/logging/src/main/java/java/util/logging/LogManager.java
index d7ade24..413efb3 100644
--- a/logging/src/main/java/java/util/logging/LogManager.java
+++ b/logging/src/main/java/java/util/logging/LogManager.java
@@ -401,7 +401,6 @@
      *             not have the required permissions to perform this action.
      */
     public void readConfiguration() throws IOException {
-        checkAccess();
         // check config class
         String configClassName = System
                 .getProperty("java.util.logging.config.class"); //$NON-NLS-1$
@@ -436,7 +435,7 @@
                                     "logging.properties"), 8192);
                 }
                 // END android-added
-                readConfigurationImpl(input);
+                readConfiguration(input);
             } finally {
                 if (input != null) {
                     try {
diff --git a/logging/src/test/java/org/apache/harmony/logging/tests/java/util/logging/LogManagerTest.java b/logging/src/test/java/org/apache/harmony/logging/tests/java/util/logging/LogManagerTest.java
index 44389be..3083dc9 100644
--- a/logging/src/test/java/org/apache/harmony/logging/tests/java/util/logging/LogManagerTest.java
+++ b/logging/src/test/java/org/apache/harmony/logging/tests/java/util/logging/LogManagerTest.java
@@ -837,6 +837,16 @@
         assertNull(m.getProperty("java.util.logging.FileHandler.pattern"));
     }
 
+    public void testReadConfiguration() throws SecurityException,
+            IOException {
+
+        MockConfigLogManager lm = new MockConfigLogManager();
+        assertFalse(lm.isCalled);
+
+        lm.readConfiguration();
+        assertTrue(lm.isCalled);
+    }
+
     private static void checkProperty(LogManager m) {
         // assertEquals(m.getProperty(".level"), "INFO");
         assertEquals(m.getProperty("java.util.logging.FileHandler.limit"), "50000");
@@ -1363,6 +1373,15 @@
     public static class MockLogManager extends LogManager {
     }
 
+	public static class MockConfigLogManager extends LogManager {
+        public boolean isCalled = false;
+
+        public void readConfiguration(InputStream ins) throws IOException {
+            isCalled = true;
+            super.readConfiguration(ins);
+        }
+    }
+
     public static class MockHandler extends Handler {
         static int number = 0;
 
diff --git a/luni/src/main/java/java/io/BufferedReader.java b/luni/src/main/java/java/io/BufferedReader.java
index 77f1975..cfdc4aa 100644
--- a/luni/src/main/java/java/io/BufferedReader.java
+++ b/luni/src/main/java/java/io/BufferedReader.java
@@ -17,6 +17,11 @@
 
 package java.io;
 
+// BEGIN android-note
+// lots of tidying up of implementation, including renaming fields and changing
+// the implementation of read(char[], int, int)
+// END android-note
+
 import org.apache.harmony.luni.util.Msg;
 
 // BEGIN android-added
@@ -44,16 +49,36 @@
 
     private Reader in;
 
+    /**
+     * The characters that can be read and refilled in bulk. We maintain three
+     * indices into this buffer:<pre>
+     *     { X X X X X X X X X X X X - - }
+     *           ^     ^             ^
+     *           |     |             |
+     *         mark   pos           end</pre>
+     * Pos points to the next readable character. End is one greater than the
+     * last readable character. When {@code pos == end}, the buffer is empty and
+     * must be {@link #fillBuf() filled} before characters can be read.
+     *
+     * <p>Mark is the value pos will be set to on calls to {@link #reset}. Its
+     * value is in the range {@code [0...pos]}. If the mark is {@code -1}, the
+     * buffer cannot be reset.
+     *
+     * <p>MarkLimit limits the distance between the mark and the pos. When this
+     * limit is exceeded, {@link #reset} is permitted (but not required) to
+     * throw an exception. For shorter distances, {@link #reset} shall not throw
+     * (unless the reader is closed).
+     */
     private char[] buf;
 
-    private int marklimit = -1;
-
-    private int count;
-
-    private int markpos = -1;
-
     private int pos;
 
+    private int end;
+
+    private int mark = -1;
+
+    private int markLimit = -1;
+
     /**
      * Constructs a new BufferedReader on the Reader {@code in}. The
      * buffer gets the default size (8 KB).
@@ -66,7 +91,7 @@
         this.in = in;
         buf = new char[8192];
 
-        // BEGIN android-added
+        // BEGIN android-only
         /*
          * For Android, we want to discourage the use of this
          * constructor (with its arguably too-large default), so we
@@ -79,7 +104,7 @@
                 "Default buffer size used in BufferedReader " +
                 "constructor. It would be " +
                 "better to be explicit if an 8k-char buffer is required.");
-        // END android-added
+        // END android-only
     }
 
     /**
@@ -120,36 +145,50 @@
         }
     }
 
-    private int fillbuf() throws IOException {
-        if (markpos == -1 || (pos - markpos >= marklimit)) {
-            /* Mark position not set or exceeded readlimit */
+    /**
+     * Populates the buffer with data. It is an error to call this method when
+     * the buffer still contains data; ie. if {@code pos < end}.
+     *
+     * @return the number of bytes read into the buffer, or -1 if the end of the
+     *      source stream has been reached.
+     */
+    private int fillBuf() throws IOException {
+        // assert(pos == end);
+
+        if (mark == -1 || (pos - mark >= markLimit)) {
+            /* mark isn't set or has exceeded its limit. use the whole buffer */
             int result = in.read(buf, 0, buf.length);
             if (result > 0) {
-                markpos = -1;
+                mark = -1;
                 pos = 0;
-                count = result == -1 ? 0 : result;
+                end = result;
             }
             return result;
         }
-        if (markpos == 0 && marklimit > buf.length) {
-            /* Increase buffer size to accommodate the readlimit */
+
+        if (mark == 0 && markLimit > buf.length) {
+            /* the only way to make room when mark=0 is by growing the buffer */
             int newLength = buf.length * 2;
-            if (newLength > marklimit) {
-                newLength = marklimit;
+            if (newLength > markLimit) {
+                newLength = markLimit;
             }
             char[] newbuf = new char[newLength];
             System.arraycopy(buf, 0, newbuf, 0, buf.length);
             buf = newbuf;
-        } else if (markpos > 0) {
-            System.arraycopy(buf, markpos, buf, 0, buf.length - markpos);
+        } else if (mark > 0) {
+            /* make room by shifting the buffered data to left mark positions */
+            System.arraycopy(buf, mark, buf, 0, buf.length - mark);
+            pos -= mark;
+            end -= mark;
+            mark = 0;
         }
 
         /* Set the new position and mark position */
-        pos -= markpos;
-        count = markpos = 0;
-        int charsread = in.read(buf, pos, buf.length - pos);
-        count = charsread == -1 ? pos : pos + charsread;
-        return charsread;
+        int count = in.read(buf, pos, buf.length - pos);
+        if (count != -1) {
+            end += count;
+        }
+        return count;
     }
 
     /**
@@ -163,32 +202,32 @@
     }
 
     /**
-     * Sets a mark position in this reader. The parameter {@code readlimit}
+     * Sets a mark position in this reader. The parameter {@code markLimit}
      * indicates how many characters can be read before the mark is invalidated.
      * Calling {@code reset()} will reposition the reader back to the marked
-     * position if {@code readlimit} has not been surpassed.
+     * position if {@code markLimit} has not been surpassed.
      * 
-     * @param readlimit
+     * @param markLimit
      *            the number of characters that can be read before the mark is
      *            invalidated.
      * @throws IllegalArgumentException
-     *             if {@code readlimit < 0}.
+     *             if {@code markLimit < 0}.
      * @throws IOException
      *             if an error occurs while setting a mark in this reader.
      * @see #markSupported()
      * @see #reset()
      */
     @Override
-    public void mark(int readlimit) throws IOException {
-        if (readlimit < 0) {
+    public void mark(int markLimit) throws IOException {
+        if (markLimit < 0) {
             throw new IllegalArgumentException();
         }
         synchronized (lock) {
             if (isClosed()) {
                 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
             }
-            marklimit = readlimit;
-            markpos = pos;
+            this.markLimit = markLimit;
+            mark = pos;
         }
     }
 
@@ -224,10 +263,9 @@
                 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
             }
             /* Are there buffered characters available? */
-            if (pos < count || fillbuf() != -1) {
+            if (pos < end || fillBuf() != -1) {
                 return buf[pos++];
             }
-            markpos = -1;
             return -1;
         }
     }
@@ -276,52 +314,59 @@
                 throw new IndexOutOfBoundsException(Msg.getString("K002f")); //$NON-NLS-1$
             }
             // END android-changed
-            if (length == 0) {
-                return 0;
-            }
-            int required;
-            if (pos < count) {
-                /* There are bytes available in the buffer. */
-                int copylength = count - pos >= length ? length : count - pos;
-                System.arraycopy(buf, pos, buffer, offset, copylength);
-                pos += copylength;
-                if (copylength == length || !in.ready()) {
-                    return copylength;
+
+            int outstanding = length;
+            while (outstanding > 0) {
+
+                /*
+                 * If there are bytes in the buffer, grab those first.
+                 */
+                int available = end - pos;
+                if (available > 0) {
+                    int count = available >= outstanding ? outstanding : available;
+                    System.arraycopy(buf, pos, buffer, offset, count);
+                    pos += count;
+                    offset += count;
+                    outstanding -= count;
                 }
-                offset += copylength;
-                required = length - copylength;
-            } else {
-                required = length;
+
+                /*
+                 * Before attempting to read from the underlying stream, make
+                 * sure we really, really want to. We won't bother if we're
+                 * done, or if we've already got some bytes and reading from the
+                 * underlying stream would block.
+                 */
+                if (outstanding == 0 || (outstanding < length && !in.ready())) {
+                    break;
+                }
+
+                // assert(pos == end);
+
+                /*
+                 * If we're unmarked and the requested size is greater than our
+                 * buffer, read the bytes directly into the caller's buffer. We
+                 * don't read into smaller buffers because that could result in
+                 * a many reads.
+                 */
+                if ((mark == -1 || (pos - mark >= markLimit))
+                        && outstanding >= buf.length) {
+                    int count = in.read(buffer, offset, outstanding);
+                    if (count > 0) {
+                        offset += count;
+                        outstanding -= count;
+                        mark = -1;
+                    }
+
+                    break; // assume the source stream gave us all that it could
+                }
+
+                if (fillBuf() == -1) {
+                    break; // source is exhausted
+                }
             }
 
-            while (true) {
-                int read;
-                /*
-                 * If we're not marked and the required size is greater than the
-                 * buffer, simply read the bytes directly bypassing the buffer.
-                 */
-                if (markpos == -1 && required >= buf.length) {
-                    read = in.read(buffer, offset, required);
-                    if (read == -1) {
-                        return required == length ? -1 : length - required;
-                    }
-                } else {
-                    if (fillbuf() == -1) {
-                        return required == length ? -1 : length - required;
-                    }
-                    read = count - pos >= required ? required : count - pos;
-                    System.arraycopy(buf, pos, buffer, offset, read);
-                    pos += read;
-                }
-                required -= read;
-                if (required == 0) {
-                    return length;
-                }
-                if (!in.ready()) {
-                    return length - required;
-                }
-                offset += read;
-            }
+            int count = length - outstanding;
+            return (count > 0 || count == length) ? count : -1;
         }
     }
 
@@ -341,11 +386,11 @@
             if (isClosed()) {
                 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
             }
-            /* Are there buffered characters available? */
-            if ((pos >= count) && (fillbuf() == -1)) {
+            /* has the underlying stream been exhausted? */
+            if (pos == end && fillBuf() == -1) {
                 return null;
             }
-            for (int charPos = pos; charPos < count; charPos++) {
+            for (int charPos = pos; charPos < end; charPos++) {
                 char ch = buf[charPos];
                 // BEGIN android-note
                 // a switch statement may be more efficient
@@ -360,7 +405,7 @@
                 } else if (ch == '\r') {
                     String res = new String(buf, pos, charPos - pos);
                     pos = charPos + 1;
-                    if (((pos < count) || (fillbuf() != -1))
+                    if (((pos < end) || (fillBuf() != -1))
                             && (buf[pos] == '\n')) {
                         pos++;
                     }
@@ -372,30 +417,28 @@
             StringBuilder result = new StringBuilder(80);
             /* Typical Line Length */
 
-            result.append(buf, pos, count - pos);
-            pos = count;
+            result.append(buf, pos, end - pos);
             while (true) {
+                pos = end;
+
                 /* Are there buffered characters available? */
-                if (pos >= count) {
-                    if (eol == '\n') {
-                        return result.toString();
-                    }
-                    // attempt to fill buffer
-                    if (fillbuf() == -1) {
-                        // characters or null.
-                        return result.length() > 0 || eol != '\0' ? result
-                                .toString() : null;
-                    }
+                if (eol == '\n') {
+                    return result.toString();
                 }
-                for (int charPos = pos; charPos < count; charPos++) {
-                    // BEGIN android-note
-                    // use a local variable for buf[charPos] and a switch statement
-                    // END android-note
+                // attempt to fill buffer
+                if (fillBuf() == -1) {
+                    // characters or null.
+                    return result.length() > 0 || eol != '\0'
+                            ? result.toString()
+                            : null;
+                }
+                for (int charPos = pos; charPos < end; charPos++) {
+                    char c = buf[charPos];
                     if (eol == '\0') {
-                        if ((buf[charPos] == '\n' || buf[charPos] == '\r')) {
-                            eol = buf[charPos];
+                        if ((c == '\n' || c == '\r')) {
+                            eol = c;
                         }
-                    } else if (eol == '\r' && (buf[charPos] == '\n')) {
+                    } else if (eol == '\r' && c == '\n') {
                         if (charPos > pos) {
                             result.append(buf, pos, charPos - pos - 1);
                         }
@@ -410,11 +453,10 @@
                     }
                 }
                 if (eol == '\0') {
-                    result.append(buf, pos, count - pos);
+                    result.append(buf, pos, end - pos);
                 } else {
-                    result.append(buf, pos, count - pos - 1);
+                    result.append(buf, pos, end - pos - 1);
                 }
-                pos = count;
             }
         }
 
@@ -437,7 +479,7 @@
             if (isClosed()) {
                 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
             }
-            return ((count - pos) > 0) || in.ready();
+            return ((end - pos) > 0) || in.ready();
         }
     }
 
@@ -457,17 +499,17 @@
             if (isClosed()) {
                 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
             }
-            if (markpos == -1) {
+            if (mark == -1) {
                 throw new IOException(Msg.getString("K005c")); //$NON-NLS-1$
             }
-            pos = markpos;
+            pos = mark;
         }
     }
 
     /**
      * Skips {@code amount} characters in this reader. Subsequent
      * {@code read()}s will not return these characters unless {@code reset()}
-     * is used. Skipping characters may invalidate a mark if {@code readlimit}
+     * is used. Skipping characters may invalidate a mark if {@code markLimit}
      * is surpassed.
      * 
      * @param amount
@@ -493,24 +535,24 @@
             if (amount < 1) {
                 return 0;
             }
-            if (count - pos >= amount) {
+            if (end - pos >= amount) {
                 pos += amount;
                 return amount;
             }
 
-            long read = count - pos;
-            pos = count;
+            long read = end - pos;
+            pos = end;
             while (read < amount) {
-                if (fillbuf() == -1) {
+                if (fillBuf() == -1) {
                     return read;
                 }
-                if (count - pos >= amount - read) {
+                if (end - pos >= amount - read) {
                     pos += amount - read;
                     return amount;
                 }
                 // Couldn't get all the characters, skip what we read
-                read += (count - pos);
-                pos = count;
+                read += (end - pos);
+                pos = end;
             }
             return amount;
         }
diff --git a/luni/src/main/java/java/io/PipedInputStream.java b/luni/src/main/java/java/io/PipedInputStream.java
index a6b0336..6d1c007 100644
--- a/luni/src/main/java/java/io/PipedInputStream.java
+++ b/luni/src/main/java/java/io/PipedInputStream.java
@@ -15,6 +15,13 @@
  *  limitations under the License.
  */
 
+// BEGIN android-note
+// We've made several changes including:
+//  - delayed buffer creation until pipe connection
+//  - throw an IOException when a pipe is closed during a write
+//  - improved consistency with PipedReader
+// END android-note
+
 package java.io;
 
 import org.apache.harmony.luni.util.Msg;
@@ -28,9 +35,11 @@
  */
 public class PipedInputStream extends InputStream {
 
-    private Thread lastReader, lastWriter;
+    private Thread lastReader;
 
-    private boolean isClosed = false;
+    private Thread lastWriter;
+
+    private boolean isClosed;
 
     /**
      * The circular buffer through which data is passed. Data is read from the
@@ -60,7 +69,7 @@
     /**
      * The index in {@code buffer} where the next byte will be read.
      */
-    protected int out = 0;
+    protected int out;
 
     /**
      * The size of the default pipe in bytes.
@@ -70,16 +79,14 @@
     /**
      * Indicates if this pipe is connected.
      */
-    boolean isConnected = false;
+    boolean isConnected;
 
     /**
      * Constructs a new unconnected {@code PipedInputStream}. The resulting
      * stream must be connected to a {@link PipedOutputStream} before data may
      * be read from it.
      */
-    public PipedInputStream() {
-        /* empty */
-    }
+    public PipedInputStream() {}
 
     /**
      * Constructs a new {@code PipedInputStream} connected to the
@@ -120,14 +127,9 @@
      *             if an error occurs while closing this stream.
      */
     @Override
-    public void close() throws IOException {
-        synchronized (this) {
-            /* No exception thrown if already closed */
-            if (buffer != null) {
-                /* Release buffer to indicate closed. */
-                buffer = null;
-            }
-        }
+    public synchronized void close() throws IOException {
+        buffer = null;
+        notifyAll();
     }
 
     /**
@@ -145,6 +147,20 @@
     }
 
     /**
+     * Establishes the connection to the PipedOutputStream.
+     *
+     * @throws IOException
+     *             If this Reader is already connected.
+     */
+    synchronized void establishConnection() throws IOException {
+        if (isConnected) {
+            throw new IOException(Msg.getString("K007a")); //$NON-NLS-1$
+        }
+        buffer = new byte[PipedInputStream.PIPE_SIZE];
+        isConnected = true;
+    }
+
+    /**
      * Reads a single byte from this stream and returns it as an integer in the
      * range from 0 to 255. Returns -1 if the end of this stream has been
      * reached. If there is no data in the pipe, this method blocks until data
@@ -408,20 +424,21 @@
         } catch (InterruptedException e) {
             throw new InterruptedIOException();
         }
-        if (buffer != null) {
-            if (in == -1) {
-                in = 0;
-            }
-            buffer[in++] = (byte) oneByte;
-            if (in == buffer.length) {
-                in = 0;
-            }
-
-            // BEGIN android-added
-            // let blocked readers read the newly available data
-            notifyAll();
-            // END android-added
+        if (buffer == null) {
+            throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$
         }
+        if (in == -1) {
+            in = 0;
+        }
+        buffer[in++] = (byte) oneByte;
+        if (in == buffer.length) {
+            in = 0;
+        }
+
+        // BEGIN android-added
+        // let blocked readers read the newly available data
+        notifyAll();
+        // END android-added
     }
 
     synchronized void done() {
diff --git a/luni/src/main/java/java/io/PipedOutputStream.java b/luni/src/main/java/java/io/PipedOutputStream.java
index 15ee930..4b640d4 100644
--- a/luni/src/main/java/java/io/PipedOutputStream.java
+++ b/luni/src/main/java/java/io/PipedOutputStream.java
@@ -15,6 +15,12 @@
  *  limitations under the License.
  */
 
+// BEGIN android-note
+// We've made several changes including:
+//  - avoid shallow concurrency problems
+//  - improved consistency with PipedWriter
+// END android-note
+
 package java.io;
 
 import org.apache.harmony.luni.util.Msg;
@@ -67,8 +73,9 @@
     @Override
     public void close() throws IOException {
         // Is the pipe connected?
-        if (dest != null) {
-            dest.done();
+        PipedInputStream stream = dest;
+        if (stream != null) {
+            stream.done();
             dest = null;
         }
     }
@@ -83,18 +90,17 @@
      *             if either stream is already connected.
      */
     public void connect(PipedInputStream stream) throws IOException {
-        if (null == stream) {
+        if (stream == null) {
             throw new NullPointerException();
         }
-        if (this.dest != null) {
-            throw new IOException(Msg.getString("K0079")); //$NON-NLS-1$
-        }
         synchronized (stream) {
+            if (this.dest != null) {
+                throw new IOException(Msg.getString("K0079")); //$NON-NLS-1$
+            }
             if (stream.isConnected) {
                 throw new IOException(Msg.getString("K007a")); //$NON-NLS-1$
             }
-            stream.buffer = new byte[PipedInputStream.PIPE_SIZE];
-            stream.isConnected = true;
+            stream.establishConnection();
             this.dest = stream;
         }
     }
@@ -108,10 +114,13 @@
      */
     @Override
     public void flush() throws IOException {
-        if (dest != null) {
-            synchronized (dest) {
-                dest.notifyAll();
-            }
+        PipedInputStream stream = dest;
+        if (stream == null) {
+            return;
+        }
+
+        synchronized (stream) {
+            stream.notifyAll();
         }
     }
 
@@ -145,13 +154,6 @@
      */
     @Override
     public void write(byte[] buffer, int offset, int count) throws IOException {
-        // BEGIN android-note
-        // changed array notation to be consistent with the rest of harmony
-        // END android-note
-        if (dest == null) {
-            // K007b=Pipe Not Connected
-            throw new IOException(Msg.getString("K007b")); //$NON-NLS-1$
-        }
         super.write(buffer, offset, count);
     }
 
@@ -177,9 +179,11 @@
      */
     @Override
     public void write(int oneByte) throws IOException {
-        if (dest == null) {
+        PipedInputStream stream = dest;
+        if (stream == null) {
+            // K007b=Pipe Not Connected
             throw new IOException(Msg.getString("K007b")); //$NON-NLS-1$
         }
-        dest.receive(oneByte);
+        stream.receive(oneByte);
     }
 }
diff --git a/luni/src/main/java/java/io/PipedReader.java b/luni/src/main/java/java/io/PipedReader.java
index 251ac90..d137a0d 100644
--- a/luni/src/main/java/java/io/PipedReader.java
+++ b/luni/src/main/java/java/io/PipedReader.java
@@ -17,6 +17,13 @@
 
 package java.io;
 
+// BEGIN android-note
+// We've made several changes including:
+//  - throw an IOException when a pipe is closed during a write
+//  - fix shallow concurrency problems, always lock on 'this'
+//  - improved consistency with PipedInputStream
+// END android-note
+
 import org.apache.harmony.luni.util.Msg;
 
 /**
@@ -35,13 +42,27 @@
     private boolean isClosed;
 
     /**
-     * The circular buffer through which data is passed.
+     * The circular buffer through which data is passed. Data is read from the
+     * range {@code [out, in)} and written to the range {@code [in, out)}.
+     * Data in the buffer is either sequential: <pre>
+     *     { - - - X X X X X X X - - - - - }
+     *             ^             ^
+     *             |             |
+     *            out           in</pre>
+     * ...or wrapped around the buffer's end: <pre>
+     *     { X X X X - - - - - - - - X X X }
+     *               ^               ^
+     *               |               |
+     *              in              out</pre>
+     * When the buffer is empty, {@code in == -1}. Reading when the buffer is
+     * empty will block until data is available. When the buffer is full,
+     * {@code in == out}. Writing when the buffer is full will block until free
+     * space is available.
      */
-    private char data[];
+    private char[] buffer;
 
     /**
-     * The index in {@code buffer} where the next character will be
-     * written.
+     * The index in {@code buffer} where the next character will be written.
      */
     private int in = -1;
 
@@ -58,18 +79,14 @@
     /**
      * Indicates if this pipe is connected
      */
-    private boolean isConnected;
+    boolean isConnected;
 
     /**
      * Constructs a new unconnected {@code PipedReader}. The resulting reader
      * must be connected to a {@code PipedWriter} before data may be read from
      * it.
-     *
-     * @see PipedWriter
      */
-    public PipedReader() {
-        data = new char[PIPE_SIZE];
-    }
+    public PipedReader() {}
 
     /**
      * Constructs a new {@code PipedReader} connected to the {@link PipedWriter}
@@ -82,7 +99,6 @@
      *             if {@code out} is already connected.
      */
     public PipedReader(PipedWriter out) throws IOException {
-        this();
         connect(out);
     }
 
@@ -94,14 +110,9 @@
      *             if an error occurs while closing this reader.
      */
     @Override
-    public void close() throws IOException {
-        synchronized (lock) {
-            /* No exception thrown if already closed */
-            if (data != null) {
-                /* Release buffer to indicate closed. */
-                data = null;
-            }
-        }
+    public synchronized void close() throws IOException {
+        buffer = null;
+        notifyAll();
     }
 
     /**
@@ -115,9 +126,7 @@
      *             src} is already connected.
      */
     public void connect(PipedWriter src) throws IOException {
-        synchronized (lock) {
-            src.connect(this);
-        }
+        src.connect(this);
     }
 
     /**
@@ -126,16 +135,12 @@
      * @throws IOException
      *             If this Reader is already connected.
      */
-    void establishConnection() throws IOException {
-        synchronized (lock) {
-            if (data == null) {
-                throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$
-            }
-            if (isConnected) {
-                throw new IOException(Msg.getString("K007a")); //$NON-NLS-1$
-            }
-            isConnected = true;
+    synchronized void establishConnection() throws IOException {
+        if (isConnected) {
+            throw new IOException(Msg.getString("K007a")); //$NON-NLS-1$
         }
+        buffer = new char[PIPE_SIZE];
+        isConnected = true;
     }
 
     /**
@@ -192,95 +197,93 @@
      *             alive.
      */
     @Override
-    public int read(char[] buffer, int offset, int count) throws IOException {
-        synchronized (lock) {
-            if (!isConnected) {
-                throw new IOException(Msg.getString("K007b")); //$NON-NLS-1$
-            }
-            if (data == null) {
-                throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$
-            }
-            // avoid int overflow
-            // BEGIN android-changed
-            // Exception priorities (in case of multiple errors) differ from
-            // RI, but are spec-compliant.
-            // made implicit null check explicit,
-            // used (offset | count) < 0 instead of (offset < 0) || (count < 0)
-            // to safe one operation
-            if (buffer == null) {
-                throw new NullPointerException(Msg.getString("K0047")); //$NON-NLS-1$
-            }
-            if ((offset | count) < 0 || count > buffer.length - offset) {
-                throw new IndexOutOfBoundsException(Msg.getString("K002f")); //$NON-NLS-1$
-            }
-            // END android-changed
-            if (count == 0) {
-                return 0;
-            }
-            /**
-             * Set the last thread to be reading on this PipedReader. If
-             * lastReader dies while someone is waiting to write an IOException
-             * of "Pipe broken" will be thrown in receive()
-             */
-            lastReader = Thread.currentThread();
-            try {
-                boolean first = true;
-                while (in == -1) {
-                    // Are we at end of stream?
-                    if (isClosed) {
-                        return -1;
-                    }
-                    if (!first && lastWriter != null && !lastWriter.isAlive()) {
-                        throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$
-                    }
-                    first = false;
-                    // Notify callers of receive()
-                    lock.notifyAll();
-                    lock.wait(1000);
+    public synchronized int read(char[] buffer, int offset, int count) throws IOException {
+        if (!isConnected) {
+            throw new IOException(Msg.getString("K007b")); //$NON-NLS-1$
+        }
+        if (this.buffer == null) {
+            throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$
+        }
+        // avoid int overflow
+        // BEGIN android-changed
+        // Exception priorities (in case of multiple errors) differ from
+        // RI, but are spec-compliant.
+        // made implicit null check explicit,
+        // used (offset | count) < 0 instead of (offset < 0) || (count < 0)
+        // to safe one operation
+        if (buffer == null) {
+            throw new NullPointerException(Msg.getString("K0047")); //$NON-NLS-1$
+        }
+        if ((offset | count) < 0 || count > buffer.length - offset) {
+            throw new IndexOutOfBoundsException(Msg.getString("K002f")); //$NON-NLS-1$
+        }
+        // END android-changed
+        if (count == 0) {
+            return 0;
+        }
+        /**
+         * Set the last thread to be reading on this PipedReader. If
+         * lastReader dies while someone is waiting to write an IOException
+         * of "Pipe broken" will be thrown in receive()
+         */
+        lastReader = Thread.currentThread();
+        try {
+            boolean first = true;
+            while (in == -1) {
+                // Are we at end of stream?
+                if (isClosed) {
+                    return -1;
                 }
-            } catch (InterruptedException e) {
-                throw new InterruptedIOException();
-            }
-
-            int copyLength = 0;
-            /* Copy chars from out to end of buffer first */
-            if (out >= in) {
-                copyLength = count > data.length - out ? data.length - out
-                        : count;
-                System.arraycopy(data, out, buffer, offset, copyLength);
-                out += copyLength;
-                if (out == data.length) {
-                    out = 0;
+                if (!first && lastWriter != null && !lastWriter.isAlive()) {
+                    throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$
                 }
-                if (out == in) {
-                    // empty buffer
-                    in = -1;
-                    out = 0;
-                }
+                first = false;
+                // Notify callers of receive()
+                notifyAll();
+                wait(1000);
             }
+        } catch (InterruptedException e) {
+            throw new InterruptedIOException();
+        }
 
-            /*
-             * Did the read fully succeed in the previous copy or is the buffer
-             * empty?
-             */
-            if (copyLength == count || in == -1) {
-                return copyLength;
-            }
-
-            int charsCopied = copyLength;
-            /* Copy bytes from 0 to the number of available bytes */
-            copyLength = in - out > count - copyLength ? count - copyLength
-                    : in - out;
-            System.arraycopy(data, out, buffer, offset + charsCopied,
-                    copyLength);
+        int copyLength = 0;
+        /* Copy chars from out to end of buffer first */
+        if (out >= in) {
+            copyLength = count > this.buffer.length - out ? this.buffer.length - out
+                    : count;
+            System.arraycopy(this.buffer, out, buffer, offset, copyLength);
             out += copyLength;
+            if (out == this.buffer.length) {
+                out = 0;
+            }
             if (out == in) {
                 // empty buffer
                 in = -1;
                 out = 0;
             }
-            return charsCopied + copyLength;
         }
+
+        /*
+         * Did the read fully succeed in the previous copy or is the buffer
+         * empty?
+         */
+        if (copyLength == count || in == -1) {
+            return copyLength;
+        }
+
+        int charsCopied = copyLength;
+        /* Copy bytes from 0 to the number of available bytes */
+        copyLength = in - out > count - copyLength ? count - copyLength
+                : in - out;
+        System.arraycopy(this.buffer, out, buffer, offset + charsCopied,
+                copyLength);
+        out += copyLength;
+        if (out == in) {
+            // empty buffer
+            in = -1;
+            out = 0;
+        }
+        return charsCopied + copyLength;
     }
 
     /**
@@ -298,16 +301,14 @@
      * @see #read(char[], int, int)
      */
     @Override
-    public boolean ready() throws IOException {
-        synchronized (lock) {
-            if (!isConnected) {
-                throw new IOException(Msg.getString("K007b")); //$NON-NLS-1$
-            }
-            if (data == null) {
-                throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$
-            }
-            return in != -1;
+    public synchronized boolean ready() throws IOException {
+        if (!isConnected) {
+            throw new IOException(Msg.getString("K007b")); //$NON-NLS-1$
         }
+        if (buffer == null) {
+            throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$
+        }
+        return in != -1;
     }
 
     /**
@@ -324,43 +325,41 @@
      *             If the stream is already closed or another IOException
      *             occurs.
      */
-    void receive(char oneChar) throws IOException {
-        synchronized (lock) {
-            if (data == null) {
-                throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$
-            }
-            if (lastReader != null && !lastReader.isAlive()) {
-                throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$
-            }
-            /*
-             * Set the last thread to be writing on this PipedWriter. If
-             * lastWriter dies while someone is waiting to read an IOException
-             * of "Pipe broken" will be thrown in read()
-             */
-            lastWriter = Thread.currentThread();
-            try {
-                while (data != null && out == in) {
-                    lock.notifyAll();
-                    // BEGIN android-changed
-                    lock.wait(1000);
-                    // END android-changed
-                    if (lastReader != null && !lastReader.isAlive()) {
-                        throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$
-                    }
+    synchronized void receive(char oneChar) throws IOException {
+        if (buffer == null) {
+            throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$
+        }
+        if (lastReader != null && !lastReader.isAlive()) {
+            throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$
+        }
+        /*
+        * Set the last thread to be writing on this PipedWriter. If
+        * lastWriter dies while someone is waiting to read an IOException
+        * of "Pipe broken" will be thrown in read()
+        */
+        lastWriter = Thread.currentThread();
+        try {
+            while (buffer != null && out == in) {
+                notifyAll();
+                // BEGIN android-changed
+                wait(1000);
+                // END android-changed
+                if (lastReader != null && !lastReader.isAlive()) {
+                    throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$
                 }
-            } catch (InterruptedException e) {
-                throw new InterruptedIOException();
             }
-            if (data != null) {
-                if (in == -1) {
-                    in = 0;
-                }
-                data[in++] = oneChar;
-                if (in == data.length) {
-                    in = 0;
-                }
-                return;
-            }
+        } catch (InterruptedException e) {
+            throw new InterruptedIOException();
+        }
+        if (buffer == null) {
+            throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$
+        }
+        if (in == -1) {
+            in = 0;
+        }
+        buffer[in++] = oneChar;
+        if (in == buffer.length) {
+            in = 0;
         }
     }
 
@@ -382,80 +381,73 @@
      *             If the stream is already closed or another IOException
      *             occurs.
      */
-    void receive(char[] chars, int offset, int count) throws IOException {
-        synchronized (lock) {
-            if (data == null) {
+    synchronized void receive(char[] chars, int offset, int count) throws IOException {
+        if (chars == null) {
+            throw new NullPointerException(Msg.getString("K0047")); //$NON-NLS-1$
+        }
+        if ((offset | count) < 0 || count > chars.length - offset) {
+            throw new IndexOutOfBoundsException(Msg.getString("K002f")); //$NON-NLS-1$
+        }
+        if (buffer == null) {
+            throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$
+        }
+        if (lastReader != null && !lastReader.isAlive()) {
+            throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$
+        }
+        /**
+         * Set the last thread to be writing on this PipedWriter. If
+         * lastWriter dies while someone is waiting to read an IOException
+         * of "Pipe broken" will be thrown in read()
+         */
+        lastWriter = Thread.currentThread();
+        while (count > 0) {
+            try {
+                while (buffer != null && out == in) {
+                    notifyAll();
+                    // BEGIN android-changed
+                    wait(1000);
+                    // END android-changed
+                    if (lastReader != null && !lastReader.isAlive()) {
+                        throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$
+                    }
+                }
+            } catch (InterruptedException e) {
+                throw new InterruptedIOException();
+            }
+            if (buffer == null) {
                 throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$
             }
-            if (lastReader != null && !lastReader.isAlive()) {
-                throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$
+            if (in == -1) {
+                in = 0;
             }
-            /**
-             * Set the last thread to be writing on this PipedWriter. If
-             * lastWriter dies while someone is waiting to read an IOException
-             * of "Pipe broken" will be thrown in read()
-             */
-            lastWriter = Thread.currentThread();
-            while (count > 0) {
-                try {
-                    while (data != null && out == in) {
-                        lock.notifyAll();
-                        // BEGIN android-changed
-                        lock.wait(1000);
-                        // END android-changed
-                        if (lastReader != null && !lastReader.isAlive()) {
-                            throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$
-                        }
-                    }
-                } catch (InterruptedException e) {
-                    throw new InterruptedIOException();
+            if (in >= out) {
+                int length = buffer.length - in;
+                if (count < length) {
+                    length = count;
                 }
-                if (data == null) {
-                    break;
-                }
-                if (in == -1) {
+                System.arraycopy(chars, offset, buffer, in, length);
+                offset += length;
+                count -= length;
+                in += length;
+                if (in == buffer.length) {
                     in = 0;
                 }
-                if (in >= out) {
-                    int length = data.length - in;
-                    if (count < length) {
-                        length = count;
-                    }
-                    System.arraycopy(chars, offset, data, in, length);
-                    offset += length;
-                    count -= length;
-                    in += length;
-                    if (in == data.length) {
-                        in = 0;
-                    }
-                }
-                if (count > 0 && in != out) {
-                    int length = out - in;
-                    if (count < length) {
-                        length = count;
-                    }
-                    System.arraycopy(chars, offset, data, in, length);
-                    offset += length;
-                    count -= length;
-                    in += length;
-                }
             }
-            if (count == 0) {
-                return;
+            if (count > 0 && in != out) {
+                int length = out - in;
+                if (count < length) {
+                    length = count;
+                }
+                System.arraycopy(chars, offset, buffer, in, length);
+                offset += length;
+                count -= length;
+                in += length;
             }
         }
     }
 
-    void done() {
-        synchronized (lock) {
-            isClosed = true;
-            lock.notifyAll();
-        }
-    }
-
-    void flush() {
-        synchronized (lock) {
-            lock.notifyAll();
-        }
+    synchronized void done() {
+        isClosed = true;
+        notifyAll();
     }
 }
diff --git a/luni/src/main/java/java/io/PipedWriter.java b/luni/src/main/java/java/io/PipedWriter.java
index 5fc968d..b0bfa98 100644
--- a/luni/src/main/java/java/io/PipedWriter.java
+++ b/luni/src/main/java/java/io/PipedWriter.java
@@ -15,6 +15,14 @@
  *  limitations under the License.
  */
 
+// BEGIN android-note
+// We've made several changes including:
+//  - move checks into the synchronized method in PipedReader
+//  - reply on PipedReader's isClosed field (rather than having 2 flags)
+//  - avoid shallow concurrency problems
+//  - improved consistency with PipedOutputStream
+// END android-note
+
 package java.io;
 
 import org.apache.harmony.luni.util.Msg;
@@ -32,8 +40,6 @@
      */
     private PipedReader dest;
 
-    private boolean closed;
-
     /**
      * Constructs a new unconnected {@code PipedWriter}. The resulting writer
      * must be connected to a {@code PipedReader} before data may be written to
@@ -70,13 +76,10 @@
      */
     @Override
     public void close() throws IOException {
-        synchronized (lock) {
-            /* Is the pipe connected? */
-            if (dest != null) {
-                dest.done();
-                dest = null;
-            }
-            closed = true;
+        PipedReader reader = dest;
+        if (reader != null) {
+            reader.done();
+            dest = null;
         }
     }
 
@@ -84,22 +87,26 @@
      * Connects this {@code PipedWriter} to a {@link PipedReader}. Any data
      * written to this writer becomes readable in the reader.
      *
-     * @param stream
+     * @param reader
      *            the reader to connect to.
      * @throws IOException
      *             if this writer is closed or already connected, or if {@code
-     *             stream} is already connected.
+     *             reader} is already connected.
      */
-    public void connect(PipedReader stream) throws IOException {
-        synchronized (lock) {
+    public void connect(PipedReader reader) throws IOException {
+        if (reader == null) {
+            throw new NullPointerException();
+        }
+        synchronized (reader) {
             if (this.dest != null) {
                 throw new IOException(Msg.getString("K0079")); //$NON-NLS-1$
             }
-            if (closed) {
+            if (reader.isConnected) {
                 throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$
             }
-            stream.establishConnection();
-            this.dest = stream;
+            reader.establishConnection();
+            this.lock = reader;
+            this.dest = reader;
         }
     }
 
@@ -112,8 +119,13 @@
      */
     @Override
     public void flush() throws IOException {
-        if (dest != null) {
-            dest.flush();
+        PipedReader reader = dest;
+        if (reader == null) {
+            return;
+        }
+
+        synchronized (reader) {
+            reader.notifyAll();
         }
     }
 
@@ -150,32 +162,12 @@
      */
     @Override
     public void write(char[] buffer, int offset, int count) throws IOException {
-        // BEGIN android-note
-        // changed array notation to be consistent with the rest of harmony
-        // END android-note
-        synchronized (lock) {
-            if (closed) {
-                throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$
-            }
-            if (dest == null) {
-                throw new IOException(Msg.getString("K007b")); //$NON-NLS-1$
-            }
-            if (buffer == null) {
-                throw new NullPointerException(Msg.getString("K0047")); //$NON-NLS-1$
-            }
-
-            // avoid int overflow
-            // BEGIN android-changed
-            // Exception priorities (in case of multiple errors) differ from
-            // RI, but are spec-compliant.
-            // removed redundant check, used (offset | count) < 0
-            // instead of (offset < 0) || (count < 0) to safe one operation
-            if ((offset | count) < 0 || count > buffer.length - offset) {
-                throw new IndexOutOfBoundsException(Msg.getString("K002f")); //$NON-NLS-1$
-            }
-            // END android-changed
-            dest.receive(buffer, offset, count);
+        PipedReader reader = dest;
+        if (reader == null) {
+            // K007b=Pipe Not Connected
+            throw new IOException(Msg.getString("K007b")); //$NON-NLS-1$
         }
+        reader.receive(buffer, offset, count);
     }
 
     /**
@@ -200,14 +192,11 @@
      */
     @Override
     public void write(int c) throws IOException {
-        synchronized (lock) {
-            if (closed) {
-                throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$
-            }
-            if (dest == null) {
-                throw new IOException(Msg.getString("K007b")); //$NON-NLS-1$
-            }
-            dest.receive((char) c);
+        PipedReader reader = dest;
+        if (reader == null) {
+            // K007b=Pipe Not Connected
+            throw new IOException(Msg.getString("K007b")); //$NON-NLS-1$
         }
+        reader.receive((char) c);
     }
 }
diff --git a/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java b/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java
index 7ae9de6..cbecab5 100644
--- a/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java
+++ b/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java
@@ -342,7 +342,9 @@
 
     @Override
     public void setTimeToLive(int ttl) throws java.io.IOException {
-        setOption(IP_MULTICAST_TTL, Byte.valueOf((byte) (ttl & 0xFF)));
+        // BEGIN android-changed: native code wants an int anyway
+        setOption(IP_MULTICAST_TTL, Integer.valueOf(ttl));
+        // END android-changed
         if ((netImpl.getSocketFlags() & MULTICAST_TTL) != 0) {
             this.ttl = ttl;
         }
@@ -350,10 +352,9 @@
 
     @Override
     public void setTTL(byte ttl) throws java.io.IOException {
-        setOption(IP_MULTICAST_TTL, Byte.valueOf(ttl));
-        if ((netImpl.getSocketFlags() & MULTICAST_TTL) != 0) {
-            this.ttl = ttl;
-        }
+        // BEGIN android-changed: remove duplication
+        setTimeToLive(ttl);
+        // END android-changed
     }
 
     @Override
diff --git a/luni/src/main/native/java_net_NetworkInterface.cpp b/luni/src/main/native/java_net_NetworkInterface.cpp
index abc6e16..52df6e1 100644
--- a/luni/src/main/native/java_net_NetworkInterface.cpp
+++ b/luni/src/main/native/java_net_NetworkInterface.cpp
@@ -20,7 +20,6 @@
 #include "jni.h"
 
 #include <errno.h>
-#include <net/if.h>
 #include <netinet/in.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -29,6 +28,8 @@
 #include <sys/socket.h>
 #include <unistd.h>
 
+#include <net/if.h> // Note: Can't appear before <sys/socket.h> on OS X.
+
 // A smart pointer that closes the given fd on going out of scope.
 // TODO: make this generally available.
 class scoped_fd {
@@ -74,6 +75,7 @@
     jniThrowException(env, "java/lang/OutOfMemoryError", "native heap");
 }
 
+// TODO(enh): move to JNIHelp.h
 static void jniThrowSocketException(JNIEnv* env) {
     char buf[BUFSIZ];
     jniThrowException(env, "java/net/SocketException",
diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp
index aaa10d3..45319f8 100644
--- a/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp
+++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp
@@ -51,9 +51,15 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
-#include <sys/sendfile.h>
 #include <sys/uio.h>
 
+#if HAVE_SYS_SENDFILE_H
+#include <sys/sendfile.h>
+#else
+#include <sys/socket.h>
+#include <sys/types.h>
+#endif
+
 static void convertToPlatform(char *path) {
     char *pathIndex;
 
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
index 5020ada..33c9119 100644
--- a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
+++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
@@ -44,7 +44,11 @@
 
 // Temporary hack to build on systems that don't have up-to-date libc headers.
 #ifndef IPV6_TCLASS
-#define IPV6_TCLASS 67
+#ifdef __linux__
+#define IPV6_TCLASS 67 // Linux
+#else
+#define IPV6_TCLASS -1 // BSD(-like); TODO: Something better than this!
+#endif
 #endif
 
 /**
@@ -112,7 +116,7 @@
 #define JAVASOCKOPT_SO_KEEPALIVE 8
 #define JAVASOCKOPT_MCAST_TIME_TO_LIVE 10 /* Currently unused */
 #define JAVASOCKOPT_SO_BINDADDR 15
-#define JAVASOCKOPT_MCAST_INTERFACE 16
+#define JAVASOCKOPT_IP_MULTICAST_IF 16
 #define JAVASOCKOPT_MCAST_TTL 17
 #define JAVASOCKOPT_IP_MULTICAST_LOOP 18
 #define JAVASOCKOPT_MCAST_ADD_MEMBERSHIP 19
@@ -197,6 +201,9 @@
 
 /**
  * Throws an SocketException with the message affiliated with the errorCode.
+ * 
+ * @deprecated: 'errorCode' is one of the bogus SOCKERR_ values, *not* errno.
+ * jniThrowSocketException is the better choice.
  */
 static void throwSocketException(JNIEnv *env, int errorCode) {
     jniThrowException(env, "java/net/SocketException",
@@ -204,26 +211,29 @@
 }
 
 // TODO(enh): move to JNIHelp.h
-static void throwNullPointerException(JNIEnv *env) {
-    jniThrowException(env, "java/lang/NullPointerException", NULL);
+static void jniThrowSocketException(JNIEnv* env, int error) {
+    char buf[BUFSIZ];
+    jniThrowException(env, "java/net/SocketException",
+            jniStrError(error, buf, sizeof(buf)));
 }
 
 // TODO(enh): move to JNIHelp.h
-static void jniThrowAssertionError(JNIEnv* env, const char* message) {
-    jniThrowException(env, "java/lang/AssertionError", message);
+static void throwNullPointerException(JNIEnv *env) {
+    jniThrowException(env, "java/lang/NullPointerException", NULL);
 }
 
 // Used by functions that shouldn't throw SocketException. (These functions
 // aren't meant to see bad addresses, so seeing one really does imply an
 // internal error.)
+// TODO: fix the code (native and Java) so we don't paint ourselves into this corner.
 static void jniThrowBadAddressFamily(JNIEnv* env) {
-    jniThrowAssertionError(env, "Bad address family");
+    jniThrowException(env, "java/lang/IllegalArgumentException", "Bad address family");
 }
 
 static bool jniGetFd(JNIEnv* env, jobject fileDescriptor, int& fd) {
     fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     if (fd == -1) {
-        throwSocketException(env, SOCKERR_BADDESC);
+        jniThrowSocketException(env, EBADF);
         return false;
     }
     return true;
@@ -293,6 +303,14 @@
             addr.s6_addr32[2] == htonl(0xffff));
 }
 
+jobject byteArrayToInetAddress(JNIEnv* env, jbyteArray byteArray) {
+    if (byteArray == NULL) {
+        return NULL;
+    }
+    return env->CallStaticObjectMethod(gCachedFields.iaddr_class,
+            gCachedFields.iaddr_getbyaddress, byteArray);
+}
+
 /**
  * Converts a native address structure to an InetAddress object.
  * Throws a NullPointerException or an IOException in case of
@@ -304,11 +322,7 @@
  */
 jobject socketAddressToInetAddress(JNIEnv* env, sockaddr_storage* sockAddress) {
     jbyteArray byteArray = socketAddressToByteArray(env, sockAddress);
-    if (byteArray == NULL) {
-        return NULL;
-    }
-    return env->CallStaticObjectMethod(gCachedFields.iaddr_class,
-            gCachedFields.iaddr_getbyaddress, byteArray);
+    return byteArrayToInetAddress(env, byteArray);
 }
 
 /**
@@ -358,10 +372,10 @@
 
     // Convert the IP address bytes to the proper IP address type.
     size_t addressLength = env->GetArrayLength(addressBytes);
+    memset(sockaddress, 0, sizeof(*sockaddress));
     if (addressLength == 4) {
         // IPv4 address.
         sockaddr_in *sin = reinterpret_cast<sockaddr_in*>(sockaddress);
-        memset(sin, 0, sizeof(*sin));
         sin->sin_family = AF_INET;
         sin->sin_port = htons(port);
         jbyte* dst = reinterpret_cast<jbyte*>(&sin->sin_addr.s_addr);
@@ -369,7 +383,6 @@
     } else if (addressLength == 16) {
         // IPv6 address.
         sockaddr_in6 *sin6 = reinterpret_cast<sockaddr_in6*>(sockaddress);
-        memset(sin6, 0, sizeof(*sin6));
         sin6->sin6_family = AF_INET6;
         sin6->sin6_port = htons(port);
         jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr);
@@ -399,37 +412,6 @@
 }
 
 /**
- * Convert a sockaddr_storage structure to a Java string.
- *
- * @param address pointer to sockaddr_storage structure to convert.
- * @param withPort whether to include the port number in the output as well.
- */
-static bool socketAddressToString(JNIEnv* env,
-        sockaddr_storage* address, char* ipString, size_t len) {
-    // TODO: getnameinfo seems to want its length parameter to be exactly
-    // sizeof(sockaddr_in) for an IPv4 address and sizeof (sockaddr_in6) for an
-    // IPv6 address. Fix getnameinfo so it accepts sizeof(sockaddr_storage), and
-    // then remove this hack.
-    int size;
-    if (address->ss_family == AF_INET) {
-        size = sizeof(sockaddr_in);
-    } else if (address->ss_family == AF_INET6) {
-        size = sizeof(sockaddr_in6);
-    } else {
-        jniThrowBadAddressFamily(env);
-        return false;
-    }
-
-    int rc = getnameinfo((sockaddr*) address, size, ipString, len, NULL,
-            0, NI_NUMERICHOST);
-    if (rc != 0) {
-        jniThrowException(env, "java/net/UnknownHostException", gai_strerror(rc));
-        return false;
-    }
-    return true;
-}
-
-/**
  * Convert a Java byte array representing an IP address to a Java string.
  *
  * @param addressByteArray the byte array to convert.
@@ -446,8 +428,25 @@
     if (!byteArrayToSocketAddress(env, byteArray, 0, &ss)) {
         return NULL;
     }
+    // TODO: getnameinfo seems to want its length parameter to be exactly
+    // sizeof(sockaddr_in) for an IPv4 address and sizeof (sockaddr_in6) for an
+    // IPv6 address. Fix getnameinfo so it accepts sizeof(sockaddr_storage), and
+    // then remove this hack.
+    int sa_size;
+    if (ss.ss_family == AF_INET) {
+        sa_size = sizeof(sockaddr_in);
+    } else if (ss.ss_family == AF_INET6) {
+        sa_size = sizeof(sockaddr_in6);
+    } else {
+        jniThrowBadAddressFamily(env);
+        return NULL;
+    }
+    // TODO: inet_ntop?
     char ipString[INET6_ADDRSTRLEN];
-    if (!socketAddressToString(env, &ss, ipString, sizeof(ipString))) {
+    int rc = getnameinfo(reinterpret_cast<sockaddr*>(&ss), sa_size,
+            ipString, sizeof(ipString), NULL, 0, NI_NUMERICHOST);
+    if (rc != 0) {
+        jniThrowException(env, "java/net/UnknownHostException", gai_strerror(rc));
         return NULL;
     }
     return env->NewStringUTF(ipString);
@@ -576,13 +575,8 @@
  *
  * @return  the new Integer
  */
-static jobject newJavaLangInteger(JNIEnv * env, jint anInt) {
-    jclass tempClass;
-    jmethodID tempMethod;
-
-    tempClass = gCachedFields.integer_class;
-    tempMethod = gCachedFields.integer_class_init;
-    return env->NewObject(tempClass, tempMethod, anInt);
+static jobject newJavaLangInteger(JNIEnv* env, jint anInt) {
+    return env->NewObject(gCachedFields.integer_class, gCachedFields.integer_class_init, anInt);
 }
 
 // Converts a number of milliseconds to a timeval.
@@ -869,7 +863,7 @@
          */
         handle = jniGetFDFromFileDescriptor(env, fileDescriptor);
         if (handle == -1) {
-            throwSocketException(env, SOCKERR_INTERRUPTED);
+            jniThrowSocketException(env, EINTR);
             return -1;
         }
 
@@ -1149,10 +1143,10 @@
     switch(level) {
         case SOL_SOCKET:
             return "SOL_SOCKET";
-        case SOL_IP:
-            return "SOL_IP";
-        case SOL_IPV6:
-            return "SOL_IPV6";
+        case IPPROTO_IP:
+            return "IPPROTO_IP";
+        case IPPROTO_IPV6:
+            return "IPPROTO_IPV6";
         default:
             return "SOL_???";
     }
@@ -1181,11 +1175,11 @@
     switch (family) {
         case AF_INET:
             option = ipv4Option;
-            protocol = SOL_IP;
+            protocol = IPPROTO_IP;
             break;
         case AF_INET6:
             option = ipv6Option;
-            protocol = SOL_IPV6;
+            protocol = IPPROTO_IPV6;
             break;
         default:
             // TODO(enh): throw Java exceptions from this method instead of just
@@ -1231,20 +1225,19 @@
  */
 static int interfaceIndexFromMulticastSocket(int socket) {
     int family = getSocketAddressFamily(socket);
-    socklen_t requestLength;
     int interfaceIndex;
     int result;
     if (family == AF_INET) {
         // IP_MULTICAST_IF returns a pointer to a struct ip_mreqn.
         struct ip_mreqn tempRequest;
-        requestLength = sizeof(tempRequest);
-        result = getsockopt(socket, SOL_IP, IP_MULTICAST_IF, &tempRequest,
+        socklen_t requestLength = sizeof(tempRequest);
+        result = getsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, &tempRequest,
             &requestLength);
         interfaceIndex = tempRequest.imr_ifindex;
     } else if (family == AF_INET6) {
         // IPV6_MULTICAST_IF returns a pointer to an integer.
-        requestLength = sizeof(interfaceIndex);
-        result = getsockopt(socket, SOL_IPV6, IPV6_MULTICAST_IF,
+        socklen_t requestLength = sizeof(interfaceIndex);
+        result = getsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_IF,
                 &interfaceIndex, &requestLength);
     } else {
         errno = EAFNOSUPPORT;
@@ -1305,7 +1298,7 @@
             interfaceIndex = interfaceIndexFromMulticastSocket(handle);
             multicastRequest.imr_ifindex = interfaceIndex;
             if (interfaceIndex == -1) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
         }
@@ -1315,16 +1308,16 @@
             return;
         }
         if (sockaddrP.ss_family != AF_INET) {
-            throwSocketException(env, SOCKERR_BADAF);
+            jniThrowSocketException(env, EAFNOSUPPORT);
             return;
         }
         struct sockaddr_in *sin = (struct sockaddr_in *) &sockaddrP;
         multicastRequest.imr_multiaddr = sin->sin_addr;
 
-        result = setsockopt(handle, SOL_IP, setSockOptVal,
+        result = setsockopt(handle, IPPROTO_IP, setSockOptVal,
                             &multicastRequest, length);
         if (0 != result) {
-            throwSocketException (env, convertError(errno));
+            jniThrowSocketException(env, errno);
             return;
         }
     } else {
@@ -1373,7 +1366,7 @@
                         ((struct sockaddr_in *) &sockaddrP)->sin_addr;
                 ipv4Request.imr_ifindex = interfaceIndex;
                 multicastRequest = &ipv4Request;
-                level = SOL_IP;
+                level = IPPROTO_IP;
                 break;
             case AF_INET6:
                 // setSockOptVal is passed in by the caller and may be IPv4-only
@@ -1389,18 +1382,18 @@
                         ((struct sockaddr_in6 *) &sockaddrP)->sin6_addr;
                 ipv6Request.ipv6mr_interface = interfaceIndex;
                 multicastRequest = &ipv6Request;
-                level = SOL_IPV6;
+                level = IPPROTO_IPV6;
                 break;
            default:
-               throwSocketException (env, SOCKERR_BADAF);
-               return;
+                jniThrowSocketException(env, EAFNOSUPPORT);
+                return;
         }
 
         /* join/drop the multicast address */
         result = setsockopt(handle, level, setSockOptVal, multicastRequest,
                             requestLength);
         if (0 != result) {
-            throwSocketException (env, convertError(errno));
+            jniThrowSocketException(env, errno);
             return;
         }
     }
@@ -1508,8 +1501,7 @@
         sock = socket(PF_INET, type, 0);
     }
     if (sock < 0) {
-        int err = convertError(errno);
-        throwSocketException(env, err);
+        jniThrowSocketException(env, errno);
         return sock;
     }
     jniSetFileDescriptorOfFD(env, fileDescriptor, sock);
@@ -1555,7 +1547,7 @@
             // available, so report "no bytes read".
             return 0;
         } else {
-            throwSocketException(env, convertError(errno));
+            jniThrowSocketException(env, errno);
             return 0;
         }
     }
@@ -1600,7 +1592,7 @@
             // it would block, so report "no bytes written".
             return 0;
         } else {
-            throwSocketException(env, convertError(errno));
+            jniThrowSocketException(env, errno);
             return 0;
         }
     }
@@ -1634,7 +1626,7 @@
     int block = nonblocking;
     int rc = ioctl(handle, FIONBIO, &block);
     if (rc == -1) {
-        throwSocketException(env, convertError(errno));
+        jniThrowSocketException(env, errno);
     }
 }
 
@@ -1720,8 +1712,8 @@
      * the descriptor sets that we will use
      */
     context =(jbyte *) malloc(sizeof(struct selectFDSet));
-    if (NULL == context) {
-        throwSocketException(env, SOCKERR_NOBUFFERS);
+    if (context == NULL) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", "native heap");
         return;
     }
 
@@ -1762,7 +1754,7 @@
         if (handle == -1) {
             sockConnectWithTimeout(handle, address, 0,
                     SOCKET_STEP_DONE, context);
-            throwSocketException(env, SOCKERR_BADDESC);
+            jniThrowSocketException(env, EBADF);
             goto bail;
         }
 
@@ -1856,7 +1848,7 @@
 
     int rc = listen(handle, backlog);
     if (rc == -1) {
-        throwSocketException(env, convertError(errno));
+        jniThrowSocketException(env, errno);
         return;
     }
 }
@@ -1889,8 +1881,7 @@
     result = recv(handle, (jbyte *) message, BUFFERSIZE, MSG_PEEK);
 
     if (0 > result) {
-        int err = convertError(errno);
-        throwSocketException(env, err);
+        jniThrowSocketException(env, errno);
         return 0;
     }
     return result;
@@ -1921,7 +1912,7 @@
     int clientFd = TEMP_FAILURE_RETRY(accept(serverFd,
             reinterpret_cast<sockaddr*>(&sa), &addrlen));
     if (clientFd == -1) {
-        throwSocketException(env, convertError(errno));
+        jniThrowSocketException(env, errno);
         return;
     }
 
@@ -1967,7 +1958,7 @@
 
     int rc = send(handle, &value, 1, MSG_OOB);
     if (rc == -1) {
-        throwSocketException(env, convertError(errno));
+        jniThrowSocketException(env, errno);
     }
 }
 
@@ -1987,7 +1978,7 @@
 
     int ret = doConnect(fd, &sockAddr);
     if (ret < 0) {
-        throwSocketException(env, convertError(errno));
+        jniThrowSocketException(env, errno);
     }
 }
 
@@ -2006,7 +1997,7 @@
 
     int result = doConnect(fd, &sockAddr);
     if (result < 0) {
-        throwSocketException(env, convertError(errno));
+        jniThrowSocketException(env, errno);
     }
 }
 
@@ -2036,7 +2027,7 @@
     ssize_t length = TEMP_FAILURE_RETRY(recvfrom(fd, NULL, 0, MSG_PEEK,
             reinterpret_cast<sockaddr*>(&sockAddr), &sockAddrLen));
     if (length == -1) {
-        throwSocketException(env, convertError(errno));
+        jniThrowSocketException(env, errno);
         return 0;
     }
 
@@ -2074,7 +2065,7 @@
     ssize_t actualLength = TEMP_FAILURE_RETRY(recvfrom(fd, buf, length, mode,
             reinterpret_cast<sockaddr*>(&sockAddr), &sockAddrLen));
     if (actualLength == -1) {
-        throwSocketException(env, convertError(errno));
+        jniThrowSocketException(env, errno);
         return 0;
     }
 
@@ -2199,7 +2190,7 @@
         if (errno == ECONNRESET || errno == ECONNREFUSED) {
             return 0;
         } else {
-            throwSocketException(env, convertError(errno));
+            jniThrowSocketException(env, errno);
         }
     }
     return bytesSent;
@@ -2237,7 +2228,7 @@
         if (errno == ECONNRESET || errno == ECONNREFUSED) {
             return 0;
         } else {
-            throwSocketException(env, convertError(errno));
+            jniThrowSocketException(env, errno);
         }
     }
     return bytesSent;
@@ -2276,7 +2267,7 @@
     }
     int rc = shutdown(fd, how);
     if (rc == -1) {
-        throwSocketException(env, convertError(errno));
+        jniThrowSocketException(env, errno);
     }
 }
 
@@ -2323,7 +2314,7 @@
                 SOCKET_NOFLAGS,
                 reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr)));
         if (bytesSent == -1) {
-            throwSocketException(env, convertError(errno));
+            jniThrowSocketException(env, errno);
             free(message);
             return 0;
         }
@@ -2430,8 +2421,14 @@
     sockaddr_storage addr;
     socklen_t addrLen = sizeof(addr);
     memset(&addr, 0, addrLen);
-    // Spec says ignore all errors
     int rc = getsockname(fd, (sockaddr*) &addr, &addrLen);
+    if (rc == -1) {
+        // TODO: the public API doesn't allow failure, so this whole method
+        // represents a broken design. In practice, though, getsockname can't
+        // fail unless we give it invalid arguments.
+        LOGE("getsockname failed: %s (errno=%i)", strerror(errno), errno);
+        return NULL;
+    }
     return socketAddressToInetAddress(env, &addr);
 }
 
@@ -2446,9 +2443,16 @@
 
     sockaddr_storage addr;
     socklen_t addrLen = sizeof(addr);
-    // The java spec does not indicate any exceptions on this call
+    memset(&addr, 0, addrLen);
     int rc = getsockname(fd, (sockaddr*) &addr, &addrLen);
-    return (rc == -1) ? 0 : getSocketAddressPort(&addr);
+    if (rc == -1) {
+        // TODO: the public API doesn't allow failure, so this whole method
+        // represents a broken design. In practice, though, getsockname can't
+        // fail unless we give it invalid arguments.
+        LOGE("getsockname failed: %s (errno=%i)", strerror(errno), errno);
+        return 0;
+    }
+    return getSocketAddressPort(&addr);
 }
 
 static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz,
@@ -2457,8 +2461,6 @@
 
     int intValue = 0;
     socklen_t intSize = sizeof(int);
-    unsigned char byteValue = 0;
-    socklen_t byteSize = sizeof(unsigned char);
     int result;
     struct sockaddr_storage sockVal;
     socklen_t sockSize = sizeof(sockVal);
@@ -2474,7 +2476,7 @@
             socklen_t size = sizeof(struct linger);
             result = getsockopt(handle, SOL_SOCKET, SO_LINGER, &lingr, &size);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return NULL;
             }
             if (!lingr.l_onoff) {
@@ -2490,7 +2492,7 @@
             }
             result = getsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &intValue, &intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return NULL;
             }
             return newJavaLangBoolean(env, intValue);
@@ -2504,22 +2506,26 @@
                                           IPV6_MULTICAST_HOPS, &intValue,
                                           &intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return NULL;
             }
             return newJavaLangByte(env, (jbyte)(intValue & 0xFF));
         }
-        case JAVASOCKOPT_MCAST_INTERFACE: {
+        case JAVASOCKOPT_IP_MULTICAST_IF: {
             if ((anOption >> 16) & BROKEN_MULTICAST_IF) {
                 return NULL;
             }
-            result = getsockopt(handle, SOL_IP, IP_MULTICAST_IF, &sockVal, &sockSize);
-            if (0 != result) {
-                throwSocketException(env, convertError(errno));
+            result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF,
+                &sockVal, &sockSize);
+            if (result == -1) {
+                jniThrowSocketException(env, errno);
                 return NULL;
             }
-            // This option is IPv4-only.
-            sockVal.ss_family = AF_INET;
+            if (sockVal.ss_family != AF_INET) {
+                // Java expects an AF_INET INADDR_ANY, but Linux just returns AF_UNSPEC.
+                jbyteArray inAddrAny = env->NewByteArray(4); // { 0, 0, 0, 0 }
+                return byteArrayToInetAddress(env, inAddrAny);
+            }
             return socketAddressToInetAddress(env, &sockVal);
         }
         case JAVASOCKOPT_IP_MULTICAST_IF2: {
@@ -2527,38 +2533,37 @@
                 return NULL;
             }
             struct ip_mreqn multicastRequest;
-            int interfaceIndex;
+            int interfaceIndex = 0;
             socklen_t optionLength;
             int addressFamily = getSocketAddressFamily(handle);
             switch (addressFamily) {
                 case AF_INET:
                     optionLength = sizeof(multicastRequest);
-                    result = getsockopt(handle, SOL_IP, IP_MULTICAST_IF,
+                    result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF,
                                         &multicastRequest, &optionLength);
                     if (result == 0)
                         interfaceIndex = multicastRequest.imr_ifindex;
                     break;
                 case AF_INET6:
                     optionLength = sizeof(interfaceIndex);
-                    result = getsockopt(handle, SOL_IPV6, IPV6_MULTICAST_IF,
+                    result = getsockopt(handle, IPPROTO_IPV6, IPV6_MULTICAST_IF,
                                         &interfaceIndex, &optionLength);
                     break;
                 default:
-                    throwSocketException(env, SOCKERR_BADAF);
+                    jniThrowSocketException(env, EAFNOSUPPORT);
                     return NULL;
             }
 
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return NULL;
             }
-
             return newJavaLangInteger(env, interfaceIndex);
         }
         case JAVASOCKOPT_SO_SNDBUF: {
             result = getsockopt(handle, SOL_SOCKET, SO_SNDBUF, &intValue, &intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return NULL;
             }
             return newJavaLangInteger(env, intValue);
@@ -2566,7 +2571,7 @@
         case JAVASOCKOPT_SO_RCVBUF: {
             result = getsockopt(handle, SOL_SOCKET, SO_RCVBUF, &intValue, &intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return NULL;
             }
             return newJavaLangInteger(env, intValue);
@@ -2574,7 +2579,7 @@
         case JAVASOCKOPT_SO_BROADCAST: {
             result = getsockopt(handle, SOL_SOCKET, SO_BROADCAST, &intValue, &intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return NULL;
             }
             return newJavaLangBoolean(env, intValue);
@@ -2582,7 +2587,7 @@
         case JAVASOCKOPT_SO_REUSEADDR: {
             result = getsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intValue, &intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return NULL;
             }
             return newJavaLangBoolean(env, intValue);
@@ -2590,7 +2595,7 @@
         case JAVASOCKOPT_SO_KEEPALIVE: {
             result = getsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &intValue, &intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return NULL;
             }
             return newJavaLangBoolean(env, intValue);
@@ -2598,7 +2603,7 @@
         case JAVASOCKOPT_SO_OOBINLINE: {
             result = getsockopt(handle, SOL_SOCKET, SO_OOBINLINE, &intValue, &intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return NULL;
             }
             return newJavaLangBoolean(env, intValue);
@@ -2609,7 +2614,7 @@
                                           IPV6_MULTICAST_LOOP, &intValue,
                                           &intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return NULL;
             }
             return newJavaLangBoolean(env, intValue);
@@ -2618,7 +2623,7 @@
             result = getOrSetSocketOption(SOCKOPT_GET, handle, IP_TOS,
                                           IPV6_TCLASS, &intValue, &intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return NULL;
             }
             return newJavaLangInteger(env, intValue);
@@ -2628,13 +2633,13 @@
             socklen_t size = sizeof(timeout);
             result = getsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, &timeout, &size);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return NULL;
             }
             return newJavaLangInteger(env, toMs(timeout));
         }
         default: {
-            throwSocketException(env, SOCKERR_OPTUNSUPP);
+            jniThrowSocketException(env, ENOPROTOOPT);
             return NULL;
         }
     }
@@ -2648,8 +2653,6 @@
     int result;
     int intVal;
     socklen_t intSize = sizeof(int);
-    unsigned char byteVal;
-    socklen_t byteSize = sizeof(unsigned char);
     struct sockaddr_storage sockVal;
     int sockSize = sizeof(sockVal);
 
@@ -2658,7 +2661,8 @@
     } else if (env->IsInstanceOf(optVal, gCachedFields.boolean_class)) {
         intVal = (int) env->GetBooleanField(optVal, gCachedFields.boolean_class_value);
     } else if (env->IsInstanceOf(optVal, gCachedFields.byte_class)) {
-        byteVal = (int) env->GetByteField(optVal, gCachedFields.byte_class_value);
+        // TTL uses a byte in Java, but the kernel still wants an int.
+        intVal = (int) env->GetByteField(optVal, gCachedFields.byte_class_value);
     } else if (env->IsInstanceOf(optVal, gCachedFields.iaddr_class)) {
         if (!inetAddressToSocketAddress(env, optVal, 0, &sockVal)) {
             return;
@@ -2666,7 +2670,7 @@
     } else if (env->IsInstanceOf(optVal, gCachedFields.genericipmreq_class)) {
         // we'll use optVal directly
     } else {
-        throwSocketException(env, SOCKERR_OPTUNSUPP);
+        jniThrowSocketException(env, ENOPROTOOPT);
         return;
     }
 
@@ -2683,7 +2687,7 @@
             result = setsockopt(handle, SOL_SOCKET, SO_LINGER, &lingr,
                     sizeof(struct linger));
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
             break;
@@ -2695,7 +2699,7 @@
             }
             result = setsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &intVal, intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
             break;
@@ -2705,13 +2709,11 @@
             if ((anOption >> 16) & BROKEN_MULTICAST_TTL) {
                 return;
             }
-            // Java uses a byte to store the TTL, but the kernel uses an int.
-            intVal = byteVal;
             result = getOrSetSocketOption(SOCKOPT_SET, handle, IP_MULTICAST_TTL,
                                           IPV6_MULTICAST_HOPS, &intVal,
                                           &intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
             break;
@@ -2729,24 +2731,24 @@
             break;
         }
 
-        case JAVASOCKOPT_MCAST_INTERFACE: {
+        case JAVASOCKOPT_IP_MULTICAST_IF: {
             if ((anOption >> 16) & BROKEN_MULTICAST_IF) {
                 return;
             }
             // This call is IPv4 only. The socket may be IPv6, but the address
             // that identifies the interface to join must be an IPv4 address.
             if (sockVal.ss_family != AF_INET) {
-                throwSocketException(env, SOCKERR_BADAF);
+                jniThrowSocketException(env, EAFNOSUPPORT);
                 return;
             }
             struct ip_mreqn mcast_req;
             memset(&mcast_req, 0, sizeof(mcast_req));
             struct sockaddr_in *sin = (struct sockaddr_in *) &sockVal;
             mcast_req.imr_address = sin->sin_addr;
-            result = setsockopt(handle, SOL_IP, IP_MULTICAST_IF,
+            result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF,
                                 &mcast_req, sizeof(mcast_req));
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
             break;
@@ -2775,14 +2777,14 @@
                     optionLength = sizeof(interfaceIndex);
                     break;
                 default:
-                    throwSocketException(env, SOCKERR_BADAF);
+                    jniThrowSocketException(env, EAFNOSUPPORT);
                     return;
             }
             result = getOrSetSocketOption(SOCKOPT_SET, handle,
                     IP_MULTICAST_IF, IPV6_MULTICAST_IF, optionValue,
                     &optionLength);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
             break;
@@ -2791,7 +2793,7 @@
         case JAVASOCKOPT_SO_SNDBUF: {
             result = setsockopt(handle, SOL_SOCKET, SO_SNDBUF, &intVal, intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
             break;
@@ -2800,7 +2802,7 @@
         case JAVASOCKOPT_SO_RCVBUF: {
             result = setsockopt(handle, SOL_SOCKET, SO_RCVBUF, &intVal, intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
             break;
@@ -2809,7 +2811,7 @@
         case JAVASOCKOPT_SO_BROADCAST: {
             result = setsockopt(handle, SOL_SOCKET, SO_BROADCAST, &intVal, intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
             break;
@@ -2818,7 +2820,7 @@
         case JAVASOCKOPT_SO_REUSEADDR: {
             result = setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intVal, intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
             break;
@@ -2826,7 +2828,7 @@
         case JAVASOCKOPT_SO_KEEPALIVE: {
             result = setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &intVal, intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
             break;
@@ -2835,7 +2837,7 @@
         case JAVASOCKOPT_SO_OOBINLINE: {
             result = setsockopt(handle, SOL_SOCKET, SO_OOBINLINE, &intVal, intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
             break;
@@ -2847,7 +2849,7 @@
                                           IPV6_MULTICAST_LOOP, &intVal,
                                           &intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
             break;
@@ -2857,7 +2859,7 @@
             result = getOrSetSocketOption(SOCKOPT_SET, handle, IP_TOS,
                                           IPV6_TCLASS, &intVal, &intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
             break;
@@ -2867,7 +2869,7 @@
             // SO_REUSEPORT doesn't need to get set on this System
             result = setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intVal, intSize);
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
             break;
@@ -2878,14 +2880,14 @@
             result = setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, &timeout,
                     sizeof(struct timeval));
             if (0 != result) {
-                throwSocketException(env, convertError(errno));
+                jniThrowSocketException(env, errno);
                 return;
             }
             break;
         }
 
         default: {
-            throwSocketException(env, SOCKERR_OPTUNSUPP);
+            jniThrowSocketException(env, ENOPROTOOPT);
         }
     }
 }
diff --git a/luni/src/test/java/java/net/AllTests.java b/luni/src/test/java/java/net/AllTests.java
new file mode 100644
index 0000000..6d26f0d
--- /dev/null
+++ b/luni/src/test/java/java/net/AllTests.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2009 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 java.net;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AllTests {
+    public static final Test suite() {
+        TestSuite suite = tests.TestSuiteFactory.createTestSuite();
+        suite.addTestSuite(java.net.SocketTest.class);
+        return suite;
+    }
+}
diff --git a/luni/src/test/java/java/net/SocketTest.java b/luni/src/test/java/java/net/SocketTest.java
new file mode 100644
index 0000000..b0e278f
--- /dev/null
+++ b/luni/src/test/java/java/net/SocketTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009 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 java.net;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class SocketTest extends junit.framework.TestCase {
+    /**
+     * Our getLocalAddress and getLocalPort currently use getsockname(3).
+     * This means they give incorrect results on closed sockets (as well
+     * as requiring an unnecessary call into native code).
+     */
+    public void test_getLocalAddress_after_close() throws Exception {
+        Socket s = new Socket();
+        try {
+            // Bind to an ephemeral local port.
+            s.bind(new InetSocketAddress("localhost", 0));
+            assertTrue(s.getLocalAddress().isLoopbackAddress());
+            // What local port did we get?
+            int localPort = s.getLocalPort();
+            assertTrue(localPort > 0);
+            // Now close the socket...
+            s.close();
+            // The RI returns the ANY address but the original local port after close.
+            assertTrue(s.getLocalAddress().isAnyLocalAddress());
+            assertEquals(localPort, s.getLocalPort());
+        } finally {
+            s.close();
+        }
+    }
+}
diff --git a/luni/src/test/java/tests/AllTests.java b/luni/src/test/java/tests/AllTests.java
index 7d12bcc..d2799ec 100644
--- a/luni/src/test/java/tests/AllTests.java
+++ b/luni/src/test/java/tests/AllTests.java
@@ -59,6 +59,7 @@
         suite.addTest(com.ibm.icu4jni.util.AllTests.suite());
         suite.addTest(java.lang.AllTests.suite());
         suite.addTest(java.lang.reflect.AllTests.suite());
+        suite.addTest(java.net.AllTests.suite());
         suite.addTest(org.apache.harmony.luni.platform.AllTests.suite());
         suite.addTest(tests.api.org.apache.harmony.kernel.dalvik.AllTests.suite());
         
diff --git a/luni/src/test/java/tests/api/java/io/BufferedReaderTest.java b/luni/src/test/java/tests/api/java/io/BufferedReaderTest.java
index b1a5755..f88b3f5 100644
--- a/luni/src/test/java/tests/api/java/io/BufferedReaderTest.java
+++ b/luni/src/test/java/tests/api/java/io/BufferedReaderTest.java
@@ -24,12 +24,14 @@
 import java.io.InputStreamReader;
 import java.io.PipedReader;
 import java.io.Reader;
+import java.io.StringReader;
 
 import tests.support.Support_ASimpleReader;
 import tests.support.Support_StringReader;
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
 import dalvik.annotation.TestTargetNew;
+import tests.support.ThrowingReader;
 
 @TestTargetClass(BufferedReader.class) 
 public class BufferedReaderTest extends junit.framework.TestCase {
@@ -502,6 +504,33 @@
         ssr.throwExceptionOnNextUse = false;
     }
 
+    public void testReadZeroLengthArray() throws IOException {
+        br = new BufferedReader(new Support_StringReader("ABCDEF"));
+        br.read();
+        br.read();
+        assertEquals(0, br.read(new char[6], 3, 0));
+    }
+
+    public void testSourceThrowsWithMark() throws IOException {
+        br = new BufferedReader(new ThrowingReader(
+                new StringReader("ABCDEFGHI"), 4));
+
+        br.read();
+        br.read();
+        br.mark(10);
+        br.read();
+        br.read();
+
+        try {
+            br.read();
+            fail();
+        } catch (IOException fromThrowingReader) {
+        }
+
+        assertEquals('E', br.read());
+        assertEquals('F', br.read());
+    }
+
     /**
      * Tears down the fixture, for example, close a network connection. This
      * method is called after a test is executed.
diff --git a/math/src/main/java/java/math/BigInt.java b/math/src/main/java/java/math/BigInt.java
index 581c22f..3ba1da2 100644
--- a/math/src/main/java/java/math/BigInt.java
+++ b/math/src/main/java/java/math/BigInt.java
@@ -234,9 +234,9 @@
         else if (val < 0) NativeBN.BN_set_negative(this.bignum, 1);
     }
 
-
-    public boolean twosCompFitsIntoBytes(int byteCnt) {
-        return NativeBN.twosCompFitsIntoBytes(this.bignum, byteCnt);
+    public boolean twosCompFitsIntoBytes(int desiredByteCount) {
+        int actualByteCount = (NativeBN.bitLength(this.bignum) + 7) / 8;
+        return actualByteCount <= desiredByteCount;
     }
 
     public int bitLength() {
diff --git a/openssl/src/main/java/org/openssl/NativeBN.java b/openssl/src/main/java/org/openssl/NativeBN.java
index 3597e3c..fd796f8 100644
--- a/openssl/src/main/java/org/openssl/NativeBN.java
+++ b/openssl/src/main/java/org/openssl/NativeBN.java
@@ -86,9 +86,6 @@
     public static native void BN_set_negative(int b, int n);
     // void BN_set_negative(BIGNUM *b, int n);
 
-
-    public static native boolean twosCompFitsIntoBytes(int a, int byteCnt);
-
     public static native int bitLength(int a);
 
     public static native boolean BN_is_bit_set(int a, int n);
diff --git a/openssl/src/main/native/BNInterface.c b/openssl/src/main/native/BNInterface.c
index 1a3eb16..79f0680 100644
--- a/openssl/src/main/native/BNInterface.c
+++ b/openssl/src/main/native/BNInterface.c
@@ -469,60 +469,6 @@
     BN_set_negative(b, n);
 }
 
-
-/**
- * public static native int twosCompFitsIntoBytes(int, int)
- */
-static jboolean NativeBN_twosCompFitsIntoBytes(JNIEnv* env, jclass cls, BIGNUM* a, int byteCnt) {
-// byteCnt IN {1, 2, 4, 8, 12, 16, ... (k * 4)}
-// We rely on: (BN_BITS2 == 32), i.e. BN_ULONG is unsigned int and has 4 bytes:
-//
-// LOGD("NativeBN_twosCompFitsIntoBytes");
-    if (!oneValidHandle(env, a)) return FALSE;
-    bn_check_top(a);
-    int intLen = a->top;
-    BN_ULONG* d = a->d;
-    BN_ULONG msd; // most significant digit
-    switch (byteCnt) {
-    case 1:
-        if (intLen > 1) return FALSE;
-        else if (intLen == 0) return TRUE;
-        msd = d[0];
-        if (a->neg) msd--;
-        return ((msd & 0XFFFFFF80) == 0);
-    case 2:
-        if (intLen > 1) return FALSE;
-        else if (intLen == 0) return TRUE;
-        msd = d[0];
-        if (a->neg) msd--;
-        return ((msd & 0XFFFF8000) == 0);
-    case 4:
-        if (intLen > 1) return FALSE;
-        else if (intLen == 0) return TRUE;
-        msd = d[0];
-        if (a->neg) msd--;
-        return ((msd & 0X80000000) == 0);
-    case 8:
-        if (intLen > 2) return FALSE;
-        else if (intLen == 0) return TRUE;
-        msd = d[1];
-        if ((a->neg) && (d[0]) == 0) msd--;
-        return ((msd & 0X80000000) == 0);
-    default:
-        if (intLen > byteCnt / 4) return FALSE;
-        else if (intLen == 0) return TRUE;
-        int i = intLen - 1;
-        msd = d[i];
-        if (a->neg) {
-            // Handle negative values correctly:
-            // i.e. decrement the msd if all other digits are 0:
-            do { i--; } while (!((i < 0) || (d[i] != 0)));
-            if (i < 0) msd--; // Only if all lower significant digits are 0 we decrement the most significant one.
-        }
-        return ((msd & 0X80000000) == 0);
-    }
-}
-
 /**
  * public static native int bitLength(int)
  */
@@ -547,15 +493,6 @@
 }
 
 /**
- * public static native int BN_num_bits(int)
- */
-// static int NativeBN_BN_num_bits(JNIEnv* env, jclass cls, BIGNUM* a) {
-// LOGD("NativeBN_BN_num_bits");
-//     if (!oneValidHandle(env, a)) return FALSE;
-//     return BN_num_bits(a);
-// }
-
-/**
  * public static native boolean BN_is_bit_set(int, int)
  */
 static jboolean NativeBN_BN_is_bit_set(JNIEnv* env, jclass cls, BIGNUM* a, int n) {
@@ -808,9 +745,7 @@
    { "bn2litEndInts", "(I[I)[I", (void*)NativeBN_bn2litEndInts },
    { "sign", "(I)I", (void*)NativeBN_sign },
    { "BN_set_negative", "(II)V", (void*)NativeBN_BN_set_negative },
-   { "twosCompFitsIntoBytes", "(II)Z", (void*)NativeBN_twosCompFitsIntoBytes },
    { "bitLength", "(I)I", (void*)NativeBN_bitLength },
-//   { "BN_num_bits", "(I)I", (void*)NativeBN_BN_num_bits },
    { "BN_is_bit_set", "(II)Z", (void*)NativeBN_BN_is_bit_set },
    { "modifyBit", "(III)Z", (void*)NativeBN_modifyBit },
    { "BN_lshift", "(III)Z", (void*)NativeBN_BN_lshift },
@@ -832,19 +767,6 @@
    { "BN_is_prime_ex", "(IIII)Z", (void*)NativeBN_BN_is_prime_ex }
 };
 
-/*
- * Peforms the actual registration of the native methods.
- * Also looks up the fields that belong to the class (if
- * any) and stores the field IDs.
- */
 int register_org_openssl_NativeBN(JNIEnv* env) {
-/*
-   jclass clazz;
-
-   clazz = (*env)->FindClass(env, "org/openssl/NativeBN");
-   if (clazz == NULL) {
-       return -1;
-   }
-*/
    return jniRegisterNativeMethods(env, "org/openssl/NativeBN", METHODS, NELEM(METHODS));
 }
diff --git a/support/src/test/java/tests/support/ThrowingReader.java b/support/src/test/java/tests/support/ThrowingReader.java
new file mode 100644
index 0000000..fbefeb1
--- /dev/null
+++ b/support/src/test/java/tests/support/ThrowingReader.java
@@ -0,0 +1,64 @@
+/*
+ *  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 tests.support;
+
+import java.io.FilterReader;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * A reader that always throws after a predetermined number of bytes have been
+ * read.
+ */
+public class ThrowingReader extends FilterReader {
+
+    private int total = 0;
+    private int throwAt;
+
+    public ThrowingReader(Reader in, int throwAt) {
+        super(in);
+        this.throwAt = throwAt;
+    }
+
+    @Override public int read() throws IOException {
+        explodeIfNecessary();
+        int result = super.read();
+        total++;
+        return result;
+    }
+
+    @Override public int read(char[] buf, int offset, int count)
+            throws IOException {
+        explodeIfNecessary();
+
+        if (total < throwAt) {
+            count = Math.min(count, (throwAt - total));
+        }
+
+        int returned = super.read(buf, offset, count);
+        total += returned;
+        return returned;
+    }
+
+    private void explodeIfNecessary() throws IOException {
+        if (total == throwAt) {
+            throwAt = Integer.MAX_VALUE;
+            throw new IOException();
+        }
+    }
+}
diff --git a/tools/integrate/Module.java b/tools/integrate/Module.java
index 63d35a2..02cdb6a 100644
--- a/tools/integrate/Module.java
+++ b/tools/integrate/Module.java
@@ -48,6 +48,8 @@
                         "crypto/src/test/java")
                 .build());
 
+        valuesMutable.put("logging", new Module.Builder(svnRoot, "logging").build());
+
         valuesMutable.put("regex", new Module.Builder(svnRoot, "regex").build());
 
         valuesMutable.put("security", new Module.Builder(svnRoot, "security")