Throw meaningful IOException instances from native code.

The Java side of OSFileSystem was throwing IOExceptions with no detail
message. If we throw from the native side instead, we can supply
meaningful explanations. This turned up a couple of bugs:

* read, readDirect, readv, writev, and ttyRead would only throw IOException
if they returned < -1, which is impossible. (writev was probably a copy & paste
from readv, and the reads were probably confused by the impedence mismatch
between Unix's use of 0 to mean end of file and -1 to mean error, and Java's
use of -1 for end of file.)

* inconsistent checking for null byte[]s passed in.

* read and write would retry on EINTR, but readDirect and writeDirect wouldn't.

* we'd silently truncate seek/lock/truncate offsets that didn't fit in 32 bits;
we now throw an IOException instead.

It also means a few native functions become "void" because errors are now
reported by throwing exceptions, and the Java functions that used to call them
are no longer needed.

Also change ProcessManager to use jniThrowIOException, remove the unused
throwIOExceptionStr from OSNetworkSystem.cpp, and remove the KnownFailure from
FileTest's test_delete, now we have a fixed version of yaffs that won't
rmdir(2) non-empty directories.

Bug: 1542253
diff --git a/libcore/luni-kernel/src/main/native/java_lang_ProcessManager.c b/libcore/luni-kernel/src/main/native/java_lang_ProcessManager.c
index eaefc9f..46e78f5 100644
--- a/libcore/luni-kernel/src/main/native/java_lang_ProcessManager.c
+++ b/libcore/luni-kernel/src/main/native/java_lang_ProcessManager.c
@@ -64,8 +64,7 @@
         jclass clazz, jobject javaDescriptor) {
     int fd = (*env)->GetIntField(env, javaDescriptor, descriptorField);
     if (closeNow(fd) == -1) {
-        jclass ioException = (*env)->FindClass(env, "java/io/IOException");
-        (*env)->ThrowNew(env, ioException, strerror(errno));
+        jniThrowIOException(env, errno);
     }
 }
 
diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java
index 08bdac6..0338a52 100644
--- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java
+++ b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java
@@ -69,7 +69,7 @@
      * Note that this value for Windows differs from the one for the
      * page size (64K and 4K respectively).
      */
-    public native int getAllocGranularity() throws IOException;
+    public native int getAllocGranularity();
 
     public boolean lock(int fileDescriptor, long start, long length, int type,
             boolean waitFlag) throws IOException {
@@ -79,160 +79,71 @@
         return result != -1;
     }
 
-    private native int unlockImpl(int fileDescriptor, long start, long length);
+    // BEGIN android-changed
+    private native void unlockImpl(int fileDescriptor, long start, long length) throws IOException;
 
     public void unlock(int fileDescriptor, long start, long length)
             throws IOException {
         // Validate arguments
         validateLockArgs(IFileSystem.SHARED_LOCK_TYPE, start, length);
-        int result = unlockImpl(fileDescriptor, start, length);
-        if (result == -1) {
-            throw new IOException();
-        }
+        unlockImpl(fileDescriptor, start, length);
     }
 
-    private native int fflushImpl(int fd, boolean metadata);
-
-    public void fflush(int fileDescriptor, boolean metadata)
-            throws IOException {
-        int result = fflushImpl(fileDescriptor, metadata);
-        if (result == -1) {
-            throw new IOException();
-        }
-    }
+    public native void fflush(int fileDescriptor, boolean metadata) throws IOException;
 
     /*
      * File position seeking.
      */
-
-    private native long seekImpl(int fd, long offset, int whence);
-
-    public long seek(int fileDescriptor, long offset, int whence)
-            throws IOException {
-        long pos = seekImpl(fileDescriptor, offset, whence);
-        if (pos == -1) {
-            throw new IOException();
-        }
-        return pos;
-    }
+    public native long seek(int fd, long offset, int whence) throws IOException;
 
     /*
      * Direct read/write APIs work on addresses.
      */
-    private native long readDirectImpl(int fileDescriptor, int address,
-            int offset, int length);
+    public native long readDirect(int fileDescriptor, int address, int offset, int length);
 
