Merge "JarFile: Add tests for ambiguous chains"
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest4.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest4.java
index 1e569e8..c5dd4f0 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest4.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest4.java
@@ -721,35 +721,6 @@
 
     }
 
-    public void test_writeObject_Collections_UnmodifiableMap_UnmodifiableEntrySet() throws Exception {
-        // Test for method void
-        // java.io.ObjectOutputStream.writeObject(java.util.Collections.UnmodifiableMap.UnmodifiableEntrySet)
-
-        Object objToSave = null;
-        Object objLoaded = null;
-
-
-            objToSave = java.util.Collections.unmodifiableMap(MAP).entrySet();
-            if (DEBUG)
-                System.out.println("Obj = " + objToSave);
-            objLoaded = dumpAndReload(objToSave);
-
-            // Has to have worked
-            boolean equals;
-            equals = ((java.util.Collection) objToSave).size() == ((java.util.Collection) objLoaded)
-                    .size();
-            if (equals) {
-                java.util.Iterator iter1 = ((java.util.Collection) objToSave)
-                        .iterator(), iter2 = ((java.util.Collection) objLoaded)
-                        .iterator();
-                while (iter1.hasNext())
-                    equals = equals && iter1.next().equals(iter2.next());
-            }
-            assertTrue(MSG_TEST_FAILED + objToSave, equals);
-
-
-    }
-
     public void test_writeObject_NumberFormat() {
         // Test for method void
         // java.io.ObjectOutputStream.writeObject(java.text.NumberFormat)
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ThreadGroupTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ThreadGroupTest.java
index dbf2e22..a437939 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ThreadGroupTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ThreadGroupTest.java
@@ -21,62 +21,41 @@
 
 public class ThreadGroupTest extends junit.framework.TestCase {
 
-    class MyThread extends Thread {
-        public volatile int heartBeat = 0;
+    private TestThreadDefaultUncaughtExceptionHandler testThreadDefaultUncaughtExceptionHandler;
+    private ThreadGroup rootThreadGroup;
+    private ThreadGroup initialThreadGroup;
+    private Thread.UncaughtExceptionHandler originalThreadDefaultUncaughtExceptionHandler;
 
-        public MyThread(ThreadGroup group, String name)
-                throws SecurityException, IllegalThreadStateException {
-            super(group, name);
+    @Override
+    protected void setUp() {
+        initialThreadGroup = Thread.currentThread().getThreadGroup();
+        rootThreadGroup = initialThreadGroup;
+        while (rootThreadGroup.getParent() != null) {
+            rootThreadGroup = rootThreadGroup.getParent();
         }
 
-        @Override
-        public void run() {
-            while (true) {
-                heartBeat++;
-                try {
-                    Thread.sleep(50);
-                } catch (InterruptedException e) {
-                }
-            }
-        }
-
-        public boolean isActivelyRunning() {
-            long MAX_WAIT = 100;
-            return isActivelyRunning(MAX_WAIT);
-        }
-
-        public boolean isActivelyRunning(long maxWait) {
-            int beat = heartBeat;
-            long start = System.currentTimeMillis();
-            do {
-                Thread.yield();
-                int beat2 = heartBeat;
-                if (beat != beat2) {
-                    return true;
-                }
-            } while (System.currentTimeMillis() - start < maxWait);
-            return false;
-        }
-
+        // When running as a CTS test Android will by default treat an uncaught exception as a
+        // fatal application error and kill the test. To avoid this the default
+        // UncaughtExceptionHandler is replaced for the duration of the test (if one exists). It
+        // also allows us to test that ultimately the default handler is called if a ThreadGroup's
+        // UncaughtExceptionHandler doesn't handle an exception.
+        originalThreadDefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
+        testThreadDefaultUncaughtExceptionHandler = new TestThreadDefaultUncaughtExceptionHandler();
+        Thread.setDefaultUncaughtExceptionHandler(testThreadDefaultUncaughtExceptionHandler);
     }
 
-    private ThreadGroup rootThreadGroup = null;
+    @Override
+    protected void tearDown() {
+        // Reset the uncaughtExceptionHandler to what it was when the test began.
+        Thread.setDefaultUncaughtExceptionHandler(originalThreadDefaultUncaughtExceptionHandler);
+    }
 
-    private ThreadGroup initialThreadGroup = null;
-
-    /**
-     * java.lang.ThreadGroup#ThreadGroup(java.lang.String)
-     */
+    // Test for method java.lang.ThreadGroup(java.lang.String)
     public void test_ConstructorLjava_lang_String() {
-        // Test for method java.lang.ThreadGroup(java.lang.String)
-
-        // Unfortunately we have to use other APIs as well as we test the
-        // constructor
-
-        ThreadGroup newGroup = null;
-        ThreadGroup initial = getInitialThreadGroup();
+        // Unfortunately we have to use other APIs as well as we test the constructor
+        ThreadGroup initial = initialThreadGroup;
         final String name = "Test name";
-        newGroup = new ThreadGroup(name);
+        ThreadGroup newGroup = new ThreadGroup(name);
         assertTrue(
                 "Has to be possible to create a subgroup of current group using simple constructor",
                 newGroup.getParent() == initial);
@@ -84,39 +63,28 @@
 
         // cleanup
         newGroup.destroy();
-
     }
 
-    /**
-     * java.lang.ThreadGroup#ThreadGroup(java.lang.ThreadGroup,
-     *java.lang.String)
-     */
+    // Test for method java.lang.ThreadGroup(java.lang.ThreadGroup, java.lang.String)
     public void test_ConstructorLjava_lang_ThreadGroupLjava_lang_String() {
-        // Test for method java.lang.ThreadGroup(java.lang.ThreadGroup,
-        // java.lang.String)
-
-        // Unfortunately we have to use other APIs as well as we test the
-        // constructor
-
+        // Unfortunately we have to use other APIs as well as we test the constructor
         ThreadGroup newGroup = null;
-
         try {
             newGroup = new ThreadGroup(null, null);
         } catch (NullPointerException e) {
         }
-        assertNull("Can't create a ThreadGroup with a null parent",
-                newGroup);
+        assertNull("Can't create a ThreadGroup with a null parent", newGroup);
 
-        newGroup = new ThreadGroup(getInitialThreadGroup(), null);
+        newGroup = new ThreadGroup(initialThreadGroup, null);
         assertTrue("Has to be possible to create a subgroup of current group",
                 newGroup.getParent() == Thread.currentThread().getThreadGroup());
 
         // Lets start all over
         newGroup.destroy();
 
-        newGroup = new ThreadGroup(getRootThreadGroup(), "a name here");
+        newGroup = new ThreadGroup(rootThreadGroup, "a name here");
         assertTrue("Has to be possible to create a subgroup of root group",
-                newGroup.getParent() == getRootThreadGroup());
+                newGroup.getParent() == rootThreadGroup);
 
         // Lets start all over
         newGroup.destroy();
@@ -126,16 +94,11 @@
         } catch (IllegalThreadStateException e) {
             newGroup = null;
         }
-        ;
-        assertNull("Can't create a subgroup of a destroyed group",
-                newGroup);
+        assertNull("Can't create a subgroup of a destroyed group", newGroup);
     }
 
-    /**
-     * java.lang.ThreadGroup#activeCount()
-     */
+    // Test for method int java.lang.ThreadGroup.activeCount()
     public void test_activeCount() {
-        // Test for method int java.lang.ThreadGroup.activeCount()
         ThreadGroup tg = new ThreadGroup("activeCount");
         Thread t1 = new Thread(tg, new Runnable() {
             public void run() {
@@ -159,13 +122,9 @@
         tg.destroy();
     }
 
-    /**
-     * java.lang.ThreadGroup#destroy()
-     */
+    // Test for method void java.lang.ThreadGroup.destroy()
     public void test_destroy() {
-        // Test for method void java.lang.ThreadGroup.destroy()
-
-        final ThreadGroup originalCurrent = getInitialThreadGroup();
+        final ThreadGroup originalCurrent = initialThreadGroup;
         ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");
         final int DEPTH = 4;
         final Vector<ThreadGroup> subgroups = buildRandomTreeUnder(testRoot, DEPTH);
@@ -175,15 +134,13 @@
 
         for (int i = 0; i < subgroups.size(); i++) {
             ThreadGroup child = subgroups.elementAt(i);
-            assertEquals("Destroyed child can't have children", 0, child
-                    .activeCount());
+            assertEquals("Destroyed child can't have children", 0, child.activeCount());
             boolean passed = false;
             try {
                 child.destroy();
             } catch (IllegalThreadStateException e) {
                 passed = true;
             }
-            ;
             assertTrue("Destroyed child can't be destroyed again", passed);
         }
 
@@ -202,7 +159,6 @@
         } catch (IllegalThreadStateException e) {
             passed = true;
         }
-        ;
         assertTrue("Daemon should have been destroyed already", passed);
 
         passed = false;
@@ -211,7 +167,6 @@
         } catch (IllegalThreadStateException e) {
             passed = true;
         }
-        ;
         assertTrue("Daemon parent should have been destroyed automatically",
                 passed);
 
@@ -231,12 +186,7 @@
         noOp.start();
 
         // Wait for the no-op thread to run inside daemon ThreadGroup
-        try {
-            noOp.join();
-        } catch (InterruptedException ie) {
-            fail("Should not be interrupted");
-        }
-        ;
+        waitForThreadToDieUninterrupted(noOp);
 
         passed = false;
         try {
@@ -244,10 +194,7 @@
         } catch (IllegalThreadStateException e) {
             passed = true;
         }
-        ;
-        assertTrue(
-                "Daemon group should have been destroyed already when last thread died",
-                passed);
+        assertTrue("Daemon group should have been destroyed already when last thread died", passed);
 
         testRoot = new ThreadGroup(originalCurrent, "Test group (daemon)");
         noOp = new Thread(testRoot, null, "no-op thread") {
@@ -261,8 +208,7 @@
             }
         };
 
-        // Has to execute the next lines in an interval < the sleep interval of
-        // the no-op thread
+        // Has to execute the next lines in an interval < the sleep interval of the no-op thread
         noOp.start();
         passed = false;
         try {
@@ -272,29 +218,18 @@
         }
         assertTrue("Can't destroy a ThreadGroup that has threads", passed);
 
-        // But after the thread dies, we have to be able to destroy the thread
-        // group
-        try {
-            noOp.join();
-        } catch (InterruptedException ie) {
-            fail("Should not be interrupted");
-        }
-        ;
+        // But after the thread dies, we have to be able to destroy the thread group
+        waitForThreadToDieUninterrupted(noOp);
         passed = true;
         try {
             testRoot.destroy();
         } catch (IllegalThreadStateException its) {
             passed = false;
         }
-        assertTrue(
-                "Should be able to destroy a ThreadGroup that has no threads",
-                passed);
-
+        assertTrue("Should be able to destroy a ThreadGroup that has no threads", passed);
     }
 
-    /**
-     * java.lang.ThreadGroup#destroy()
-     */
+    // Test for method java.lang.ThreadGroup.destroy()
     public void test_destroy_subtest0() {
         ThreadGroup group1 = new ThreadGroup("test_destroy_subtest0");
         group1.destroy();
@@ -305,13 +240,9 @@
         }
     }
 
-    /**
-     * java.lang.ThreadGroup#getMaxPriority()
-     */
+    // Test for method int java.lang.ThreadGroup.getMaxPriority()
     public void test_getMaxPriority() {
-        // Test for method int java.lang.ThreadGroup.getMaxPriority()
-
-        final ThreadGroup originalCurrent = getInitialThreadGroup();
+        final ThreadGroup originalCurrent = initialThreadGroup;
         ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");
 
         boolean passed = true;
@@ -322,38 +253,26 @@
         }
         assertTrue("Should be able to set priority", passed);
 
-        assertTrue("New value should be the same as we set", testRoot
-                .getMaxPriority() == Thread.MIN_PRIORITY);
+        assertTrue("New value should be the same as we set",
+                testRoot.getMaxPriority() == Thread.MIN_PRIORITY);
 
         testRoot.destroy();
-
     }
 
-    /**
-     * java.lang.ThreadGroup#getName()
-     */
+    // Test for method java.lang.String java.lang.ThreadGroup.getName()
     public void test_getName() {
-        // Test for method java.lang.String java.lang.ThreadGroup.getName()
-
-        final ThreadGroup originalCurrent = getInitialThreadGroup();
+        final ThreadGroup originalCurrent = initialThreadGroup;
         final String name = "Test group";
         final ThreadGroup testRoot = new ThreadGroup(originalCurrent, name);
 
-        assertTrue("Setting a name&getting does not work", testRoot.getName()
-                .equals(name));
+        assertTrue("Setting a name&getting does not work", testRoot.getName().equals(name));
 
         testRoot.destroy();
-
     }
 
-    /**
-     * java.lang.ThreadGroup#getParent()
-     */
+    // Test for method java.lang.ThreadGroup java.lang.ThreadGroup.getParent()
     public void test_getParent() {
-        // Test for method java.lang.ThreadGroup
-        // java.lang.ThreadGroup.getParent()
-
-        final ThreadGroup originalCurrent = getInitialThreadGroup();
+        final ThreadGroup originalCurrent = initialThreadGroup;
         ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");
 
         assertTrue("Parent is wrong", testRoot.getParent() == originalCurrent);
@@ -381,38 +300,16 @@
         testRoot.destroy();
     }
 
-    /**
-     * java.lang.ThreadGroup#isDaemon()
-     */
-    public void test_isDaemon() {
-        // Test for method boolean java.lang.ThreadGroup.isDaemon()
-
-        daemonTests();
-
-    }
-
-    /**
-     * java.lang.ThreadGroup#list()
-     */
+    // Test for method void java.lang.ThreadGroup.list()
     public void test_list() {
-        // Test for method void java.lang.ThreadGroup.list()
-
-        final ThreadGroup originalCurrent = getInitialThreadGroup();
-        // wipeSideEffectThreads destroy all side effect of threads created in
-        // java.lang.Thread
-        boolean result = wipeSideEffectThreads(originalCurrent);
-        if (result == false) {
-            fail("wipe threads in test_list() not successful");
-        }
-        final ThreadGroup testRoot = new ThreadGroup(originalCurrent,
-                "Test group");
+        final ThreadGroup originalCurrent = initialThreadGroup;
+        final ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");
 
         // First save the original System.out
         java.io.PrintStream originalOut = System.out;
 
         try {
-            java.io.ByteArrayOutputStream contentsStream = new java.io.ByteArrayOutputStream(
-                    100);
+            java.io.ByteArrayOutputStream contentsStream = new java.io.ByteArrayOutputStream(100);
             java.io.PrintStream newOut = new java.io.PrintStream(contentsStream);
 
             // We have to "redirect" System.out to test the method 'list'
@@ -421,13 +318,11 @@
             originalCurrent.list();
 
             /*
-                * The output has to look like this
-                *
-                * java.lang.ThreadGroup[name=main,maxpri=10] Thread[main,5,main]
-                * java.lang.ThreadGroup[name=Test group,maxpri=10]
-                *
-                */
-
+             * The output has to look like this:
+             *
+             * java.lang.ThreadGroup[name=main,maxpri=10] Thread[main,5,main]
+             * java.lang.ThreadGroup[name=Test group,maxpri=10]
+             */
             String contents = new String(contentsStream.toByteArray());
             boolean passed = (contents.indexOf("ThreadGroup[name=main") != -1) &&
                     (contents.indexOf("Thread[") != -1) &&
@@ -442,17 +337,11 @@
             // No matter what, we need to restore the original System.out
             System.setOut(originalOut);
         }
-
     }
 
-    /**
-     * java.lang.ThreadGroup#parentOf(java.lang.ThreadGroup)
-     */
+    // Test for method boolean java.lang.ThreadGroup.parentOf(java.lang.ThreadGroup)
     public void test_parentOfLjava_lang_ThreadGroup() {
-        // Test for method boolean
-        // java.lang.ThreadGroup.parentOf(java.lang.ThreadGroup)
-
-        final ThreadGroup originalCurrent = getInitialThreadGroup();
+        final ThreadGroup originalCurrent = initialThreadGroup;
         final ThreadGroup testRoot = new ThreadGroup(originalCurrent,
                 "Test group");
         final int DEPTH = 4;
@@ -460,8 +349,7 @@
 
         final ThreadGroup[] allChildren = allGroups(testRoot);
         for (ThreadGroup element : allChildren) {
-            assertTrue("Have to be parentOf all children", testRoot
-                    .parentOf(element));
+            assertTrue("Have to be parentOf all children", testRoot.parentOf(element));
         }
 
         assertTrue("Have to be parentOf itself", testRoot.parentOf(testRoot));
@@ -471,14 +359,20 @@
                 !arrayIncludes(groups(testRoot.getParent()), testRoot));
     }
 
-    /**
-     * java.lang.ThreadGroup#setDaemon(boolean)
-     */
-    public void test_setDaemonZ() {
-        // Test for method void java.lang.ThreadGroup.setDaemon(boolean)
+    // Test for method boolean java.lang.ThreadGroup.isDaemon() and
+    // void java.lang.ThreadGroup.setDaemon(boolean)
+    public void test_setDaemon_isDaemon() {
+        final ThreadGroup originalCurrent = initialThreadGroup;
+        final ThreadGroup testRoot = new ThreadGroup(originalCurrent,
+                "Test group");
 
-        daemonTests();
+        testRoot.setDaemon(true);
+        assertTrue("Setting daemon&getting does not work", testRoot.isDaemon());
 
+        testRoot.setDaemon(false);
+        assertTrue("Setting daemon&getting does not work", !testRoot.isDaemon());
+
+        testRoot.destroy();
     }
 
     /*
@@ -501,13 +395,9 @@
         assertFalse(ctg.isDaemon());
     }
 
-    /**
-     * java.lang.ThreadGroup#setMaxPriority(int)
-     */
+    // Test for method void java.lang.ThreadGroup.setMaxPriority(int)
     public void test_setMaxPriorityI() {
-        // Test for method void java.lang.ThreadGroup.setMaxPriority(int)
-
-        final ThreadGroup originalCurrent = getInitialThreadGroup();
+        final ThreadGroup originalCurrent = initialThreadGroup;
         ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");
 
         boolean passed;
@@ -529,8 +419,7 @@
         passed = testRoot.getMaxPriority() == Thread.MIN_PRIORITY;
         assertTrue(
                 "setMaxPriority: Any value smaller than MIN_PRIORITY is adjusted to MIN_PRIORITY. Before: "
-                        + currentMax + " , after: " + testRoot.getMaxPriority(),
-                passed);
+                        + currentMax + " , after: " + testRoot.getMaxPriority(), passed);
 
         // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
@@ -579,8 +468,7 @@
 
         assertTrue(
                 "Priority of leaf child group has to be much smaller than original root group",
-                current.getMaxPriority() == testRoot.getMaxPriority()
-                        - TOTAL_DEPTH);
+                current.getMaxPriority() == testRoot.getMaxPriority() - TOTAL_DEPTH);
 
         testRoot.destroy();
 
@@ -599,351 +487,184 @@
         testRoot.destroy();
     }
 
-    /**
-     * java.lang.ThreadGroup#uncaughtException(java.lang.Thread,
-     *java.lang.Throwable)
+    /*
+     * Test for method void java.lang.ThreadGroup.uncaughtException(java.lang.Thread,
+     * java.lang.Throwable)
+     * Tests if a Thread tells its ThreadGroup about ThreadDeath.
      */