-    public long readDirect(int fileDescriptor, int address, int offset,
-            int length) throws IOException {
-        long bytesRead = readDirectImpl(fileDescriptor, address, offset, length);
-        if (bytesRead < -1) {
-            throw new IOException();
-        }
-        return bytesRead;
-    }
-
-    private native long writeDirectImpl(int fileDescriptor, int address,
-            int offset, int length);
-
-    public long writeDirect(int fileDescriptor, int address, int offset,
-            int length) throws IOException {
-        long bytesWritten = writeDirectImpl(fileDescriptor, address, offset,
-                length);
-        if (bytesWritten < 0) {
-            throw new IOException();
-        }
-        return bytesWritten;
-    }
+    public native long writeDirect(int fileDescriptor, int address, int offset, int length)
+            throws IOException;
 
     /*
      * Indirect read/writes work on byte[]'s
      */
     private native long readImpl(int fileDescriptor, byte[] bytes, int offset,
-            int length);
+            int length) throws IOException;
 
     public long read(int fileDescriptor, byte[] bytes, int offset, int length)
             throws IOException {
         if (bytes == null) {
             throw new NullPointerException();
         }
-        long bytesRead = readImpl(fileDescriptor, bytes, offset, length);
-        if (bytesRead < -1) {
-            /*
-             * TODO: bytesRead is never less than -1 so this code
-             * does nothing?
-             * The native code throws an exception in only one case
-             * so perhaps this should be 'bytesRead < 0' to handle
-             * any other cases.  But the other cases have been
-             * ignored until now so fixing this could break things
-             */
-            throw new IOException();
-        }
-        return bytesRead;
+        return readImpl(fileDescriptor, bytes, offset, length);
     }
 
     private native long writeImpl(int fileDescriptor, byte[] bytes,
-            int offset, int length);
+            int offset, int length) throws IOException;
 
     public long write(int fileDescriptor, byte[] bytes, int offset, int length)
             throws IOException {
-        long bytesWritten = writeImpl(fileDescriptor, bytes, offset, length);
-        if (bytesWritten < 0) {
-            throw new IOException();
+        if (bytes == null) {
+            throw new NullPointerException();
         }
-        return bytesWritten;
+        return writeImpl(fileDescriptor, bytes, offset, length);
     }
+    // END android-changed
 
     /*
      * Scatter/gather calls.
      */
-    public long readv(int fileDescriptor, int[] addresses, int[] offsets,
-            int[] lengths, int size) throws IOException {
-        long bytesRead = readvImpl(fileDescriptor, addresses, offsets, lengths,
-                size);
-        if (bytesRead < -1) {
-            throw new IOException();
-        }
-        return bytesRead;
-    }
+    public native long readv(int fileDescriptor, int[] addresses,
+            int[] offsets, int[] lengths, int size) throws IOException;
 
-    private native long readvImpl(int fileDescriptor, int[] addresses,
-            int[] offsets, int[] lengths, int size);
+    public native long writev(int fileDescriptor, int[] addresses, int[] offsets,
+            int[] lengths, int size) throws IOException;
 
-    public long writev(int fileDescriptor, int[] addresses, int[] offsets,
-            int[] lengths, int size) throws IOException {
-        long bytesWritten = writevImpl(fileDescriptor, addresses, offsets,
-                lengths, size);
-        if (bytesWritten < 0) {
-            throw new IOException();
-        }
-        return bytesWritten;
-    }
+    // BEGIN android-changed
+    public native void close(int fileDescriptor) throws IOException;
 
-    private native long writevImpl(int fileDescriptor, int[] addresses,
-            int[] offsets, int[] lengths, int size);
-
-    private native int closeImpl(int fileDescriptor);
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see org.apache.harmony.luni.platform.IFileSystem#close(long)
-     */
-    public void close(int fileDescriptor) throws IOException {
-        int rc = closeImpl(fileDescriptor);
-        if (rc == -1) {
-            throw new IOException();
-        }
-    }
-
-    public void truncate(int fileDescriptor, long size) throws IOException {
-        int rc = truncateImpl(fileDescriptor, size);
-        if (rc < 0) {
-            throw new IOException();
-        }
-    }
-
-    private native int truncateImpl(int fileDescriptor, long size);
+    public native void truncate(int fileDescriptor, long size) throws IOException;
+    // END android-changed
 
     public int open(byte[] fileName, int mode) throws FileNotFoundException {
         if (fileName == null) {
@@ -254,16 +165,10 @@
 
     private native int openImpl(byte[] fileName, int mode);
 
-    public long transfer(int fileHandler, FileDescriptor socketDescriptor,
-            long offset, long count) throws IOException {
-        long result = transferImpl(fileHandler, socketDescriptor, offset, count);
-        if (result < 0)
-                throw new IOException();
-        return result;
-    }
-
-    private native long transferImpl(int fileHandler,
-            FileDescriptor socketDescriptor, long offset, long count);
+    // BEGIN android-changed
+    public native long transfer(int fd, FileDescriptor sd, long offset, long count)
+            throws IOException;
+    // END android-changed
 
     // BEGIN android-deleted
     // public long ttyAvailable() throws IOException {
@@ -277,17 +182,16 @@
     // private native long ttyAvailableImpl();
     // END android-deleted
 
+    // BEGIN android-changed
     public long ttyRead(byte[] bytes, int offset, int length) throws IOException {
-        long nChar = ttyReadImpl(bytes, offset, length);
-        // BEGIN android-changed
-        if (nChar < -1) {
-            throw new IOException();
+        if (bytes == null) {
+            throw new NullPointerException();
         }
-        // END android-changed
-        return nChar;
+        return ttyReadImpl(bytes, offset, length);
     }
 
-    private native long ttyReadImpl(byte[] bytes, int offset, int length);
+    private native long ttyReadImpl(byte[] bytes, int offset, int length) throws IOException;
+    // END android-changed
 
     // BEGIN android-added
     public native int ioctlAvailable(int fileDescriptor) throws IOException;
diff --git a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp
index 5614214..d32767c 100644
--- a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp
+++ b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp
@@ -14,179 +14,193 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
+
+// BEGIN android-note
+// This file corresponds to harmony's OSFileSystem.c and OSFileSystemLinux32.c.
+// It has been greatly simplified by the assumption that the underlying
+// platform is always Linux.
+// END android-note
+
 /*
  * Common natives supporting the file system interface.
  */
 
 #define HyMaxPath 1024
-#define HyOpenRead    1       /* Values for HyFileOpen */
+
+/* Values for HyFileOpen */
+#define HyOpenRead    1
 #define HyOpenWrite   2
 #define HyOpenCreate  4
 #define HyOpenTruncate  8
 #define HyOpenAppend  16
 #define HyOpenText    32
-
 /* Use this flag with HyOpenCreate, if this flag is specified then
- * trying to create an existing file will fail 
+ * trying to create an existing file will fail
  */
 #define HyOpenCreateNew 64
-#define HyOpenSync		128
+#define HyOpenSync      128
 #define SHARED_LOCK_TYPE 1L
 
 #include "JNIHelp.h"
 #include "AndroidSystemNatives.h"
-#include <string.h>
-#include <stdio.h>
+#include <assert.h>
 #include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
 #include <stdlib.h>
+#include <sys/ioctl.h>
 #include <sys/sendfile.h>
 #include <sys/uio.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
 
-typedef struct socket_struct {
-    int sock;
-    unsigned short family;
-} socket_struct;
+// An equivalent of the glibc macro of the same name.
+// We want to hide EINTR from Java by simply retrying directly in
+// the native code. We care about all other errors, though.
+#define EINTR_RETRY(exp) ({                   \
+    typeof (exp) _rc;                         \
+    do {                                      \
+        _rc = (exp);                          \
+    } while (_rc == -1 && errno == EINTR);    \
+    _rc; })
 
 static void convertToPlatform(char *path) {
     char *pathIndex;
 
     pathIndex = path;
     while (*pathIndex != '\0') {
-        if(*pathIndex == '\\') {
+        if (*pathIndex == '\\') {
             *pathIndex = '/';
         }
         pathIndex++;
     }
 }
 
-static int
-EsTranslateOpenFlags(int flags) {
+static int EsTranslateOpenFlags(int flags) {
     int realFlags = 0;
 
-    if(flags & HyOpenAppend) {
+    if (flags & HyOpenAppend) {
         realFlags |= O_APPEND;
     }
-    if(flags & HyOpenTruncate) {
+    if (flags & HyOpenTruncate) {
         realFlags |= O_TRUNC;
     }
-    if(flags & HyOpenCreate) {
+    if (flags & HyOpenCreate) {
         realFlags |= O_CREAT;
     }
-    if(flags & HyOpenCreateNew) {
+    if (flags & HyOpenCreateNew) {
         realFlags |= O_EXCL | O_CREAT;
     }
 #ifdef O_SYNC
-	if(flags & HyOpenSync) {
-		realFlags |= O_SYNC;
-	}
-#endif    
-    if(flags & HyOpenRead) {
-        if(flags & HyOpenWrite) {
+    if (flags & HyOpenSync) {
+        realFlags |= O_SYNC;
+    }
+#endif
+    if (flags & HyOpenRead) {
+        if (flags & HyOpenWrite) {
             return (O_RDWR | realFlags);
         }
         return (O_RDONLY | realFlags);
     }
-    if(flags & HyOpenWrite) {
+    if (flags & HyOpenWrite) {
         return (O_WRONLY | realFlags);
     }
     return -1;
 }
 
-/**
- * Lock the file identified by the given handle.
- * The range and lock type are given.
- */
-static jint harmony_io_lockImpl(JNIEnv * env, jobject thiz, jint handle, 
-        jlong start, jlong length, jint typeFlag, jboolean waitFlag) {
+// Checks whether we can safely treat the given jlong as an off_t without
+// accidental loss of precision.
+// TODO: this is bogus; we should use _FILE_OFFSET_BITS=64.
+static bool offsetTooLarge(JNIEnv* env, jlong longOffset) {
+    if (sizeof(off_t) >= sizeof(jlong)) {
+        // We're only concerned about the possibility that off_t is
+        // smaller than jlong. off_t is signed, so we don't need to
+        // worry about signed/unsigned.
+        return false;
+    }
 
-    int rc;
-    int waitMode = (waitFlag) ? F_SETLKW : F_SETLK;
+    // TODO: use std::numeric_limits<off_t>::max() and min() when we have them.
+    assert(sizeof(off_t) == sizeof(int));
+    static const off_t off_t_max = INT_MAX;
+    static const off_t off_t_min = INT_MIN;
+
+    if (longOffset > off_t_max || longOffset < off_t_min) {
+        // "Value too large for defined data type".
+        jniThrowIOException(env, EOVERFLOW);
+        return true;
+    }
+    return false;
+}
+
+static jlong translateLockLength(jlong length) {
+    // FileChannel.tryLock uses Long.MAX_VALUE to mean "lock the whole
+    // file", where POSIX would use 0. We can support that special case,
+    // even for files whose actual length we can't represent. For other
+    // out of range lengths, though, we want our range checking to fire.
+    return (length == 0x7fffffffffffffffLL) ? 0 : length;
+}
+
+static struct flock flockFromStartAndLength(jlong start, jlong length) {
     struct flock lock;
-
     memset(&lock, 0, sizeof(lock));
 
-    // If start or length overflow the max values we can represent, then max them out.
-    if(start > 0x7fffffffL) {
-        start = 0x7fffffffL;
-    }
-    if(length > 0x7fffffffL) {
-        length = 0x7fffffffL;
-    }
-
     lock.l_whence = SEEK_SET;
     lock.l_start = start;
     lock.l_len = length;
 
-    if((typeFlag & SHARED_LOCK_TYPE) == SHARED_LOCK_TYPE) {
+    return lock;
+}
+
+static jint harmony_io_lockImpl(JNIEnv* env, jobject, jint handle,
+        jlong start, jlong length, jint typeFlag, jboolean waitFlag) {
+
+    length = translateLockLength(length);
+    if (offsetTooLarge(env, start) || offsetTooLarge(env, length)) {
+        return -1;
+    }
+
+    struct flock lock(flockFromStartAndLength(start, length));
+
+    if ((typeFlag & SHARED_LOCK_TYPE) == SHARED_LOCK_TYPE) {
         lock.l_type = F_RDLCK;
     } else {
         lock.l_type = F_WRLCK;
     }
 
-    do {
-        rc = fcntl(handle, waitMode, &lock);
-    } while ((rc < 0) && (errno == EINTR));
-
-    return (rc == -1) ? -1 : 0;
+    int waitMode = (waitFlag) ? F_SETLKW : F_SETLK;
+    return EINTR_RETRY(fcntl(handle, waitMode, &lock));
 }
 
-/**
- * Unlocks the specified region of the file.
- */
-static jint harmony_io_unlockImpl(JNIEnv * env, jobject thiz, jint handle, 
+static void harmony_io_unlockImpl(JNIEnv* env, jobject, jint handle,
         jlong start, jlong length) {
 
-    int rc;
-    struct flock lock;
-
-    memset(&lock, 0, sizeof(lock));
-
-    // If start or length overflow the max values we can represent, then max them out.
-    if(start > 0x7fffffffL) {
-        start = 0x7fffffffL;
-    }
-    if(length > 0x7fffffffL) {
-        length = 0x7fffffffL;
+    length = translateLockLength(length);
+    if (offsetTooLarge(env, start) || offsetTooLarge(env, length)) {
+        return;
     }
 
-    lock.l_whence = SEEK_SET;
-    lock.l_start = start;
-    lock.l_len = length;
+    struct flock lock(flockFromStartAndLength(start, length));
     lock.l_type = F_UNLCK;
 
-    do {
-        rc = fcntl(handle, F_SETLKW, &lock);
-    } while ((rc < 0) && (errno == EINTR));
-
-    return (rc == -1) ? -1 : 0;
+    int rc = EINTR_RETRY(fcntl(handle, F_SETLKW, &lock));
+    if (rc == -1) {
+        jniThrowIOException(env, errno);
+    }
 }
 
 /**
  * Returns the granularity of the starting address for virtual memory allocation.
  * (It's the same as the page size.)
- * Class:     org_apache_harmony_luni_platform_OSFileSystem
- * Method:    getAllocGranularity
- * Signature: ()I
  */
-static jint harmony_io_getAllocGranularity(JNIEnv * env, jobject thiz) {
-    static int allocGranularity = 0;
-    if(allocGranularity == 0) {
-        allocGranularity = getpagesize();
-    }
+static jint harmony_io_getAllocGranularity(JNIEnv* env, jobject) {
+    static int allocGranularity = getpagesize();
     return allocGranularity;
 }
 
-/*
- * Class:     org_apache_harmony_luni_platform_OSFileSystem
- * Method:    readvImpl
- * Signature: (I[J[I[I)J
- */
-static jlong harmony_io_readvImpl(JNIEnv *env, jobject thiz, jint fd, 
+static jlong harmony_io_readv(JNIEnv* env, jobject, jint fd,
         jintArray jBuffers, jintArray jOffsets, jintArray jLengths, jint size) {
     iovec* vectors = new iovec[size];
     if (vectors == NULL) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", "native heap");
         return -1;
     }
     jint *buffers = env->GetIntArrayElements(jBuffers, NULL);
@@ -201,18 +215,17 @@
     env->ReleaseIntArrayElements(jOffsets, offsets, JNI_ABORT);
     env->ReleaseIntArrayElements(jLengths, lengths, JNI_ABORT);
     delete[] vectors;
+    if (result == -1) {
+        jniThrowIOException(env, errno);
+    }
     return result;
 }
 
-/*
- * Class:     org_apache_harmony_luni_platform_OSFileSystem
- * Method:    writevImpl
- * Signature: (I[J[I[I)J
- */
-static jlong harmony_io_writevImpl(JNIEnv *env, jobject thiz, jint fd, 
+static jlong harmony_io_writev(JNIEnv* env, jobject, jint fd,
         jintArray jBuffers, jintArray jOffsets, jintArray jLengths, jint size) {
     iovec* vectors = new iovec[size];
     if (vectors == NULL) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", "native heap");
         return -1;
     }
     jint *buffers = env->GetIntArrayElements(jBuffers, NULL);
@@ -227,82 +240,60 @@
     env->ReleaseIntArrayElements(jOffsets, offsets, JNI_ABORT);
     env->ReleaseIntArrayElements(jLengths, lengths, JNI_ABORT);
     delete[] vectors;
+    if (result == -1) {
+        jniThrowIOException(env, errno);
+    }
     return result;
 }
 
-/*
- * Class:     org_apache_harmony_luni_platform_OSFileSystem
- * Method:    transferImpl
- * Signature: (IJJ)J
- */
-static jlong harmony_io_transferImpl(JNIEnv *env, jobject thiz, jint fd, 
-        jobject sd, jlong offset, jlong count) {
+static jlong harmony_io_transfer(JNIEnv* env, jobject, jint fd, jobject sd,
+        jlong offset, jlong count) {
 
-    int socket;
-    off_t off;
-
-    socket = jniGetFDFromFileDescriptor(env, sd);
-    if(socket == 0 || socket == -1) {
+    int socket = jniGetFDFromFileDescriptor(env, sd);
+    if (socket == -1) {
         return -1;
     }
 
     /* Value of offset is checked in jint scope (checked in java layer)
        The conversion here is to guarantee no value lost when converting offset to off_t
      */
-    off = offset;
+    off_t off = offset;
 
-    return sendfile(socket,(int)fd,(off_t *)&off,(size_t)count);
+    ssize_t rc = sendfile(socket, fd, &off, count);
+    if (rc == -1) {
+        jniThrowIOException(env, errno);
+    }
+    return rc;
 }
 
-/*
- * Class:     org_apache_harmony_io
- * Method:    readDirectImpl
- * Signature: (IJI)J
- */
-static jlong harmony_io_readDirectImpl(JNIEnv * env, jobject thiz, jint fd, 
+static jlong harmony_io_readDirect(JNIEnv* env, jobject, jint fd,
         jint buf, jint offset, jint nbytes) {
-    jint result;
-    if(nbytes == 0) {
-        return (jlong) 0;
+    if (nbytes == 0) {
+        return 0;
     }
 
-    result = read(fd, (void *) ((jint *)(buf+offset)), (int) nbytes);
-    if(result == 0) {
-        return (jlong) -1;
-    } else {
-        return (jlong) result;
+    jbyte* dst = reinterpret_cast<jbyte*>(buf + offset);
+    jlong rc = EINTR_RETRY(read(fd, dst, nbytes));
+    if (rc == 0) {
+        return -1;
     }
+    if (rc == -1) {
+        jniThrowIOException(env, errno);
+    }
+    return rc;
 }
 
-/*
- * Class:     org_apache_harmony_io
- * Method:    writeDirectImpl
- * Signature: (IJI)J
- */
-static jlong harmony_io_writeDirectImpl(JNIEnv * env, jobject thiz, jint fd, 
+static jlong harmony_io_writeDirect(JNIEnv* env, jobject, jint fd,
         jint buf, jint offset, jint nbytes) {
-
-
-    int rc = 0;
-
-    /* write will just do the right thing for HYPORT_TTY_OUT and HYPORT_TTY_ERR */
-    rc = write (fd, (const void *) ((jint *)(buf+offset)), (int) nbytes);
-
-    if(rc == -1) {
-        jniThrowException(env, "java/io/IOException", strerror(errno));
-        return -2;
+    jbyte* src = reinterpret_cast<jbyte*>(buf + offset);
+    jlong rc = EINTR_RETRY(write(fd, src, nbytes));
+    if (rc == -1) {
+        jniThrowIOException(env, errno);
     }
-    return (jlong) rc;
-
+    return rc;
 }
 
-// BEGIN android-changed
-/*
- * Class:     org_apache_harmony_io
- * Method:    readImpl
- * Signature: (I[BII)J
- */
-static jlong harmony_io_readImpl(JNIEnv * env, jobject thiz, jint fd, 
+static jlong harmony_io_readImpl(JNIEnv* env, jobject, jint fd,
         jbyteArray byteArray, jint offset, jint nbytes) {
 
     if (nbytes == 0) {
@@ -310,67 +301,28 @@
     }
 
     jbyte* bytes = env->GetByteArrayElements(byteArray, NULL);
-    jlong result;
-    for (;;) {
-        result = read(fd, (void *) (bytes + offset), (int) nbytes);
-
-        if ((result != -1) || (errno != EINTR)) {
-            break;
-        }
-
-        /*
-         * If we didn't break above, that means that the read() call
-         * returned due to EINTR. We shield Java code from this
-         * possibility by trying again. Note that this is different
-         * from EAGAIN, which should result in this code throwing
-         * an InterruptedIOException.
-         */
-    }
-
+    jlong rc = EINTR_RETRY(read(fd, bytes + offset, nbytes));
     env->ReleaseByteArrayElements(byteArray, bytes, 0);
 
-    if (result == 0) {
+    if (rc == 0) {
         return -1;
     }
-    
-    if (result == -1) {
+    if (rc == -1) {
         if (errno == EAGAIN) {
             jniThrowException(env, "java/io/InterruptedIOException",
                     "Read timed out");
         } else {
-            jniThrowException(env, "java/io/IOException", strerror(errno));
+            jniThrowIOException(env, errno);
         }
     }
-
-    return result;
+    return rc;
 }
 
-/*
- * Class:     org_apache_harmony_io
- * Method:    writeImpl
- * Signature: (I[BII)J
- */
-static jlong harmony_io_writeImpl(JNIEnv * env, jobject thiz, jint fd, 
+static jlong harmony_io_writeImpl(JNIEnv* env, jobject, jint fd,
         jbyteArray byteArray, jint offset, jint nbytes) {
 
     jbyte* bytes = env->GetByteArrayElements(byteArray, NULL);
-    jlong result;
-    for (;;) {
-        result = write(fd, (const char *) bytes + offset, (int) nbytes);
-        
-        if ((result != -1) || (errno != EINTR)) {
-            break;
-        }
-
-        /*
-         * If we didn't break above, that means that the read() call
-         * returned due to EINTR. We shield Java code from this
-         * possibility by trying again. Note that this is different
-         * from EAGAIN, which should result in this code throwing
-         * an InterruptedIOException.
-         */
-    }
-
+    jlong result = EINTR_RETRY(write(fd, bytes + offset, nbytes));
     env->ReleaseByteArrayElements(byteArray, bytes, JNI_ABORT);
 
     if (result == -1) {
@@ -378,159 +330,77 @@
             jniThrowException(env, "java/io/InterruptedIOException",
                     "Write timed out");
         } else {
-            jniThrowException(env, "java/io/IOException", strerror(errno));
+            jniThrowIOException(env, errno);
         }
     }
-
     return result;
 }
-// END android-changed
 
-/**
- * Seeks a file descriptor to a given file position.
- * 
- * @param env pointer to Java environment
- * @param thiz pointer to object receiving the message
- * @param fd handle of file to be seeked
- * @param offset distance of movement in bytes relative to whence arg
- * @param whence enum value indicating from where the offset is relative
- * The valid values are defined in fsconstants.h.
- * @return the new file position from the beginning of the file, in bytes;
- * or -1 if a problem occurs.
- */
-static jlong harmony_io_seekImpl(JNIEnv * env, jobject thiz, jint fd, 
-        jlong offset, jint whence) {
-
-    int mywhence = 0;
-
+static jlong harmony_io_seek(JNIEnv* env, jobject, jint fd, jlong offset,
+        jint javaWhence) {
     /* Convert whence argument */
-    switch (whence) {
-        case 1:
-                mywhence = 0;
-                break;
-        case 2:
-                mywhence = 1;
-                break;
-        case 4:
-                mywhence = 2;
-                break;
-        default:
-                return -1;
-    }
-
-
-    off_t localOffset = (int) offset;
-
-    if((mywhence < 0) || (mywhence > 2)) {
+    int nativeWhence = 0;
+    switch (javaWhence) {
+    case 1:
+        nativeWhence = SEEK_SET;
+        break;
+    case 2:
+        nativeWhence = SEEK_CUR;
+        break;
+    case 4:
+        nativeWhence = SEEK_END;
+        break;
+    default:
         return -1;
     }
 
-    /* If file offsets are 32 bit, truncate the seek to that range */
-    if(sizeof (off_t) < sizeof (jlong)) {
-        if(offset > 0x7FFFFFFF) {
-            localOffset = 0x7FFFFFFF;
-        } else if(offset < -0x7FFFFFFF) {
-            localOffset = -0x7FFFFFFF;
-        }
+    // If the offset is relative, lseek(2) will tell us whether it's too large.
+    // We're just worried about too large an absolute offset, which would cause
+    // us to lie to lseek(2).
+    if (offsetTooLarge(env, offset)) {
+        return -1;
     }
 
-    return (jlong) lseek(fd, localOffset, mywhence);
-}
-
-/**
- * Flushes a file state to disk.
- *
- * @param env pointer to Java environment
- * @param thiz pointer to object receiving the message
- * @param fd handle of file to be flushed
- * @param metadata if true also flush metadata, 
- *         otherwise just flush data is possible.
- * @return zero on success and -1 on failure
- *
- * Method:    fflushImpl
- * Signature: (IZ)I
- */
-static jint harmony_io_fflushImpl(JNIEnv * env, jobject thiz, jint fd, 
-        jboolean metadata) {
-    return (jint) fsync(fd);
-}
-
-// BEGIN android-changed
-/**
- * Closes the given file handle
- * 
- * @param env pointer to Java environment
- * @param thiz pointer to object receiving the message
- * @param fd handle of file to be closed
- * @return zero on success and -1 on failure
- *
- * Class:     org_apache_harmony_io
- * Method:    closeImpl
- * Signature: (I)I
- */
-static jint harmony_io_closeImpl(JNIEnv * env, jobject thiz, jint fd) {
-    jint result;
-
-    for (;;) {
-        result = (jint) close(fd);
-        
-        if ((result != -1) || (errno != EINTR)) {
-            break;
-        }
-
-        /*
-         * If we didn't break above, that means that the close() call
-         * returned due to EINTR. We shield Java code from this
-         * possibility by trying again.
-         */
+    jlong result = lseek(fd, offset, nativeWhence);
+    if (result == -1) {
+        jniThrowIOException(env, errno);
     }
-
     return result;
 }
-// END android-changed
 
-
-/*
- * Class:     org_apache_harmony_io
- * Method:    truncateImpl
- * Signature: (IJ)I
- */
-static jint harmony_io_truncateImpl(JNIEnv * env, jobject thiz, jint fd, 
-        jlong size) {
-
-    int rc;
-    off_t length = (off_t) size;
-
-    // If file offsets are 32 bit, truncate the newLength to that range
-    if(sizeof (off_t) < sizeof (jlong)) {
-        if(length > 0x7FFFFFFF) {
-            length = 0x7FFFFFFF;
-        } else if(length < -0x7FFFFFFF) {
-            length = -0x7FFFFFFF;
-        }
+// TODO: are we supposed to support the 'metadata' flag? (false => fdatasync.)
+static void harmony_io_fflush(JNIEnv* env, jobject, jint fd,
+        jboolean metadata) {
+    int rc = fsync(fd);
+    if (rc == -1) {
+        jniThrowIOException(env, errno);
     }
-
-  rc = ftruncate((int)fd, length);
-
-  return (jint) rc;
-
 }
 
-/*
- * Class:     org_apache_harmony_io
- * Method:    openImpl
- * Signature: ([BI)I
- */
-static jint harmony_io_openImpl(JNIEnv * env, jobject obj, jbyteArray path, 
-        jint jflags) {
-    
-    if (path == NULL) {
-        jniThrowException(env, "java/lang/NullPointerException", NULL);
+static jint harmony_io_close(JNIEnv* env, jobject, jint fd) {
+    jint rc = EINTR_RETRY(close(fd));
+    if (rc == -1) {
+        jniThrowIOException(env, errno);
+    }
+    return rc;
+}
+
+static jint harmony_io_truncate(JNIEnv* env, jobject, jint fd, jlong length) {
+    if (offsetTooLarge(env, length)) {
         return -1;
     }
-    
+
+    int rc = ftruncate(fd, length);
+    if (rc == -1) {
+        jniThrowIOException(env, errno);
+    }
+    return rc;
+}
+
+static jint harmony_io_openImpl(JNIEnv* env, jobject, jbyteArray path,
+        jint jflags) {
     int flags = 0;
-    int mode = 0; 
+    int mode = 0;
 
 // BEGIN android-changed
 // don't want default permissions to allow global access.
@@ -552,7 +422,7 @@
               mode = 0600;
               break;
       case 256:
-              flags = HyOpenWrite | HyOpenCreate | HyOpenAppend; 
+              flags = HyOpenWrite | HyOpenCreate | HyOpenAppend;
               mode = 0600;
               break;
     }
@@ -560,6 +430,7 @@
 
     flags = EsTranslateOpenFlags(flags);
 
+    // TODO: clean this up when we clean up the java.io.File equivalent.
     jsize length = env->GetArrayLength (path);
     length = length < HyMaxPath - 1 ? length : HyMaxPath - 1;
     char pathCopy[HyMaxPath];
@@ -567,104 +438,48 @@
     pathCopy[length] = '\0';
     convertToPlatform (pathCopy);
 
-    int cc;
-    do {
-        cc = open(pathCopy, flags, mode);
-    } while(cc < 0 && errno == EINTR);
-
-    if(cc < 0 && errno > 0) {
+    jint cc = EINTR_RETRY(open(pathCopy, flags, mode));
+    // TODO: chase up the callers of this and check they wouldn't rather
+    // have us throw a meaningful IOException right here.
+    if (cc < 0 && errno > 0) {
         cc = -errno;
     }
-
     return cc;
-
-
 }
 
-// BEGIN android-deleted
-#if 0
-/*
- * Answers the number of remaining chars in the stdin.
- *
- * Class:     org_apache_harmony_io
- * Method:    ttyAvailableImpl
- * Signature: ()J
- */
-static jlong harmony_io_ttyAvailableImpl(JNIEnv *env, jobject thiz) {
-  
-    int rc;
-    off_t curr, end;
-
-    int avail = 0;
-
-    // when redirected from a file
-    curr = lseek(STDIN_FILENO, 0L, 2);    /* don't use tell(), it doesn't exist on all platforms, i.e. linux */
-    if(curr != -1) {
-        end = lseek(STDIN_FILENO, 0L, 4);
-        lseek(STDIN_FILENO, curr, 1);
-        if(end >= curr) {
-            return (jlong) (end - curr);
-        }
-    }
-
-    /* ioctl doesn't work for files on all platforms (i.e. SOLARIS) */
-
-    rc = ioctl (STDIN_FILENO, FIONREAD, &avail);
-
-    /* 64 bit platforms use a 32 bit value, using IDATA fails on big endian */
-    /* Pass in IDATA because ioctl() is device dependent, some devices may write 64 bits */
-    if(rc != -1) {
-        return (jlong) *(jint *) & avail;
-    }
-    return (jlong) 0;
-}
-#endif
-// END android-deleted
-
-// BEGIN android-added
-/*
- * Answers the number of remaining bytes in a file descriptor
- * using IOCTL.
- *
- * Class:     org_apache_harmony_io
- * Method:    ioctlAvailable
- * Signature: ()I
- */
-static jint harmony_io_ioctlAvailable(JNIEnv *env, jobject thiz, jint fd) {
-    int avail = 0;
-    int rc = ioctl(fd, FIONREAD, &avail);
-
+static jint harmony_io_ioctlAvailable(JNIEnv*env, jobject, jint fd) {
     /*
      * On underlying platforms Android cares about (read "Linux"),
      * ioctl(fd, FIONREAD, &avail) is supposed to do the following:
-     * 
+     *
      * If the fd refers to a regular file, avail is set to
      * the difference between the file size and the current cursor.
      * This may be negative if the cursor is past the end of the file.
-     * 
+     *
      * If the fd refers to an open socket or the read end of a
      * pipe, then avail will be set to a number of bytes that are
      * available to be read without blocking.
-     * 
+     *
      * If the fd refers to a special file/device that has some concept
      * of buffering, then avail will be set in a corresponding way.
-     * 
+     *
      * If the fd refers to a special device that does not have any
      * concept of buffering, then the ioctl call will return a negative
      * number, and errno will be set to ENOTTY.
-     * 
+     *
      * If the fd refers to a special file masquerading as a regular file,
      * then avail may be returned as negative, in that the special file
      * may appear to have zero size and yet a previous read call may have
      * actually read some amount of data and caused the cursor to be
      * advanced.
      */
-
+    int avail = 0;
+    int rc = ioctl(fd, FIONREAD, &avail);
     if (rc >= 0) {
         /*
          * Success, but make sure not to return a negative number (see
          * above).
-         */ 
+         */
         if (avail < 0) {
             avail = 0;
         }
@@ -673,22 +488,13 @@
         avail = 0;
     } else {
         /* Something strange is happening. */
-        jniThrowException(env, "java/io/IOException", strerror(errno));
-        avail = 0;
-    }  
+        jniThrowIOException(env, errno);
+    }
 
     return (jint) avail;
 }
-// END android-added
 
-/*
- * Reads the number of bytes from stdin.
- *
- * Class:     org_apache_harmony_io
- * Method:    ttyReadImpl
- * Signature: ([BII)J
- */
-static jlong harmony_io_ttyReadImpl(JNIEnv *env, jobject thiz, 
+static jlong harmony_io_ttyReadImpl(JNIEnv* env, jobject thiz,
         jbyteArray byteArray, jint offset, jint nbytes) {
     return harmony_io_readImpl(env, thiz, STDIN_FILENO, byteArray, offset, nbytes);
 }
@@ -698,32 +504,27 @@
  */
 static JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
-    { "lockImpl",           "(IJJIZ)I",   (void*) harmony_io_lockImpl },
-    { "getAllocGranularity","()I",     (void*) harmony_io_getAllocGranularity },
-    { "unlockImpl",         "(IJJ)I",     (void*) harmony_io_unlockImpl },
-    { "fflushImpl",         "(IZ)I",      (void*) harmony_io_fflushImpl },
-    { "seekImpl",           "(IJI)J",     (void*) harmony_io_seekImpl },
-    { "readDirectImpl",     "(IIII)J",    (void*) harmony_io_readDirectImpl },
-    { "writeDirectImpl",    "(IIII)J",    (void*) harmony_io_writeDirectImpl },
-    { "readImpl",           "(I[BII)J",   (void*) harmony_io_readImpl },
-    { "writeImpl",          "(I[BII)J",   (void*) harmony_io_writeImpl },
-    { "readvImpl",          "(I[I[I[II)J",(void*) harmony_io_readvImpl },
-    { "writevImpl",         "(I[I[I[II)J",(void*) harmony_io_writevImpl },
-    { "closeImpl",          "(I)I",       (void*) harmony_io_closeImpl },
-    { "truncateImpl",       "(IJ)I",      (void*) harmony_io_truncateImpl },
-    { "openImpl",           "([BI)I",     (void*) harmony_io_openImpl },
-    { "transferImpl",       "(ILjava/io/FileDescriptor;JJ)J", 
-    	    (void*) harmony_io_transferImpl },
-    // BEGIN android-deleted
-    //{ "ttyAvailableImpl",   "()J",        (void*) harmony_io_ttyAvailableImpl },
-    // END android-deleted
-    // BEGIN android-added
+    { "close",              "(I)V",       (void*) harmony_io_close },
+    { "fflush",             "(IZ)V",      (void*) harmony_io_fflush },
+    { "getAllocGranularity","()I",        (void*) harmony_io_getAllocGranularity },
     { "ioctlAvailable",     "(I)I",       (void*) harmony_io_ioctlAvailable },
-    // END android added
-    { "ttyReadImpl",        "([BII)J",    (void*) harmony_io_ttyReadImpl }
+    { "lockImpl",           "(IJJIZ)I",   (void*) harmony_io_lockImpl },
+    { "openImpl",           "([BI)I",     (void*) harmony_io_openImpl },
+    { "readDirect",         "(IIII)J",    (void*) harmony_io_readDirect },
+    { "readImpl",           "(I[BII)J",   (void*) harmony_io_readImpl },
+    { "readv",              "(I[I[I[II)J",(void*) harmony_io_readv },
+    { "seek",               "(IJI)J",     (void*) harmony_io_seek },
+    { "transfer",           "(ILjava/io/FileDescriptor;JJ)J",
+                                          (void*) harmony_io_transfer },
+    { "truncate",           "(IJ)V",      (void*) harmony_io_truncate },
+    { "ttyReadImpl",        "([BII)J",    (void*) harmony_io_ttyReadImpl },
+    { "unlockImpl",         "(IJJ)V",     (void*) harmony_io_unlockImpl },
+    { "writeDirect",        "(IIII)J",    (void*) harmony_io_writeDirect },
+    { "writeImpl",          "(I[BII)J",   (void*) harmony_io_writeImpl },
+    { "writev",             "(I[I[I[II)J",(void*) harmony_io_writev },
 };
-int register_org_apache_harmony_luni_platform_OSFileSystem(JNIEnv *_env) {
-	return jniRegisterNativeMethods(_env, 
-	        "org/apache/harmony/luni/platform/OSFileSystem", gMethods, 
-	        NELEM(gMethods));
+int register_org_apache_harmony_luni_platform_OSFileSystem(JNIEnv* _env) {
+    return jniRegisterNativeMethods(_env,
+            "org/apache/harmony/luni/platform/OSFileSystem", gMethods,
+            NELEM(gMethods));
 }
diff --git a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
index dd96498..eede677 100644
--- a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
+++ b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
@@ -209,13 +209,6 @@
 }
 
 /**
- * Throws an IOException with the given message.
- */
-static void throwIOExceptionStr(JNIEnv *env, const char *message) {
-    jniThrowException(env, "java/io/IOException", message);
-}
-
-/**
  * Throws a NullPointerException.
  */
 static void throwNullPointerException(JNIEnv *env) {
diff --git a/libcore/luni/src/test/java/tests/api/java/io/FileTest.java b/libcore/luni/src/test/java/tests/api/java/io/FileTest.java
index 2850826..5c0cb2a 100644
--- a/libcore/luni/src/test/java/tests/api/java/io/FileTest.java
+++ b/libcore/luni/src/test/java/tests/api/java/io/FileTest.java
@@ -688,10 +688,7 @@
         method = "delete",
         args = {}
     )
-    @KnownFailure("Non empty directories are deleted on Android.")
     public void test_delete() {
-        // this test passes in the emulator, but it fails on the device
-
         // Test for method boolean java.io.File.delete()
         try {
             File dir = new File(System.getProperty("java.io.tmpdir"), platformId
diff --git a/libcore/luni/src/test/java/tests/api/java/io/RandomAccessFileTest.java b/libcore/luni/src/test/java/tests/api/java/io/RandomAccessFileTest.java
index 3b545e3..dc35610 100644
--- a/libcore/luni/src/test/java/tests/api/java/io/RandomAccessFileTest.java
+++ b/libcore/luni/src/test/java/tests/api/java/io/RandomAccessFileTest.java
@@ -1200,16 +1200,25 @@
         } catch (IOException e) {
             // Expected.
         }
+        // BEGIN android-added
+        try {
+            // Android uses 32-bit off_t, so anything larger than a signed 32-bit int won't work.
+            raf.seek(((long) Integer.MAX_VALUE) + 1);
+            fail("Test 2: IOException expected.");
+        } catch (IOException e) {
+            // Expected.
+        }
+        // END android-added
 
         raf.write(testString.getBytes(), 0, testLength);
         raf.seek(12);
-        assertEquals("Test 2: Seek failed to set file pointer.", 12, 
+        assertEquals("Test 3: Seek failed to set file pointer.", 12,
                 raf.getFilePointer());
         
         raf.close();
         try {
             raf.seek(1);
-            fail("Test 1: IOException expected.");
+            fail("Test 4: IOException expected.");
         } catch (IOException e) {
             // Expected.
         }
@@ -1296,10 +1305,21 @@
         assertEquals("Test 7: Incorrect file length;", 
                 testLength + 2, raf.length());
         
+        // BEGIN android-added
+        // Exception testing.
+        try {
+            // Android uses 32-bit off_t, so anything larger than a signed 32-bit int won't work.
+            raf.setLength(((long) Integer.MAX_VALUE) + 1);
+            fail("Test 8: IOException expected.");
+        } catch (IOException e) {
+            // Expected.
+        }
+        // END android-added
+
         // Exception testing.
         try {
             raf.setLength(-1);
-            fail("Test 8: IllegalArgumentException expected.");
+            fail("Test 9: IllegalArgumentException expected.");
         } catch (IllegalArgumentException e) {
             // Expected.
         }
@@ -1307,7 +1327,7 @@
         raf.close();
         try {
             raf.setLength(truncLength);
-            fail("Test 9: IOException expected.");
+            fail("Test 10: IOException expected.");
         } catch (IOException e) {
             // Expected.
         }
@@ -1501,4 +1521,4 @@
         super.tearDown();
     }
 
-}
\ No newline at end of file
+}
diff --git a/libcore/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/FileChannelTest.java b/libcore/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/FileChannelTest.java
index 91d6d06..a846e70 100644
--- a/libcore/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/FileChannelTest.java
+++ b/libcore/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/FileChannelTest.java
@@ -1155,6 +1155,25 @@
         } catch (IllegalArgumentException e) {
             // expected
         }
+
+        // BEGIN android-added
+        // Android uses 32-bit off_t, so anything larger than a signed 32-bit int won't work...
+        // ...except for the special case of length == Long.MAX_VALUE, which is used to mean "the
+        // whole file". The special case is tested elsewhere.
+        long tooBig = ((long) Integer.MAX_VALUE) + 1;
+        try {
+            readWriteFileChannel.tryLock(tooBig, 1, false);
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        try {
+            readWriteFileChannel.tryLock(0, tooBig, false);
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        // END android-added
     }
 
     /**