-    @SuppressWarnings("deprecation")
-    public void test_uncaughtExceptionLjava_lang_ThreadLjava_lang_Throwable() {
-        // Test for method void
-        // java.lang.ThreadGroup.uncaughtException(java.lang.Thread,
-        // java.lang.Throwable)
+    public void test_uncaughtException_threadDeath() {
+        final boolean[] passed = new boolean[1];
 
-        final ThreadGroup originalCurrent = getInitialThreadGroup();
+        ThreadGroup testRoot = new ThreadGroup(rootThreadGroup,
+                "Test Forcing a throw of ThreadDeath") {
+            @Override
+            public void uncaughtException(Thread t, Throwable e) {
+                if (e instanceof ThreadDeath) {
+                    passed[0] = true;
+                }
+                // always forward, any exception
+                super.uncaughtException(t, e);
+            }
+        };
 
-        // indices for the array defined below
-        final int TEST_DEATH = 0;
-        final int TEST_OTHER = 1;
-        final int TEST_EXCEPTION_IN_UNCAUGHT = 2;
-        final int TEST_OTHER_THEN_DEATH = 3;
-        final int TEST_FORCING_THROW_THREAD_DEATH = 4;
-        final int TEST_KILLING = 5;
-        final int TEST_DEATH_AFTER_UNCAUGHT = 6;
+        final ThreadDeath threadDeath = new ThreadDeath();
+        Thread thread = new Thread(testRoot, null, "suicidal thread") {
+            @Override
+            public void run() {
+                throw threadDeath;
+            }
+        };
+        thread.start();
+        waitForThreadToDieUninterrupted(thread);
+        testThreadDefaultUncaughtExceptionHandler.assertWasCalled(thread, threadDeath);
 
-        final boolean[] passed = new boolean[] { false, false, false, false,
-                false, false, false };
+        testRoot.destroy();
+        assertTrue(
+                "Any thread should notify its ThreadGroup about its own death, even if suicide:"
+                        + testRoot, passed[0]);
+    }
 
-        ThreadGroup testRoot;
-        Thread thread;
+    /*
+     * Test for method void java.lang.ThreadGroup.uncaughtException(java.lang.Thread,
+     * java.lang.Throwable)
+     * Test if a Thread tells its ThreadGroup about a natural (non-exception) death.
+     */
+    public void test_uncaughtException_naturalDeath() {
+        final boolean[] failed = new boolean[1];
 
+        ThreadGroup testRoot = new ThreadGroup(initialThreadGroup, "Test ThreadDeath") {
+            @Override
+            public void uncaughtException(Thread t, Throwable e) {
+                failed[0] = true;
+
+                // always forward any exception
+                super.uncaughtException(t, e);
+            }
+        };
+
+        Thread thread = new Thread(testRoot, null, "no-op thread");
+        thread.start();
+        waitForThreadToDieUninterrupted(thread);
+        testThreadDefaultUncaughtExceptionHandler.assertWasNotCalled();
+        testRoot.destroy();
+        assertFalse("A thread should not call uncaughtException when it dies:"
+                + testRoot, failed[0]);
+    }
+
+    /*
+     * Test for method void java.lang.ThreadGroup.uncaughtException(java.lang.Thread,
+     * java.lang.Throwable)
+     * Test if a Thread tells its ThreadGroup about an Exception
+     */
+    public void test_uncaughtException_runtimeException() {
         // Our own exception class
         class TestException extends RuntimeException {
             private static final long serialVersionUID = 1L;
         }
 
-        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-        // - - - - - - -
-        testRoot = new ThreadGroup(originalCurrent,
-                "Test killing a Thread, forcing it to throw ThreadDeath") {
-            @Override
-            public void uncaughtException(Thread t, Throwable e) {
-                if (e instanceof ThreadDeath) {
-                    passed[TEST_KILLING] = true;
-                }
-                // always forward, any exception
-                super.uncaughtException(t, e);
-            }
-        };
+        final boolean[] passed = new boolean[1];
 
-        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-        // - - - - - - -
-        testRoot = new ThreadGroup(originalCurrent,
-                "Test Forcing a throw of ThreadDeath") {
-            @Override
-            public void uncaughtException(Thread t, Throwable e) {
-                if (e instanceof ThreadDeath) {
-                    passed[TEST_FORCING_THROW_THREAD_DEATH] = true;
-                }
-                // always forward, any exception
-                super.uncaughtException(t, e);
-            }
-        };
-
-        // Test if a Thread tells its ThreadGroup about ThreadDeath
-        thread = new Thread(testRoot, null, "suicidal thread") {
-            @Override
-            public void run() {
-                throw new ThreadDeath();
-            }
-        };
-        thread.start();
-        try {
-            thread.join();
-        } catch (InterruptedException ie) {
-            fail("Should not have been interrupted");
-        }
-        testRoot.destroy();
-        assertTrue(
-                "Any thread should notify its ThreadGroup about its own death, even if suicide:"
-                        + testRoot, passed[TEST_FORCING_THROW_THREAD_DEATH]);
-
-        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-        // - - - - - - -
-
-        testRoot = new ThreadGroup(originalCurrent, "Test ThreadDeath") {
-            @Override
-            public void uncaughtException(Thread t, Throwable e) {
-                passed[TEST_DEATH] = false;
-                // always forward, any exception
-                super.uncaughtException(t, e);
-            }
-        };
-
-        // Test if a Thread tells its ThreadGroup about ThreadDeath
-        passed[TEST_DEATH] = true;
-        thread = new Thread(testRoot, null, "no-op thread");
-        thread.start();
-        try {
-            thread.join();
-        } catch (InterruptedException ie) {
-            fail("Should not have been interrupted");
-        }
-        testRoot.destroy();
-        assertTrue("A thread should not call uncaughtException when it dies:"
-                + testRoot, passed[TEST_DEATH]);
-
-        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-        // - - - - - - -
-
-        testRoot = new ThreadGroup(originalCurrent, "Test other Exception") {
+        ThreadGroup testRoot = new ThreadGroup(initialThreadGroup, "Test other Exception") {
             @Override
             public void uncaughtException(Thread t, Throwable e) {
                 if (e instanceof TestException) {
-                    passed[TEST_OTHER] = true;
-                } else {
-                    // only forward exceptions other than our test
-                    super.uncaughtException(t, e);
+                    passed[0] = true;
                 }
+                // always forward any exception
+                super.uncaughtException(t, e);
             }
         };
 
-        // Test if a Thread tells its ThreadGroup about an Exception
-        thread = new Thread(testRoot, null, "no-op thread") {
+        final TestException testException = new TestException();
+        Thread thread = new Thread(testRoot, null, "RuntimeException thread") {
             @Override
             public void run() {
-                throw new TestException();
+                throw testException;
             }
         };
         thread.start();
-        try {
-            thread.join();
-        } catch (InterruptedException ie) {
-            fail("Should not have been interrupted");
-        }
+        waitForThreadToDieUninterrupted(thread);
+        testThreadDefaultUncaughtExceptionHandler.assertWasCalled(thread, testException);
         testRoot.destroy();
         assertTrue(
                 "Any thread should notify its ThreadGroup about an uncaught exception:"
-                        + testRoot, passed[TEST_OTHER]);
+                        + testRoot, passed[0]);
+    }
 
-        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-        // - - - - - - -
-
-        // Our own uncaught exception class
-        class UncaughtException extends TestException {
+    /*
+     * Test for method void java.lang.ThreadGroup.uncaughtException(java.lang.Thread,
+     * java.lang.Throwable)
+     * Test if a handler doesn't pass on the exception to super.uncaughtException that's ok.
+     */
+    public void test_uncaughtException_exceptionHandledByHandler() {
+        // Our own exception class
+        class TestException extends RuntimeException {
             private static final long serialVersionUID = 1L;
         }
 
-        testRoot = new ThreadGroup(originalCurrent,
-                "Test Exception in uncaught exception") {
+        ThreadGroup testRoot = new ThreadGroup(initialThreadGroup, "Test other Exception") {
             @Override
             public void uncaughtException(Thread t, Throwable e) {
-                if (e instanceof TestException) {
-                    passed[TEST_EXCEPTION_IN_UNCAUGHT] = true;
-                    // Let's simulate an error inside our uncaughtException
-                    // method.
-                    // This should be no-op according to the spec
-                    throw new UncaughtException();
-                }
-                // only forward exceptions other than our test
-                super.uncaughtException(t, e);
-            }
-        };
-
-        // Test if an Exception in uncaughtException is really a no-op
-        thread = new Thread(testRoot, null, "no-op thread") {
-            @Override
-            public void run() {
-                try {
-                    throw new TestException();
-                } catch (UncaughtException ue) {
-                    // any exception in my ThreadGroup's uncaughtException must
-                    // not be propagated.
-                    // If it gets propagated and we detected that, the test failed
-                    passed[TEST_EXCEPTION_IN_UNCAUGHT] = false;
-                }
-            }
-        };
-        thread.start();
-        try {
-            thread.join();
-        } catch (InterruptedException ie) {
-            fail("Should not have been interrupted");
-        }
-        testRoot.destroy();
-        assertTrue(
-                "Any uncaughtException in uncaughtException should be no-op:"
-                        + testRoot, passed[TEST_EXCEPTION_IN_UNCAUGHT]);
-
-        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-        // - - - - - - -
-
-        // This is a mix of 2 of the tests above. It is assumed that ThreadDeath
-        // and any random exception do work , tested separately. Now we test
-        // if after an uncaughtException is forwarded to the ThreadGroup and
-        // the Thread dies, if ThreadDeath is also forwarded. It should be
-        // (so that a ThreadGroup can know its Thread died)
-        testRoot = new ThreadGroup(originalCurrent,
-                "Test Uncaught followed by ThreadDeath") {
-            @Override
-            public void uncaughtException(Thread t, Throwable e) {
-                if (e instanceof ThreadDeath) {
-                    passed[TEST_DEATH_AFTER_UNCAUGHT] = true;
-                }
-                if (e instanceof TestException) {
-                    passed[TEST_OTHER_THEN_DEATH] = true;
-                } else {
-                    // only forward exceptions other than our test
+                // Swallow TestException and always forward any other exception
+                if (!(e instanceof TestException)) {
                     super.uncaughtException(t, e);
                 }
             }
         };
 
-        // Test if a Thread tells its ThreadGroup about an Exception and also
-        // ThreadDeath
-        thread = new Thread(testRoot, null, "no-op thread") {
+        final TestException testException = new TestException();
+        Thread thread = new Thread(testRoot, null, "RuntimeException thread") {
             @Override
             public void run() {
-                throw new TestException();
+                throw testException;
             }
         };
         thread.start();
-        try {
-            thread.join();
-        } catch (InterruptedException ie) {
-            fail("Should not have been interrupted");
-        }
+        waitForThreadToDieUninterrupted(thread);
+        testThreadDefaultUncaughtExceptionHandler.assertWasNotCalled();
         testRoot.destroy();
     }
 
-    @Override
-    protected void setUp() {
-        initialThreadGroup = Thread.currentThread().getThreadGroup();
-        rootThreadGroup = initialThreadGroup;
-        while (rootThreadGroup.getParent() != null) {
-            rootThreadGroup = rootThreadGroup.getParent();
-        }
-    }
-
-    @Override
-    protected void tearDown() {
-        try {
-            // Give the threads a chance to die.
-            Thread.sleep(50);
-        } catch (InterruptedException e) {
-        }
-    }
-
-    private Thread[] threads(ThreadGroup parent) {
-        // No API to get the count of immediate children only ?
-        int count = parent.activeCount();
-        Thread[] all = new Thread[count];
-        int actualSize = parent.enumerate(all, false);
-        Thread[] result;
-        if (actualSize == all.length) {
-            result = all;
-        } else {
-            result = new Thread[actualSize];
-            System.arraycopy(all, 0, result, 0, actualSize);
+    /*
+     * Test for method void java.lang.ThreadGroup.uncaughtException(java.lang.Thread,
+     * java.lang.Throwable)
+     * Tests an exception thrown by the handler itself.
+     */
+    public void test_uncaughtException_exceptionInUncaughtException() {
+        // Our own uncaught exception classes
+        class UncaughtException extends RuntimeException {
+            private static final long serialVersionUID = 1L;
         }
 
-        return result;
+        ThreadGroup testRoot = new ThreadGroup(initialThreadGroup,
+                "Test Exception in uncaught exception") {
+            @Override
+            public void uncaughtException(Thread t, Throwable e) {
+                // This should be no-op according to the spec
+                throw new UncaughtException();
+            }
+        };
 
+        Thread thread = new Thread(testRoot, null, "no-op thread") {
+            @Override
+            public void run() {
+                throw new RuntimeException();
+            }
+        };
+        thread.start();
+        waitForThreadToDieUninterrupted(thread);
+        testThreadDefaultUncaughtExceptionHandler.assertWasNotCalled();
+        testRoot.destroy();
     }
 
-    private ThreadGroup getInitialThreadGroup() {
-        return initialThreadGroup;
-    }
-
-    private ThreadGroup[] allGroups(ThreadGroup parent) {
+    private static ThreadGroup[] allGroups(ThreadGroup parent) {
         int count = parent.activeGroupCount();
         ThreadGroup[] all = new ThreadGroup[count];
         parent.enumerate(all, true);
         return all;
     }
 
-    private void daemonTests() {
-        // Test for method void java.lang.ThreadGroup.setDaemon(boolean)
-
-        final ThreadGroup originalCurrent = getInitialThreadGroup();
-        final ThreadGroup testRoot = new ThreadGroup(originalCurrent,
-                "Test group");
-
-        testRoot.setDaemon(true);
-        assertTrue("Setting daemon&getting does not work", testRoot.isDaemon());
-
-        testRoot.setDaemon(false);
-        assertTrue("Setting daemon&getting does not work", !testRoot.isDaemon());
-
-        testRoot.destroy();
-
-    }
-
-    private boolean wipeAllThreads(final ThreadGroup aGroup) {
-        boolean ok = true;
-        Thread[] threads = threads(aGroup);
-        for (Thread t : threads) {
-            ok = ok && wipeThread(t);
-        }
-
-        // Recursively for subgroups (if any)
-        ThreadGroup[] children = groups(aGroup);
-        for (ThreadGroup element : children) {
-            ok = ok && wipeAllThreads(element);
-        }
-
-        return ok;
-
-    }
-
-    private boolean wipeSideEffectThreads(ThreadGroup aGroup) {
-        boolean ok = true;
-        Thread[] threads = threads(aGroup);
-        for (Thread t : threads) {
-            if (t.getName().equals("SimpleThread")
-                    || t.getName().equals("Bogus Name")
-                    || t.getName().equals("Testing")
-                    || t.getName().equals("foo")
-                    || t.getName().equals("Test Group")
-                    || t.getName().equals("Squawk")
-                    || t.getName().equals("Thread-1")
-                    || t.getName().equals("firstOne")
-                    || t.getName().equals("secondOne")
-                    || t.getName().equals("Thread-16")
-                    || t.getName().equals("Thread-14")) {
-                ok = ok && wipeThread(t);
-            }
-        }
-
-        // Recursively for subgroups (if any)
-        ThreadGroup[] children = groups(aGroup);
-
-        for (ThreadGroup element : children) {
-            ok = ok && wipeSideEffectThreads(element);
-            if (element.getName().equals("Test Group")
-                    || element.getName().equals("foo")
-                    || element.getName().equals("jp")) {
-                element.destroy();
-            }
-        }
-        try {
-            // Give the threads a chance to die.
-            Thread.sleep(50);
-        } catch (InterruptedException e) {
-        }
-        return ok;
-    }
-
-    private void asyncBuildRandomTreeUnder(final ThreadGroup aGroup,
+    private static void asyncBuildRandomTreeUnder(final ThreadGroup aGroup,
             final int depth, final Vector<ThreadGroup> allCreated) {
         if (depth <= 0) {
             return;
@@ -969,7 +690,7 @@
 
     }
 
-    private Vector<ThreadGroup> asyncBuildRandomTreeUnder(final ThreadGroup aGroup,
+    private static Vector<ThreadGroup> asyncBuildRandomTreeUnder(final ThreadGroup aGroup,
             final int depth) {
         Vector<ThreadGroup> result = new Vector<ThreadGroup>();
         asyncBuildRandomTreeUnder(aGroup, depth, result);
@@ -977,19 +698,7 @@
 
     }
 
-    private boolean allSuspended(Vector<MyThread> threads) {
-        for (int i = 0; i < threads.size(); i++) {
-            MyThread t = threads.elementAt(i);
-            if (t.isActivelyRunning()) {
-                return false;
-            }
-        }
-
-        return true;
-
-    }
-
-    private ThreadGroup[] groups(ThreadGroup parent) {
+    private static ThreadGroup[] groups(ThreadGroup parent) {
         // No API to get the count of immediate children only ?
         int count = parent.activeGroupCount();
         ThreadGroup[] all = new ThreadGroup[count];
@@ -1013,57 +722,11 @@
 
     }
 
-    private Vector<MyThread> populateGroupsWithThreads(final ThreadGroup aGroup,
-            final int threadCount) {
-        Vector<MyThread> result = new Vector<MyThread>();
-        populateGroupsWithThreads(aGroup, threadCount, result);
-        return result;
-
-    }
-
-    private void populateGroupsWithThreads(final ThreadGroup aGroup,
-            final int threadCount, final Vector<MyThread> allCreated) {
-        for (int i = 0; i < threadCount; i++) {
-            final int iClone = i;
-            final String name = "(MyThread)N =" + iClone + "/" + threadCount
-                    + " ,Vector size at creation: " + allCreated.size();
-
-            MyThread t = new MyThread(aGroup, name);
-            allCreated.addElement(t);
-        }
-
-        // Recursively for subgroups (if any)
-        ThreadGroup[] children = groups(aGroup);
-        for (ThreadGroup element : children) {
-            populateGroupsWithThreads(element, threadCount, allCreated);
-        }
-
-    }
-
-    private int random(int max) {
-
+    private static int random(int max) {
         return 1 + ((new Object()).hashCode() % max);
-
     }
 
-    @SuppressWarnings("deprecation")
-    private boolean wipeThread(Thread t) {
-        t.stop();
-        try {
-            t.join(1000);
-        } catch (InterruptedException ie) {
-            fail("Should not have been interrupted");
-        }
-        // The thread had plenty (subjective) of time to die so there
-        // is a problem.
-        if (t.isAlive()) {
-            return false;
-        }
-
-        return true;
-    }
-
-    private Vector<ThreadGroup> buildRandomTreeUnder(ThreadGroup aGroup, int depth) {
+    private static Vector<ThreadGroup> buildRandomTreeUnder(ThreadGroup aGroup, int depth) {
         Vector<ThreadGroup> result = asyncBuildRandomTreeUnder(aGroup, depth);
         while (true) {
             int sizeBefore = result.size();
@@ -1087,24 +750,46 @@
 
     }
 
-    private boolean arrayIncludes(Object[] array, Object toTest) {
+    private static boolean arrayIncludes(Object[] array, Object toTest) {
         for (Object element : array) {
             if (element == toTest) {
                 return true;
             }
         }
-
         return false;
     }
 
-    protected void myassertTrue(String msg, boolean b) {
-        // This method is defined here just to solve a visibility problem
-        // of protected methods with inner types
-        assertTrue(msg, b);
+    private static void waitForThreadToDieUninterrupted(Thread thread) {
+        try {
+            thread.join();
+        } catch (InterruptedException ie) {
+            fail("Should not have been interrupted");
+        }
     }
 
-    private ThreadGroup getRootThreadGroup() {
-        return rootThreadGroup;
+    private static class TestThreadDefaultUncaughtExceptionHandler
+            implements Thread.UncaughtExceptionHandler {
 
+        private boolean called;
+        private Throwable ex;
+        private Thread thread;
+
+        @Override
+        public void uncaughtException(Thread thread, Throwable ex) {
+            this.called = true;
+            this.thread = thread;
+            this.ex = ex;
+        }
+
+        public void assertWasCalled(Thread thread, Throwable ex) {
+            assertTrue(called);
+            assertSame(this.thread, thread);
+            assertSame(this.ex, ex);
+        }
+
+        public void assertWasNotCalled() {
+            assertFalse(called);
+        }
     }
+
 }
diff --git a/luni/src/main/java/java/io/FileInputStream.java b/luni/src/main/java/java/io/FileInputStream.java
index 644f749..5debe64 100644
--- a/luni/src/main/java/java/io/FileInputStream.java
+++ b/luni/src/main/java/java/io/FileInputStream.java
@@ -21,10 +21,8 @@
 
 import java.nio.NioUtils;
 import java.nio.channels.FileChannel;
-import java.util.Arrays;
 import libcore.io.ErrnoException;
 import libcore.io.IoBridge;
-import libcore.io.IoUtils;
 import libcore.io.Libcore;
 import libcore.io.Streams;
 import static libcore.io.OsConstants.*;
@@ -118,7 +116,7 @@
                 channel.close();
             }
             if (shouldClose) {
-                IoUtils.close(fd);
+                IoBridge.closeAndSignalBlockedThreads(fd);
             } else {
                 // An owned fd has been invalidated by IoUtils.close, but
                 // we need to explicitly stop using an unowned fd (http://b/4361076).
diff --git a/luni/src/main/java/java/io/FileOutputStream.java b/luni/src/main/java/java/io/FileOutputStream.java
index f5ba11e..e796e80 100644
--- a/luni/src/main/java/java/io/FileOutputStream.java
+++ b/luni/src/main/java/java/io/FileOutputStream.java
@@ -20,9 +20,8 @@
 import dalvik.system.CloseGuard;
 import java.nio.NioUtils;
 import java.nio.channels.FileChannel;
-import java.util.Arrays;
 import libcore.io.IoBridge;
-import libcore.io.IoUtils;
+
 import static libcore.io.OsConstants.*;
 
 /**
@@ -136,7 +135,7 @@
                 channel.close();
             }
             if (shouldClose) {
-                IoUtils.close(fd);
+                IoBridge.closeAndSignalBlockedThreads(fd);
             } else {
                 // An owned fd has been invalidated by IoUtils.close, but
                 // we need to explicitly stop using an unowned fd (http://b/4361076).
diff --git a/luni/src/main/java/java/io/RandomAccessFile.java b/luni/src/main/java/java/io/RandomAccessFile.java
index 6e88fe0..eac7641 100644
--- a/luni/src/main/java/java/io/RandomAccessFile.java
+++ b/luni/src/main/java/java/io/RandomAccessFile.java
@@ -25,7 +25,6 @@
 import java.util.Arrays;
 import libcore.io.ErrnoException;
 import libcore.io.IoBridge;
-import libcore.io.IoUtils;
 import libcore.io.Libcore;
 import libcore.io.Memory;
 import libcore.io.SizeOf;
@@ -163,7 +162,7 @@
                 channel.close();
                 channel = null;
             }
-            IoUtils.close(fd);
+            IoBridge.closeAndSignalBlockedThreads(fd);
         }
     }
 
diff --git a/luni/src/main/java/java/net/InetAddress.java b/luni/src/main/java/java/net/InetAddress.java
index 98ad098..885b472 100644
--- a/luni/src/main/java/java/net/InetAddress.java
+++ b/luni/src/main/java/java/net/InetAddress.java
@@ -28,8 +28,6 @@
 import java.nio.ByteOrder;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Comparator;
-import java.util.Enumeration;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -734,7 +732,7 @@
             }
         }
 
-        IoBridge.closeSocket(fd);
+        IoBridge.closeAndSignalBlockedThreads(fd);
 
         return reached;
     }
diff --git a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
index edf7024..a9ade28 100644
--- a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
@@ -78,7 +78,7 @@
     public synchronized void close() {
         guard.close();
         try {
-            IoBridge.closeSocket(fd);
+            IoBridge.closeAndSignalBlockedThreads(fd);
         } catch (IOException ignored) {
         }
     }
diff --git a/luni/src/main/java/java/net/PlainSocketImpl.java b/luni/src/main/java/java/net/PlainSocketImpl.java
index d81303f..e3988ed 100644
--- a/luni/src/main/java/java/net/PlainSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainSocketImpl.java
@@ -145,7 +145,7 @@
     @Override
     protected synchronized void close() throws IOException {
         guard.close();
-        IoBridge.closeSocket(fd);
+        IoBridge.closeAndSignalBlockedThreads(fd);
     }
 
     @Override
diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java
index 7c92d83..a303903 100644
--- a/luni/src/main/java/java/nio/DatagramChannelImpl.java
+++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java
@@ -519,7 +519,7 @@
     @Override protected synchronized void implCloseSelectableChannel() throws IOException {
         // A closed channel is not connected.
         onDisconnect(true /* updateSocketState */);
-        IoBridge.closeSocket(fd);
+        IoBridge.closeAndSignalBlockedThreads(fd);
         multicastMembershipHandler = null;
 
         if (socket != null && !socket.isClosed()) {
diff --git a/luni/src/main/java/java/nio/FileChannelImpl.java b/luni/src/main/java/java/nio/FileChannelImpl.java
index 6206d1b..9a47706 100644
--- a/luni/src/main/java/java/nio/FileChannelImpl.java
+++ b/luni/src/main/java/java/nio/FileChannelImpl.java
@@ -50,7 +50,7 @@
         }
     };
 
-    private final Object stream;
+    private final Closeable ioObject;
     private final FileDescriptor fd;
     private final int mode;
 
@@ -61,9 +61,9 @@
      * Create a new file channel implementation class that wraps the given
      * fd and operates in the specified mode.
      */
-    public FileChannelImpl(Object stream, FileDescriptor fd, int mode) {
+    public FileChannelImpl(Closeable ioObject, FileDescriptor fd, int mode) {
         this.fd = fd;
-        this.stream = stream;
+        this.ioObject = ioObject;
         this.mode = mode;
     }
 
@@ -86,9 +86,7 @@
     }
 
     protected void implCloseChannel() throws IOException {
-        if (stream instanceof Closeable) {
-            ((Closeable) stream).close();
-        }
+        ioObject.close();
     }
 
     private FileLock basicLock(long position, long size, boolean shared, boolean wait) throws IOException {
diff --git a/luni/src/main/java/java/nio/NioUtils.java b/luni/src/main/java/java/nio/NioUtils.java
index 34af76b..51adddb 100644
--- a/luni/src/main/java/java/nio/NioUtils.java
+++ b/luni/src/main/java/java/nio/NioUtils.java
@@ -16,6 +16,7 @@
 
 package java.nio;
 
+import java.io.Closeable;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.net.SocketOption;
@@ -48,8 +49,8 @@
     /**
      * Helps bridge between io and nio.
      */
-    public static FileChannel newFileChannel(Object stream, FileDescriptor fd, int mode) {
-        return new FileChannelImpl(stream, fd, mode);
+    public static FileChannel newFileChannel(Closeable ioObject, FileDescriptor fd, int mode) {
+        return new FileChannelImpl(ioObject, fd, mode);
     }
 
     /**
diff --git a/luni/src/main/java/java/nio/SelectorImpl.java b/luni/src/main/java/java/nio/SelectorImpl.java
index d63fa63..3495523 100644
--- a/luni/src/main/java/java/nio/SelectorImpl.java
+++ b/luni/src/main/java/java/nio/SelectorImpl.java
@@ -17,18 +17,15 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.io.InterruptedIOException;
 import java.nio.channels.ClosedSelectorException;
 import java.nio.channels.IllegalSelectorException;
-import java.nio.channels.SelectableChannel;
 import java.nio.channels.SelectionKey;
-import static java.nio.channels.SelectionKey.*;
 import java.nio.channels.Selector;
-import java.nio.channels.SocketChannel;
 import java.nio.channels.spi.AbstractSelectableChannel;
 import java.nio.channels.spi.AbstractSelectionKey;
 import java.nio.channels.spi.AbstractSelector;
 import java.nio.channels.spi.SelectorProvider;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -40,8 +37,15 @@
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
 import libcore.io.StructPollfd;
-import libcore.util.EmptyArray;
-import static libcore.io.OsConstants.*;
+
+import static java.nio.channels.SelectionKey.OP_ACCEPT;
+import static java.nio.channels.SelectionKey.OP_CONNECT;
+import static java.nio.channels.SelectionKey.OP_READ;
+import static java.nio.channels.SelectionKey.OP_WRITE;
+import static libcore.io.OsConstants.EINTR;
+import static libcore.io.OsConstants.POLLHUP;
+import static libcore.io.OsConstants.POLLIN;
+import static libcore.io.OsConstants.POLLOUT;
 
 /*
  * Default implementation of java.nio.channels.Selector
@@ -321,6 +325,7 @@
         try {
             Libcore.os.write(wakeupOut, new byte[] { 1 }, 0, 1);
         } catch (ErrnoException ignored) {
+        } catch (InterruptedIOException ignored) {
         }
         return this;
     }
diff --git a/luni/src/main/java/java/nio/SocketChannelImpl.java b/luni/src/main/java/java/nio/SocketChannelImpl.java
index d7cd562..e4e92f3 100644
--- a/luni/src/main/java/java/nio/SocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/SocketChannelImpl.java
@@ -520,9 +520,9 @@
     protected synchronized void implCloseSelectableChannel() throws IOException {
         if (status != SOCKET_STATUS_CLOSED) {
             status = SOCKET_STATUS_CLOSED;
-            // IoBridge.closeSocket(fd) is idempotent: It is safe to call on an already-closed file
-            // descriptor.
-            IoBridge.closeSocket(fd);
+            // IoBridge.closeAndSignalBlockedThreads(fd) is idempotent: It is safe to call on an
+            // already-closed file descriptor.
+            IoBridge.closeAndSignalBlockedThreads(fd);
             if (socket != null && !socket.isClosed()) {
                 socket.onClose();
             }
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
index 05a756f..eaa7f0c 100644
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -19,6 +19,7 @@
 import dalvik.system.BlockGuard;
 import dalvik.system.SocketTagger;
 import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.SocketException;
@@ -158,6 +159,11 @@
         os.mkdir(path, mode);
     }
 
+    @Override public void mkfifo(String path, int mode) throws ErrnoException {
+        BlockGuard.getThreadPolicy().onWriteToDisk();
+        os.mkfifo(path, mode);
+    }
+
     @Override public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
         BlockGuard.getThreadPolicy().onReadFromDisk();
         if ((mode & O_ACCMODE) != O_RDONLY) {
@@ -180,32 +186,32 @@
         os.posix_fallocate(fd, offset, length);
     }
 
-    @Override public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException {
+    @Override public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
         BlockGuard.getThreadPolicy().onReadFromDisk();
         return os.pread(fd, buffer, offset);
     }
 
-    @Override public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException {
+    @Override public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
         BlockGuard.getThreadPolicy().onReadFromDisk();
         return os.pread(fd, bytes, byteOffset, byteCount, offset);
     }
 
-    @Override public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException {
+    @Override public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
         return os.pwrite(fd, buffer, offset);
     }
 
-    @Override public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException {
+    @Override public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
         return os.pwrite(fd, bytes, byteOffset, byteCount, offset);
     }
 
-    @Override public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
+    @Override public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
         BlockGuard.getThreadPolicy().onReadFromDisk();
         return os.read(fd, buffer);
     }
 
-    @Override public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
+    @Override public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
         BlockGuard.getThreadPolicy().onReadFromDisk();
         return os.read(fd, bytes, byteOffset, byteCount);
     }
@@ -215,7 +221,7 @@
       return os.readlink(path);
     }
 
-    @Override public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException {
+    @Override public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException {
         BlockGuard.getThreadPolicy().onReadFromDisk();
         return os.readv(fd, buffers, offsets, byteCounts);
     }
@@ -283,17 +289,17 @@
         os.symlink(oldPath, newPath);
     }
 
-    @Override public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
+    @Override public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
         return os.write(fd, buffer);
     }
 
-    @Override public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
+    @Override public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
         return os.write(fd, bytes, byteOffset, byteCount);
     }
 
-    @Override public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException {
+    @Override public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
         return os.writev(fd, buffers, offsets, byteCounts);
     }
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index 2de35e9..0b49071 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -17,6 +17,7 @@
 package libcore.io;
 
 import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
@@ -90,6 +91,7 @@
     public StructStat lstat(String path) throws ErrnoException { return os.lstat(path); }
     public void mincore(long address, long byteCount, byte[] vector) throws ErrnoException { os.mincore(address, byteCount, vector); }
     public void mkdir(String path, int mode) throws ErrnoException { os.mkdir(path, mode); }
+    public void mkfifo(String path, int mode) throws ErrnoException { os.mkfifo(path, mode); }
     public void mlock(long address, long byteCount) throws ErrnoException { os.mlock(address, byteCount); }
     public long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException { return os.mmap(address, byteCount, prot, flags, fd, offset); }
     public void msync(long address, long byteCount, int flags) throws ErrnoException { os.msync(address, byteCount, flags); }
@@ -99,14 +101,14 @@
     public FileDescriptor[] pipe() throws ErrnoException { return os.pipe(); }
     public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException { return os.poll(fds, timeoutMs); }
     public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException { os.posix_fallocate(fd, offset, length); }
-    public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException { return os.pread(fd, buffer, offset); }
-    public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException { return os.pread(fd, bytes, byteOffset, byteCount, offset); }
-    public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException { return os.pwrite(fd, buffer, offset); }
-    public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException { return os.pwrite(fd, bytes, byteOffset, byteCount, offset); }
-    public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { return os.read(fd, buffer); }
-    public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException { return os.read(fd, bytes, byteOffset, byteCount); }
+    public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { return os.pread(fd, buffer, offset); }
+    public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { return os.pread(fd, bytes, byteOffset, byteCount, offset); }
+    public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { return os.pwrite(fd, buffer, offset); }
+    public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { return os.pwrite(fd, bytes, byteOffset, byteCount, offset); }
+    public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { return os.read(fd, buffer); }
+    public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { return os.read(fd, bytes, byteOffset, byteCount); }
     public String readlink(String path) throws ErrnoException { return os.readlink(path); }
-    public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException { return os.readv(fd, buffers, offsets, byteCounts); }
+    public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException { return os.readv(fd, buffers, offsets, byteCounts); }
     public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, buffer, flags, srcAddress); }
     public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
     public void remove(String path) throws ErrnoException { os.remove(path); }
@@ -143,7 +145,7 @@
     public StructUtsname uname() { return os.uname(); }
     public void unsetenv(String name) throws ErrnoException { os.unsetenv(name); }
     public int waitpid(int pid, MutableInt status, int options) throws ErrnoException { return os.waitpid(pid, status, options); }
-    public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { return os.write(fd, buffer); }
-    public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException { return os.write(fd, bytes, byteOffset, byteCount); }
-    public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException { return os.writev(fd, buffers, offsets, byteCounts); }
+    public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { return os.write(fd, buffer); }
+    public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { return os.write(fd, bytes, byteOffset, byteCount); }
+    public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException { return os.writev(fd, buffers, offsets, byteCounts); }
 }
diff --git a/luni/src/main/java/libcore/io/IoBridge.java b/luni/src/main/java/libcore/io/IoBridge.java
index 08d4837..89070cd 100644
--- a/luni/src/main/java/libcore/io/IoBridge.java
+++ b/luni/src/main/java/libcore/io/IoBridge.java
@@ -177,9 +177,15 @@
         return detail;
     }
 
-    public static void closeSocket(FileDescriptor fd) throws IOException {
-        if (!fd.valid()) {
-            // Socket.close doesn't throw if you try to close an already-closed socket.
+    /**
+     * Closes the supplied file descriptor and sends a signal to any threads are currently blocking.
+     * In order for the signal to be sent the blocked threads must have registered with
+     * the AsynchronousCloseMonitor before they entered the blocking operation.
+     *
+     * <p>This method is a no-op if passed a {@code null} or already-closed file descriptor.
+     */
+    public static void closeAndSignalBlockedThreads(FileDescriptor fd) throws IOException {
+        if (fd == null || !fd.valid()) {
             return;
         }
         int intFd = fd.getInt$();
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index a4541a7..2065c70 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -17,6 +17,7 @@
 package libcore.io;
 
 import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
@@ -82,6 +83,7 @@
     public StructStat lstat(String path) throws ErrnoException;
     public void mincore(long address, long byteCount, byte[] vector) throws ErrnoException;
     public void mkdir(String path, int mode) throws ErrnoException;
+    public void mkfifo(String path, int mode) throws ErrnoException;
     public void mlock(long address, long byteCount) throws ErrnoException;
     public long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException;
     public void msync(long address, long byteCount, int flags) throws ErrnoException;
@@ -92,14 +94,14 @@
     /* TODO: if we used the non-standard ppoll(2) behind the scenes, we could take a long timeout. */
     public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException;
     public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException;
-    public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException;
-    public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException;
-    public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException;
-    public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException;
-    public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
-    public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException;
+    public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException;
+    public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException;
+    public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException;
+    public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException;
+    public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException;
+    public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException;
     public String readlink(String path) throws ErrnoException;
-    public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
+    public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
     public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
     public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
     public void remove(String path) throws ErrnoException;
@@ -136,7 +138,7 @@
     public StructUtsname uname();
     public void unsetenv(String name) throws ErrnoException;
     public int waitpid(int pid, MutableInt status, int options) throws ErrnoException;
-    public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
-    public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException;
-    public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
+    public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException;
+    public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException;
+    public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
 }
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index c6f13ea..a5c3eb0 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -17,6 +17,7 @@
 package libcore.io;
 
 import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
@@ -84,6 +85,7 @@
     public native StructStat lstat(String path) throws ErrnoException;
     public native void mincore(long address, long byteCount, byte[] vector) throws ErrnoException;
     public native void mkdir(String path, int mode) throws ErrnoException;
+    public native void mkfifo(String path, int mode) throws ErrnoException;
     public native void mlock(long address, long byteCount) throws ErrnoException;
     public native long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException;
     public native void msync(long address, long byteCount, int flags) throws ErrnoException;
@@ -93,44 +95,44 @@
     public native FileDescriptor[] pipe() throws ErrnoException;
     public native int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException;
     public native void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException;
-    public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException {
+    public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
         if (buffer.isDirect()) {
             return preadBytes(fd, buffer, buffer.position(), buffer.remaining(), offset);
         } else {
             return preadBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), offset);
         }
     }
-    public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException {
+    public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
         // This indirection isn't strictly necessary, but ensures that our public interface is type safe.
         return preadBytes(fd, bytes, byteOffset, byteCount, offset);
     }
-    private native int preadBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException;
-    public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException {
+    private native int preadBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException;
+    public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
         if (buffer.isDirect()) {
             return pwriteBytes(fd, buffer, buffer.position(), buffer.remaining(), offset);
         } else {
             return pwriteBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), offset);
         }
     }
-    public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException {
+    public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
         // This indirection isn't strictly necessary, but ensures that our public interface is type safe.
         return pwriteBytes(fd, bytes, byteOffset, byteCount, offset);
     }
-    private native int pwriteBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException;
-    public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
+    private native int pwriteBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException;
+    public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
         if (buffer.isDirect()) {
             return readBytes(fd, buffer, buffer.position(), buffer.remaining());
         } else {
             return readBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining());
         }
     }
-    public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
+    public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
         // This indirection isn't strictly necessary, but ensures that our public interface is type safe.
         return readBytes(fd, bytes, byteOffset, byteCount);
     }
-    private native int readBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException;
+    private native int readBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException, InterruptedIOException;
     public native String readlink(String path) throws ErrnoException;
-    public native int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
+    public native int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
     public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
         if (buffer.isDirect()) {
             return recvfromBytes(fd, buffer, buffer.position(), buffer.remaining(), flags, srcAddress);
@@ -193,17 +195,17 @@
     public native StructUtsname uname();
     public native void unsetenv(String name) throws ErrnoException;
     public native int waitpid(int pid, MutableInt status, int options) throws ErrnoException;
-    public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
+    public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
         if (buffer.isDirect()) {
             return writeBytes(fd, buffer, buffer.position(), buffer.remaining());
         } else {
             return writeBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining());
         }
     }
-    public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
+    public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
         // This indirection isn't strictly necessary, but ensures that our public interface is type safe.
         return writeBytes(fd, bytes, byteOffset, byteCount);
     }
-    private native int writeBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException;
-    public native int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
+    private native int writeBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException, InterruptedIOException;
+    public native int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
 }
diff --git a/luni/src/main/native/AsynchronousSocketCloseMonitor.cpp b/luni/src/main/native/AsynchronousCloseMonitor.cpp
similarity index 76%
rename from luni/src/main/native/AsynchronousSocketCloseMonitor.cpp
rename to luni/src/main/native/AsynchronousCloseMonitor.cpp
index 9617e9d..92d5260 100644
--- a/luni/src/main/native/AsynchronousSocketCloseMonitor.cpp
+++ b/luni/src/main/native/AsynchronousCloseMonitor.cpp
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "AsynchronousSocketCloseMonitor"
+#define LOG_TAG "AsynchronousCloseMonitor"
 
-#include "AsynchronousSocketCloseMonitor.h"
+#include "AsynchronousCloseMonitor.h"
 #include "cutils/log.h"
 
 #include <errno.h>
@@ -27,12 +27,12 @@
  * We use an intrusive doubly-linked list to keep track of blocked threads.
  * This gives us O(1) insertion and removal, and means we don't need to do any allocation.
  * (The objects themselves are stack-allocated.)
- * Waking potentially-blocked threads when a socket is closed is O(n) in the total number of
- * blocked threads (not the number of threads actually blocked on the socket in question).
- * For now at least, this seems like a good compromise for Android.
+ * Waking potentially-blocked threads when a file descriptor is closed is O(n) in the total number
+ * of blocked threads (not the number of threads actually blocked on the file descriptor in
+ * question). For now at least, this seems like a good compromise for Android.
  */
 static pthread_mutex_t blockedThreadListMutex = PTHREAD_MUTEX_INITIALIZER;
-static AsynchronousSocketCloseMonitor* blockedThreadList = NULL;
+static AsynchronousCloseMonitor* blockedThreadList = NULL;
 
 /**
  * The specific signal chosen here is arbitrary.
@@ -47,7 +47,7 @@
     // Do nothing. We only sent this signal for its side-effect of interrupting syscalls.
 }
 
-void AsynchronousSocketCloseMonitor::init() {
+void AsynchronousCloseMonitor::init() {
     // Ensure that the signal we send interrupts system calls but doesn't kill threads.
     // Using sigaction(2) lets us ensure that the SA_RESTART flag is not set.
     // (The whole reason we're sending this signal is to unblock system calls!)
@@ -61,21 +61,27 @@
     }
 }
 
-void AsynchronousSocketCloseMonitor::signalBlockedThreads(int fd) {
+void AsynchronousCloseMonitor::signalBlockedThreads(int fd) {
     ScopedPthreadMutexLock lock(&blockedThreadListMutex);
-    for (AsynchronousSocketCloseMonitor* it = blockedThreadList; it != NULL; it = it->mNext) {
+    for (AsynchronousCloseMonitor* it = blockedThreadList; it != NULL; it = it->mNext) {
         if (it->mFd == fd) {
+            it->mSignaled = true;
             pthread_kill(it->mThread, BLOCKED_THREAD_SIGNAL);
             // Keep going, because there may be more than one thread...
         }
     }
 }
 
-AsynchronousSocketCloseMonitor::AsynchronousSocketCloseMonitor(int fd) {
+bool AsynchronousCloseMonitor::wasSignaled() const {
+    return mSignaled;
+}
+
+AsynchronousCloseMonitor::AsynchronousCloseMonitor(int fd) {
     ScopedPthreadMutexLock lock(&blockedThreadListMutex);
     // Who are we, and what are we waiting for?
     mThread = pthread_self();
     mFd = fd;
+    mSignaled = false;
     // Insert ourselves at the head of the intrusive doubly-linked list...
     mPrev = NULL;
     mNext = blockedThreadList;
@@ -85,7 +91,7 @@
     blockedThreadList = this;
 }
 
-AsynchronousSocketCloseMonitor::~AsynchronousSocketCloseMonitor() {
+AsynchronousCloseMonitor::~AsynchronousCloseMonitor() {
     ScopedPthreadMutexLock lock(&blockedThreadListMutex);
     // Unlink ourselves from the intrusive doubly-linked list...
     if (mNext != NULL) {
diff --git a/luni/src/main/native/AsynchronousCloseMonitor.h b/luni/src/main/native/AsynchronousCloseMonitor.h
new file mode 100644
index 0000000..eefbbdf
--- /dev/null
+++ b/luni/src/main/native/AsynchronousCloseMonitor.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef ASYNCHRONOUS_CLOSE_MONITOR_H_included
+#define ASYNCHRONOUS_CLOSE_MONITOR_H_included
+
+#include "ScopedPthreadMutexLock.h"
+#include <pthread.h>
+
+/**
+ * AsynchronousCloseMonitor helps implement Java's asynchronous close semantics.
+ *
+ * AsynchronousCloseMonitor::init must be called before anything else.
+ *
+ * Every blocking I/O operation must be surrounded by an AsynchronousCloseMonitor
+ * instance. For example:
+ *
+ *   {
+ *     AsynchronousCloseMonitor monitor(fd);
+ *     byteCount = ::read(fd, buf, sizeof(buf));
+ *   }
+ *
+ * To interrupt all threads currently blocked on file descriptor 'fd', call signalBlockedThreads:
+ *
+ *   AsynchronousCloseMonitor::signalBlockedThreads(fd);
+ *
+ * To test to see if the interruption was due to the signalBlockedThreads call:
+ *
+ *   monitor.wasSignaled();
+ */
+class AsynchronousCloseMonitor {
+public:
+    AsynchronousCloseMonitor(int fd);
+    ~AsynchronousCloseMonitor();
+    bool wasSignaled() const;
+
+    static void init();
+
+    static void signalBlockedThreads(int fd);
+
+private:
+    AsynchronousCloseMonitor* mPrev;
+    AsynchronousCloseMonitor* mNext;
+    pthread_t mThread;
+    int mFd;
+    bool mSignaled;
+
+    // Disallow copy and assignment.
+    AsynchronousCloseMonitor(const AsynchronousCloseMonitor&);
+    void operator=(const AsynchronousCloseMonitor&);
+};
+
+#endif  // ASYNCHRONOUS_CLOSE_MONITOR_H_included
diff --git a/luni/src/main/native/AsynchronousSocketCloseMonitor.h b/luni/src/main/native/AsynchronousSocketCloseMonitor.h
deleted file mode 100644
index 3370e22..0000000
--- a/luni/src/main/native/AsynchronousSocketCloseMonitor.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#ifndef ASYNCHRONOUS_SOCKET_CLOSE_MONITOR_H_included
-#define ASYNCHRONOUS_SOCKET_CLOSE_MONITOR_H_included
-
-#include "ScopedPthreadMutexLock.h"
-#include <pthread.h>
-
-/**
- * AsynchronousSocketCloseMonitor helps implement Java's asynchronous Socket.close semantics.
- *
- * AsynchronousSocketCloseMonitor::init must be called before anything else.
- *
- * Every blocking network I/O operation must be surrounded by an AsynchronousSocketCloseMonitor
- * instance. For example:
- *
- *   {
- *     AsynchronousSocketCloseMonitor monitor(fd);
- *     byteCount = ::read(fd, buf, sizeof(buf));
- *   }
- *
- * To interrupt all threads currently blocked on file descriptor 'fd', call signalBlockedThreads:
- *
- *   AsynchronousSocketCloseMonitor::signalBlockedThreads(fd);
- */
-class AsynchronousSocketCloseMonitor {
-public:
-    AsynchronousSocketCloseMonitor(int fd);
-    ~AsynchronousSocketCloseMonitor();
-
-    static void init();
-
-    static void signalBlockedThreads(int fd);
-
-private:
-    AsynchronousSocketCloseMonitor* mPrev;
-    AsynchronousSocketCloseMonitor* mNext;
-    pthread_t mThread;
-    int mFd;
-
-    // Disallow copy and assignment.
-    AsynchronousSocketCloseMonitor(const AsynchronousSocketCloseMonitor&);
-    void operator=(const AsynchronousSocketCloseMonitor&);
-};
-
-#endif  // ASYNCHRONOUS_SOCKET_CLOSE_MONITOR_H_included
diff --git a/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp b/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
index 4f50ce5..a27e7b8 100644
--- a/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
+++ b/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
@@ -16,20 +16,20 @@
 
 #define LOG_TAG "AsynchronousCloseMonitor"
 
-#include "AsynchronousSocketCloseMonitor.h"
+#include "AsynchronousCloseMonitor.h"
 #include "JNIHelp.h"
 #include "JniConstants.h"
 #include "jni.h"
 
 static void AsynchronousCloseMonitor_signalBlockedThreads(JNIEnv* env, jclass, jobject javaFd) {
     int fd = jniGetFDFromFileDescriptor(env, javaFd);
-    AsynchronousSocketCloseMonitor::signalBlockedThreads(fd);
+    AsynchronousCloseMonitor::signalBlockedThreads(fd);
 }
 
 static JNINativeMethod gMethods[] = {
     NATIVE_METHOD(AsynchronousCloseMonitor, signalBlockedThreads, "(Ljava/io/FileDescriptor;)V"),
 };
 void register_libcore_io_AsynchronousCloseMonitor(JNIEnv* env) {
-    AsynchronousSocketCloseMonitor::init();
+    AsynchronousCloseMonitor::init();
     jniRegisterNativeMethods(env, "libcore/io/AsynchronousCloseMonitor", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index b9f16a7..65ee36f 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "Posix"
 
-#include "AsynchronousSocketCloseMonitor.h"
+#include "AsynchronousCloseMonitor.h"
 #include "cutils/log.h"
 #include "ExecStrings.h"
 #include "JNIHelp.h"
@@ -68,31 +68,77 @@
 };
 
 /**
- * Used to retry networking system calls that can return EINTR. Unlike TEMP_FAILURE_RETRY,
- * this also handles the case where the reason for failure is that another thread called
- * Socket.close. This macro also throws exceptions on failure.
+ * Used to retry networking system calls that can be interrupted with a signal. Unlike
+ * TEMP_FAILURE_RETRY, this also handles the case where
+ * AsynchronousCloseMonitor::signalBlockedThreads(fd) is used to signal a close() or
+ * Thread.interrupt(). Other signals that result in an EINTR result are ignored and the system call
+ * is retried.
  *
- * Returns the result of 'exp', though a Java exception will be pending if the result is -1.
+ * Returns the result of the system call though a Java exception will be pending if the result is
+ * -1:  a SocketException if signaled via AsynchronousCloseMonitor, or ErrnoException for other
+ * failures.
  */
 #define NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ...) ({ \
     return_type _rc = -1; \
     do { \
+        bool _wasSignaled; \
+        int _syscallErrno; \
         { \
             int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \
-            AsynchronousSocketCloseMonitor _monitor(_fd); \
+            AsynchronousCloseMonitor _monitor(_fd); \
             _rc = syscall_name(_fd, __VA_ARGS__); \
+            _syscallErrno = errno; \
+            _wasSignaled = _monitor.wasSignaled(); \
         } \
-        if (_rc == -1) { \
-            if (jniGetFDFromFileDescriptor(jni_env, java_fd) == -1) { \
-                jniThrowException(jni_env, "java/net/SocketException", "Socket closed"); \
+        if (_wasSignaled) { \
+            jniThrowException(jni_env, "java/net/SocketException", "Socket closed"); \
+            break; \
+        } \
+        if (_rc == -1 && _syscallErrno != EINTR) { \
+            /* TODO: with a format string we could show the arguments too, like strace(1). */ \
+            throwErrnoException(jni_env, # syscall_name); \
+            break; \
+        } \
+    } while (_rc == -1); /* _syscallErrno == EINTR && !_wasSignaled */ \
+    _rc; })
+
+/**
+ * Used to retry system calls that can be interrupted with a signal. Unlike TEMP_FAILURE_RETRY, this
+ * also handles the case where AsynchronousCloseMonitor::signalBlockedThreads(fd) is used to signal
+ * a close() or Thread.interrupt(). Other signals that result in an EINTR result are ignored and the
+ * system call is retried.
+ *
+ * Returns the result of the system call though a Java exception will be pending if the result is
+ * -1: an IOException if the file descriptor is already closed, a InterruptedIOException if signaled
+ * via AsynchronousCloseMonitor, or ErrnoException for other failures.
+ */
+#define IO_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ...) ({ \
+    return_type _rc = -1; \
+    int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \
+    if (_fd == -1) { \
+        jniThrowException(jni_env, "java/io/IOException", "File descriptor closed"); \
+    } else { \
+        do { \
+            bool _wasSignaled; \
+            int _syscallErrno; \
+            { \
+                int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \
+                AsynchronousCloseMonitor _monitor(_fd); \
+                _rc = syscall_name(_fd, __VA_ARGS__); \
+                _syscallErrno = errno; \
+                _wasSignaled = _monitor.wasSignaled(); \
+            } \
+            if (_wasSignaled) { \
+                jniThrowException(jni_env, "java/io/InterruptedIOException", # syscall_name " interrupted"); \
                 break; \
-            } else if (errno != EINTR) { \
+            } \
+            if (_rc == -1 && _syscallErrno != EINTR) { \
                 /* TODO: with a format string we could show the arguments too, like strace(1). */ \
                 throwErrnoException(jni_env, # syscall_name); \
                 break; \
             } \
-        } \
-    } while (_rc == -1); \
+        } while (_rc == -1); /* && _syscallErrno == EINTR && !_wasSignaled */ \
+    } \
     _rc; })
 
 static void throwException(JNIEnv* env, jclass exceptionClass, jmethodID ctor3, jmethodID ctor2,
@@ -907,6 +953,14 @@
     throwIfMinusOne(env, "mkdir", TEMP_FAILURE_RETRY(mkdir(path.c_str(), mode)));
 }
 
+static void Posix_mkfifo(JNIEnv* env, jobject, jstring javaPath, jint mode) {
+    ScopedUtfChars path(env, javaPath);
+    if (path.c_str() == NULL) {
+        return;
+    }
+    throwIfMinusOne(env, "mkfifo", TEMP_FAILURE_RETRY(mkfifo(path.c_str(), mode)));
+}
+
 static void Posix_mlock(JNIEnv* env, jobject, jlong address, jlong byteCount) {
     void* ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(address));
     throwIfMinusOne(env, "mlock", TEMP_FAILURE_RETRY(mlock(ptr, byteCount)));
@@ -990,11 +1044,9 @@
         ++count;
     }
 
-    // Since we don't know which fds -- if any -- are sockets, be conservative and register
-    // all fds for asynchronous socket close monitoring.
-    std::vector<AsynchronousSocketCloseMonitor*> monitors;
+    std::vector<AsynchronousCloseMonitor*> monitors;
     for (size_t i = 0; i < count; ++i) {
-        monitors.push_back(new AsynchronousSocketCloseMonitor(fds[i].fd));
+        monitors.push_back(new AsynchronousCloseMonitor(fds[i].fd));
     }
     int rc = poll(fds.get(), count, timeoutMs);
     for (size_t i = 0; i < monitors.size(); ++i) {
@@ -1029,8 +1081,7 @@
     if (bytes.get() == NULL) {
         return -1;
     }
-    int fd = jniGetFDFromFileDescriptor(env, javaFd);
-    return throwIfMinusOne(env, "pread", TEMP_FAILURE_RETRY(pread64(fd, bytes.get() + byteOffset, byteCount, offset)));
+    return IO_FAILURE_RETRY(env, ssize_t, pread64, javaFd, bytes.get() + byteOffset, byteCount, offset);
 }
 
 static jint Posix_pwriteBytes(JNIEnv* env, jobject, jobject javaFd, jbyteArray javaBytes, jint byteOffset, jint byteCount, jlong offset) {
@@ -1038,8 +1089,7 @@
     if (bytes.get() == NULL) {
         return -1;
     }
-    int fd = jniGetFDFromFileDescriptor(env, javaFd);
-    return throwIfMinusOne(env, "pwrite", TEMP_FAILURE_RETRY(pwrite64(fd, bytes.get() + byteOffset, byteCount, offset)));
+    return IO_FAILURE_RETRY(env, ssize_t, pwrite64, javaFd, bytes.get() + byteOffset, byteCount, offset);
 }
 
 static jint Posix_readBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount) {
@@ -1047,8 +1097,7 @@
     if (bytes.get() == NULL) {
         return -1;
     }
-    int fd = jniGetFDFromFileDescriptor(env, javaFd);
-    return throwIfMinusOne(env, "read", TEMP_FAILURE_RETRY(read(fd, bytes.get() + byteOffset, byteCount)));
+    return IO_FAILURE_RETRY(env, ssize_t, read, javaFd, bytes.get() + byteOffset, byteCount);
 }
 
 static jstring Posix_readlink(JNIEnv* env, jobject, jstring javaPath) {
@@ -1070,8 +1119,7 @@
     if (!ioVec.init(buffers, offsets, byteCounts)) {
         return -1;
     }
-    int fd = jniGetFDFromFileDescriptor(env, javaFd);
-    return throwIfMinusOne(env, "readv", TEMP_FAILURE_RETRY(readv(fd, ioVec.get(), ioVec.size())));
+    return IO_FAILURE_RETRY(env, ssize_t, readv, javaFd, ioVec.get(), ioVec.size());
 }
 
 static jint Posix_recvfromBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaInetSocketAddress) {
@@ -1417,8 +1465,7 @@
     if (bytes.get() == NULL) {
         return -1;
     }
-    int fd = jniGetFDFromFileDescriptor(env, javaFd);
-    return throwIfMinusOne(env, "write", TEMP_FAILURE_RETRY(write(fd, bytes.get() + byteOffset, byteCount)));
+    return IO_FAILURE_RETRY(env, ssize_t, write, javaFd, bytes.get() + byteOffset, byteCount);
 }
 
 static jint Posix_writev(JNIEnv* env, jobject, jobject javaFd, jobjectArray buffers, jintArray offsets, jintArray byteCounts) {
@@ -1426,8 +1473,7 @@
     if (!ioVec.init(buffers, offsets, byteCounts)) {
         return -1;
     }
-    int fd = jniGetFDFromFileDescriptor(env, javaFd);
-    return throwIfMinusOne(env, "writev", TEMP_FAILURE_RETRY(writev(fd, ioVec.get(), ioVec.size())));
+    return IO_FAILURE_RETRY(env, ssize_t, writev, javaFd, ioVec.get(), ioVec.size());
 }
 
 static JNINativeMethod gMethods[] = {
@@ -1486,6 +1532,7 @@
     NATIVE_METHOD(Posix, lstat, "(Ljava/lang/String;)Llibcore/io/StructStat;"),
     NATIVE_METHOD(Posix, mincore, "(JJ[B)V"),
     NATIVE_METHOD(Posix, mkdir, "(Ljava/lang/String;I)V"),
+    NATIVE_METHOD(Posix, mkfifo, "(Ljava/lang/String;I)V"),
     NATIVE_METHOD(Posix, mlock, "(JJ)V"),
     NATIVE_METHOD(Posix, mmap, "(JJIILjava/io/FileDescriptor;J)J"),
     NATIVE_METHOD(Posix, msync, "(JJI)V"),
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index e8b6e4a..6bec778 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -4,7 +4,7 @@
 # or BUILD_*_LIBRARY.
 
 LOCAL_SRC_FILES := \
-    AsynchronousSocketCloseMonitor.cpp \
+    AsynchronousCloseMonitor.cpp \
     ExecStrings.cpp \
     IcuUtilities.cpp \
     JniException.cpp \
diff --git a/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java b/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java
index e5fd39f..30ae7eb 100755
--- a/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java
+++ b/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java
@@ -207,7 +207,10 @@
 
     private static void confirmInterrupted(Thread thread) throws InterruptedException {
         // validate and clear interrupted bit before join
-        assertTrue(Thread.interrupted());
-        thread.join();
+        try {
+            assertTrue(Thread.interrupted());
+        } finally {
+            thread.join();
+        }
     }
 }
diff --git a/luni/src/test/java/libcore/java/nio/channels/FileIOInterruptTest.java b/luni/src/test/java/libcore/java/nio/channels/FileIOInterruptTest.java
new file mode 100644
index 0000000..2ac8827
--- /dev/null
+++ b/luni/src/test/java/libcore/java/nio/channels/FileIOInterruptTest.java
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package libcore.java.nio.channels;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.ClosedByInterruptException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.FileChannel;
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+import libcore.io.OsConstants;
+
+import static libcore.io.IoUtils.closeQuietly;
+
+/**
+ * A test for file interrupt behavior. Because forcing a real file to block on read or write is
+ * difficult this test uses Unix FIFO / Named Pipes. FIFOs appear to Java as files but the test
+ * has more control over the available data. Reader will block until the other end writes, and
+ * writers can also be made to block.
+ *
+ * <p>Using FIFOs has a few drawbacks:
+ * <ol>
+ * <li>FIFOs are not supported from Java or the command-line on Android, so this test includes
+ * native code to create the FIFO.
+ * <li>FIFOs will not open() until there is both a reader and a writer of the FIFO; each test must
+ * always attach both ends or experience a blocked test.
+ * <li>FIFOs are not supported on some file systems. e.g. VFAT, so the test has to be particular
+ * about the temporary directory it uses to hold the FIFO.
+ * <li>Writes to FIFOs are buffered by the OS which makes blocking behavior more difficult to
+ * induce. See {@link ChannelWriter} and {@link StreamWriter}.
+ * </ol>
+ */
+public class FileIOInterruptTest extends TestCase {
+
+  private static File VOGAR_DEVICE_TEMP_DIR = new File("/data/data/file_io_interrupt_test");
+
+  private File fifoFile;
+
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+
+    // This test relies on a FIFO file. The file system must support FIFOs, so we check the path.
+    String tmpDirName = System.getProperty("java.io.tmpdir");
+    File tmpDir;
+    if (tmpDirName.startsWith("/sdcard")) {
+      // Vogar execution on device runs in /sdcard. Unfortunately the file system used does not
+      // support FIFOs so the test must use one that is more likely to work.
+      if (!VOGAR_DEVICE_TEMP_DIR.exists()) {
+        assertTrue(VOGAR_DEVICE_TEMP_DIR.mkdir());
+      }
+      VOGAR_DEVICE_TEMP_DIR.deleteOnExit();
+      tmpDir = VOGAR_DEVICE_TEMP_DIR;
+    } else {
+      tmpDir = new File(tmpDirName);
+    }
+    fifoFile = new File(tmpDir, "fifo_file.tmp");
+    if (fifoFile.exists()) {
+      fifoFile.delete();
+    }
+    fifoFile.deleteOnExit();
+
+    // Create the fifo. This will throw an exception if the file system does not support it.
+    Libcore.os.mkfifo(fifoFile.getAbsolutePath(), OsConstants.S_IRWXU);
+  }
+
+  @Override
+  public void tearDown() throws Exception {
+    super.tearDown();
+    fifoFile.delete();
+    VOGAR_DEVICE_TEMP_DIR.delete();
+  }
+
+  public void testStreamRead_exceptionWhenAlreadyClosed() throws Exception {
+    FifoWriter fifoWriter = new FifoWriter(fifoFile);
+    fifoWriter.start();
+
+    FileInputStream fis = new FileInputStream(fifoFile);
+    fis.close();
+
+    byte[] buffer = new byte[10];
+    try {
+      fis.read(buffer);
+      fail();
+    } catch (IOException expected) {
+      assertSame(IOException.class, expected.getClass());
+    }
+
+    fifoWriter.tidyUp();
+  }
+
+  // This test fails on the RI: close() does not wake up a blocking FileInputStream.read() call.
+  public void testStreamRead_exceptionOnCloseWhenBlocked() throws Exception {
+    FifoWriter fifoWriter = new FifoWriter(fifoFile);
+    fifoWriter.start();
+
+    FileInputStream fis = new FileInputStream(fifoFile);
+    StreamReader streamReader = new StreamReader(fis);
+    Thread streamReaderThread = createAndStartThread("StreamReader", streamReader);
+
+    // Delay until we can be fairly sure the reader thread is blocking.
+    streamReader.waitForThreadToBlock();
+
+    // Now close the OutputStream to see what happens.
+    fis.close();
+
+    // Test for expected behavior in the reader thread.
+    waitToDie(streamReaderThread);
+    assertSame(InterruptedIOException.class, streamReader.ioe.getClass());
+    assertFalse(streamReader.wasInterrupted);
+
+    // Tidy up the writer thread.
+    fifoWriter.tidyUp();
+  }
+
+  public void testStreamWrite_exceptionWhenAlreadyClosed() throws Exception {
+    FifoReader fifoReader = new FifoReader(fifoFile);
+    fifoReader.start();
+
+    FileOutputStream fos = new FileOutputStream(fifoFile);
+    byte[] buffer = new byte[10];
+    fos.close();
+
+    try {
+      fos.write(buffer);
+      fail();
+    } catch (IOException expected) {
+      assertSame(IOException.class, expected.getClass());
+    }
+
+    fifoReader.tidyUp();
+  }
+
+  // This test fails on the RI: close() does not wake up a blocking FileInputStream.write() call.
+  public void testStreamWrite_exceptionOnCloseWhenBlocked() throws Exception {
+    FifoReader fifoReader = new FifoReader(fifoFile);
+    fifoReader.start();
+
+    FileOutputStream fos = new FileOutputStream(fifoFile);
+    StreamWriter streamWriter = new StreamWriter(fos);
+    Thread streamWriterThread = createAndStartThread("StreamWriter", streamWriter);
+
+    // Delay until we can be fairly sure the writer thread is blocking.
+    streamWriter.waitForThreadToBlock();
+
+    // Now close the OutputStream to see what happens.
+    fos.close();
+
+    // Test for expected behavior in the writer thread.
+    waitToDie(streamWriterThread);
+    assertSame(InterruptedIOException.class, streamWriter.ioe.getClass());
+    assertFalse(streamWriter.wasInterrupted);
+
+    // Tidy up the reader thread.
+    fifoReader.tidyUp();
+  }
+
+  public void testChannelRead_exceptionWhenAlreadyClosed() throws Exception {
+    testChannelRead_exceptionWhenAlreadyClosed(ChannelReader.Method.READ);
+  }
+
+  public void testChannelReadV_exceptionWhenAlreadyClosed() throws Exception {
+    testChannelRead_exceptionWhenAlreadyClosed(ChannelReader.Method.READV);
+  }
+
+  private void testChannelRead_exceptionWhenAlreadyClosed(ChannelReader.Method method)
+      throws Exception {
+    FifoWriter fifoWriter = new FifoWriter(fifoFile);
+    fifoWriter.start();
+    FileInputStream fis = new FileInputStream(fifoFile);
+    FileChannel fileInputChannel = fis.getChannel();
+    fileInputChannel.close();
+
+    ByteBuffer buffer = ByteBuffer.allocateDirect(10);
+    try {
+      if (method == ChannelReader.Method.READ) {
+        fileInputChannel.read(buffer);
+      } else {
+        ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
+        fileInputChannel.read(new ByteBuffer[] { buffer, buffer2});
+      }
+      fail();
+    } catch (IOException expected) {
+      assertSame(ClosedChannelException.class, expected.getClass());
+    }
+
+    fifoWriter.tidyUp();
+  }
+
+  public void testChannelRead_exceptionOnCloseWhenBlocked() throws Exception {
+    testChannelRead_exceptionOnCloseWhenBlocked(ChannelReader.Method.READ);
+  }
+
+  public void testChannelReadV_exceptionOnCloseWhenBlocked() throws Exception {
+    testChannelRead_exceptionOnCloseWhenBlocked(ChannelReader.Method.READV);
+  }
+
+  private void testChannelRead_exceptionOnCloseWhenBlocked(ChannelReader.Method method)
+      throws Exception {
+    FifoWriter fifoWriter = new FifoWriter(fifoFile);
+    fifoWriter.start();
+    FileInputStream fis = new FileInputStream(fifoFile);
+    FileChannel fileInputChannel = fis.getChannel();
+
+    ChannelReader channelReader = new ChannelReader(fileInputChannel, method);
+    Thread channelReaderThread = createAndStartThread("ChannelReader", channelReader);
+
+    // Delay until we can be fairly sure the reader thread is blocking.
+    channelReader.waitForThreadToBlock();
+
+    // Now close the FileChannel to see what happens.
+    fileInputChannel.close();
+
+    // Test for expected behavior in the reader thread.
+    waitToDie(channelReaderThread);
+    assertSame(AsynchronousCloseException.class, channelReader.ioe.getClass());
+    assertFalse(channelReader.wasInterrupted);
+
+    // Tidy up the writer thread.
+    fifoWriter.tidyUp();
+  }
+
+  public void testChannelRead_exceptionOnInterrupt() throws Exception {
+    testChannelRead_exceptionOnInterrupt(ChannelReader.Method.READ);
+  }
+
+  public void testChannelReadV_exceptionOnInterrupt() throws Exception {
+    testChannelRead_exceptionOnInterrupt(ChannelReader.Method.READV);
+  }
+
+  private void testChannelRead_exceptionOnInterrupt(ChannelReader.Method method) throws Exception {
+    FifoWriter fifoWriter = new FifoWriter(fifoFile);
+    fifoWriter.start();
+    FileChannel fileChannel = new FileInputStream(fifoFile).getChannel();
+
+    ChannelReader channelReader = new ChannelReader(fileChannel, method);
+    Thread channelReaderThread = createAndStartThread("ChannelReader", channelReader);
+
+    // Delay until we can be fairly sure the reader thread is blocking.
+    channelReader.waitForThreadToBlock();
+
+    // Now interrupt the reader thread to see what happens.
+    channelReaderThread.interrupt();
+
+    // Test for expected behavior in the reader thread.
+    waitToDie(channelReaderThread);
+    assertSame(ClosedByInterruptException.class, channelReader.ioe.getClass());
+    assertTrue(channelReader.wasInterrupted);
+
+    // Tidy up the writer thread.
+    fifoWriter.tidyUp();
+  }
+
+  public void testChannelWrite_exceptionWhenAlreadyClosed() throws Exception {
+    testChannelWrite_exceptionWhenAlreadyClosed(ChannelWriter.Method.WRITE);
+  }
+
+  public void testChannelWriteV_exceptionWhenAlreadyClosed() throws Exception {
+    testChannelWrite_exceptionWhenAlreadyClosed(ChannelWriter.Method.WRITEV);
+  }
+
+  private void testChannelWrite_exceptionWhenAlreadyClosed(ChannelWriter.Method method)
+      throws Exception {
+    FifoReader fifoReader = new FifoReader(fifoFile);
+    fifoReader.start();
+    FileChannel fileOutputChannel = new FileOutputStream(fifoFile).getChannel();
+    fileOutputChannel.close();
+
+    ByteBuffer buffer = ByteBuffer.allocateDirect(10);
+    try {
+      if (method == ChannelWriter.Method.WRITE) {
+        fileOutputChannel.write(buffer);
+      } else {
+        ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
+        fileOutputChannel.write(new ByteBuffer[] { buffer, buffer2 });
+      }
+      fail();
+    } catch (IOException expected) {
+      assertSame(ClosedChannelException.class, expected.getClass());
+    }
+
+    fifoReader.tidyUp();
+  }
+
+  public void testChannelWrite_exceptionOnCloseWhenBlocked() throws Exception {
+    testChannelWrite_exceptionOnCloseWhenBlocked(ChannelWriter.Method.WRITE);
+  }
+
+  public void testChannelWriteV_exceptionOnCloseWhenBlocked() throws Exception {
+    testChannelWrite_exceptionOnCloseWhenBlocked(ChannelWriter.Method.WRITEV);
+  }
+
+  private void testChannelWrite_exceptionOnCloseWhenBlocked(ChannelWriter.Method method)
+      throws Exception {
+    FifoReader fifoReader = new FifoReader(fifoFile);
+    fifoReader.start();
+    FileChannel fileOutputChannel = new FileOutputStream(fifoFile).getChannel();
+
+    ChannelWriter channelWriter = new ChannelWriter(fileOutputChannel, method);
+    Thread channelWriterThread = createAndStartThread("ChannelWriter", channelWriter);
+
+    // Delay until we can be fairly sure the writer thread is blocking.
+    channelWriter.waitForThreadToBlock();
+
+    // Now close the channel to see what happens.
+    fileOutputChannel.close();
+
+    // Test for expected behavior in the writer thread.
+    waitToDie(channelWriterThread);
+    // The RI throws ChannelClosedException. AsynchronousCloseException is more correct according to
+    // the docs.
+    assertSame(AsynchronousCloseException.class, channelWriter.ioe.getClass());
+    assertFalse(channelWriter.wasInterrupted);
+
+    // Tidy up the writer thread.
+    fifoReader.tidyUp();
+  }
+
+  public void testChannelWrite_exceptionOnInterrupt() throws Exception {
+    testChannelWrite_exceptionOnInterrupt(ChannelWriter.Method.WRITE);
+  }
+
+  public void testChannelWriteV_exceptionOnInterrupt() throws Exception {
+    testChannelWrite_exceptionOnInterrupt(ChannelWriter.Method.WRITEV);
+  }
+
+  private void testChannelWrite_exceptionOnInterrupt(ChannelWriter.Method method) throws Exception {
+    FifoReader fifoReader = new FifoReader(fifoFile);
+    fifoReader.start();
+
+    FileChannel fileChannel = new FileOutputStream(fifoFile).getChannel();
+    ChannelWriter channelWriter = new ChannelWriter(fileChannel, method);
+    Thread channelWriterThread = createAndStartThread("ChannelWriter", channelWriter);
+
+    // Delay until we can be fairly sure the writer thread is blocking.
+    channelWriter.waitForThreadToBlock();
+
+    // Now interrupt the writer thread to see what happens.
+    channelWriterThread.interrupt();
+
+    // Test for expected behavior in the writer thread.
+    waitToDie(channelWriterThread);
+    assertSame(ClosedByInterruptException.class, channelWriter.ioe.getClass());
+    assertTrue(channelWriter.wasInterrupted);
+
+    // Tidy up the reader thread.
+    fifoReader.tidyUp();
+  }
+
+  private static class StreamReader implements Runnable {
+
+    private final FileInputStream inputStream;
+    volatile boolean started;
+    volatile IOException ioe;
+    volatile boolean wasInterrupted;
+
+    StreamReader(FileInputStream inputStream) {
+      this.inputStream = inputStream;
+    }
+
+    @Override
+    public void run() {
+      byte[] buffer = new byte[10];
+      try {
+        started = true;
+        int bytesRead = inputStream.read(buffer);
+        fail("This isn't supposed to happen: read() returned: " + bytesRead);
+      } catch (IOException e) {
+        this.ioe = e;
+      }
+      wasInterrupted = Thread.interrupted();
+    }
+
+    public void waitForThreadToBlock() {
+      for (int i = 0; i < 10 && !started; i++) {
+        delay(100);
+      }
+      assertTrue(started);
+      // Just give it some more time to start blocking.
+      delay(100);
+    }
+  }
+
+  private static class StreamWriter implements Runnable {
+
+    private final FileOutputStream outputStream;
+    volatile int bytesWritten;
+    volatile IOException ioe;
+    volatile boolean wasInterrupted;
+
+    StreamWriter(FileOutputStream outputStream) {
+      this.outputStream = outputStream;
+    }
+
+    @Override
+    public void run() {
+      // Writes to FIFOs are buffered. We try to fill the buffer and induce blocking (the
+      // buffer is typically 64k).
+      byte[] buffer = new byte[10000];
+      while (true) {
+        try {
+          outputStream.write(buffer);
+          bytesWritten += buffer.length;
+        } catch (IOException e) {
+          this.ioe = e;
+          break;
+        }
+        wasInterrupted = Thread.interrupted();
+      }
+    }
+
+    public void waitForThreadToBlock() {
+      int lastCount = bytesWritten;
+      for (int i = 0; i < 10; i++) {
+        delay(500);
+        int newBytesWritten = bytesWritten;
+        if (newBytesWritten > 0 && lastCount == newBytesWritten) {
+          // The thread is probably blocking.
+          return;
+        }
+        lastCount = bytesWritten;
+      }
+      fail("Writer never started blocking. Bytes written: " + bytesWritten);
+    }
+  }
+
+  private static class ChannelReader implements Runnable {
+    enum Method {
+      READ,
+      READV,
+    }
+
+    private final FileChannel channel;
+    private final Method method;
+    volatile boolean started;
+    volatile IOException ioe;
+    volatile boolean wasInterrupted;
+
+    ChannelReader(FileChannel channel, Method method) {
+      this.channel = channel;
+      this.method = method;
+    }
+
+    @Override
+    public void run() {
+      ByteBuffer buffer = ByteBuffer.allocateDirect(10);
+      try {
+        started = true;
+        if (method == Method.READ) {
+          channel.read(buffer);
+        } else {
+          ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
+          channel.read(new ByteBuffer[] { buffer, buffer2 });
+        }
+        fail("All tests should block until an exception");
+      } catch (IOException e) {
+        this.ioe = e;
+      }
+      wasInterrupted = Thread.interrupted();
+    }
+
+    public void waitForThreadToBlock() {
+      for (int i = 0; i < 10 && !started; i++) {
+        delay(100);
+      }
+      assertTrue(started);
+      // Just give it some more time to start blocking.
+      delay(100);
+    }
+  }
+
+  private static class ChannelWriter implements Runnable {
+    enum Method {
+      WRITE,
+      WRITEV,
+    }
+
+    private final FileChannel channel;
+    private final Method method;
+    volatile int bytesWritten;
+    volatile IOException ioe;
+    volatile boolean wasInterrupted;
+
+    ChannelWriter(FileChannel channel, Method method) {
+      this.channel = channel;
+      this.method = method;
+    }
+
+    @Override
+    public void run() {
+      ByteBuffer buffer1 = ByteBuffer.allocateDirect(10000);
+      ByteBuffer buffer2 = ByteBuffer.allocateDirect(10000);
+      // Writes to FIFOs are buffered. We try to fill the buffer and induce blocking (the
+      // buffer is typically 64k).
+      while (true) {
+        // Make the buffers look non-empty.
+        buffer1.position(0).limit(buffer1.capacity());
+        buffer2.position(0).limit(buffer2.capacity());
+        try {
+          if (method == Method.WRITE) {
+            bytesWritten += channel.write(buffer1);
+          } else {
+            bytesWritten += channel.write(new ByteBuffer[]{ buffer1, buffer2 });
+          }
+        } catch (IOException e) {
+          this.ioe = e;
+          break;
+        }
+      }
+      wasInterrupted = Thread.interrupted();
+    }
+
+    public void waitForThreadToBlock() {
+      int lastCount = bytesWritten;
+      for (int i = 0; i < 10; i++) {
+        delay(500);
+        int newBytesWritten = bytesWritten;
+        if (newBytesWritten > 0 && lastCount == newBytesWritten) {
+          // The thread is probably blocking.
+          return;
+        }
+        lastCount = bytesWritten;
+      }
+      fail("Writer never started blocking. Bytes written: " + bytesWritten);
+    }
+  }
+
+  /**
+   * Opens a FIFO for writing. Exists to unblock the other end of the FIFO.
+   */
+  private static class FifoWriter extends Thread {
+
+    private final File file;
+    private FileOutputStream fos;
+
+    public FifoWriter(File file) {
+      super("FifoWriter");
+      this.file = file;
+    }
+
+    @Override
+    public void run() {
+      try {
+        fos = new FileOutputStream(file);
+      } catch (IOException ignored) {
+      }
+    }
+
+    public void tidyUp() {
+      FileIOInterruptTest.waitToDie(this);
+      closeQuietly(fos);
+    }
+  }
+
+  /**
+   * Opens a FIFO for reading. Exists to unblock the other end of the FIFO.
+   */
+  private static class FifoReader extends Thread {
+
+    private final File file;
+    private FileInputStream fis;
+
+    public FifoReader(File file) {
+      super("FifoReader");
+      this.file = file;
+    }
+
+    @Override
+    public void run() {
+      try {
+        fis = new FileInputStream(file);
+      } catch (IOException ignored) {
+      }
+    }
+
+    public void tidyUp() {
+      FileIOInterruptTest.waitToDie(this);
+      closeQuietly(fis);
+    }
+  }
+
+  private static Thread createAndStartThread(String name, Runnable runnable) {
+    Thread t = new Thread(runnable, name);
+    t.setDaemon(true);
+    t.start();
+    return t;
+  }
+
+  private static void waitToDie(Thread thread) {
+    try {
+      thread.join(5000);
+    } catch (InterruptedException ignored) {
+    }
+
+    if (thread.isAlive()) {
+      fail("Thread \"" + thread.getName() + "\" did not exit.");
+    }
+  }
+
+  private static void delay(int millis) {
+    try {
+      Thread.sleep(millis);
+    } catch (InterruptedException ignored) {
+    }
+  }
+
+}