Merge "libcore: Remove native calls in FileDescriptor <clinit>" into rvc-dev
diff --git a/dalvik/src/main/java/dalvik/system/ZygoteHooks.java b/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
index 0339b4d..475acd9 100644
--- a/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
+++ b/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
@@ -21,6 +21,7 @@
 import android.icu.util.ULocale;
 
 import java.io.File;
+import java.io.FileDescriptor;
 
 /**
  * Provides hooks for the zygote to call back into the runtime to perform
@@ -67,6 +68,11 @@
     public static void onEndPreload() {
         // All cache references created by ICU from this point will be soft.
         CacheValue.setStrength(CacheValue.Strength.SOFT);
+
+        // Clone standard descriptors as originals closed / rebound during zygote post fork.
+        FileDescriptor.in.cloneForFork();
+        FileDescriptor.out.cloneForFork();
+        FileDescriptor.err.cloneForFork();
     }
 
     /**
diff --git a/luni/src/test/java/libcore/java/io/FileDescriptorTest.java b/luni/src/test/java/libcore/java/io/FileDescriptorTest.java
index 3e82690..390cfd6 100644
--- a/luni/src/test/java/libcore/java/io/FileDescriptorTest.java
+++ b/luni/src/test/java/libcore/java/io/FileDescriptorTest.java
@@ -16,7 +16,10 @@
 
 package libcore.java.io;
 
+import android.system.Os;
+
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -24,6 +27,8 @@
 import java.net.ServerSocket;
 import junit.framework.TestCase;
 
+import static org.junit.Assert.assertNotEquals;
+
 public class FileDescriptorTest extends TestCase {
   public void testReadOnlyFileDescriptorSync() throws Exception {
     File f= File.createTempFile("FileDescriptorTest", "tmp");
@@ -40,4 +45,31 @@
     assertTrue(s.getImpl().getFD$().isSocket$());
     s.close();
   }
+
+  public void testStaticFileDescriptors() {
+    assertTrue(FileDescriptor.in.valid());
+    assertTrue(FileDescriptor.out.valid());
+    assertTrue(FileDescriptor.err.valid());
+  }
+
+  public void testFileDescriptorCloneForFork() throws Exception {
+    FileDescriptor [] sources = { FileDescriptor.in, FileDescriptor.out, FileDescriptor.err };
+    for (FileDescriptor source : sources) {
+      // Create a new file descriptor and set it's native descriptor to each of the well
+      // known descriptors in FileDescriptor.
+      FileDescriptor target = new FileDescriptor();
+      target.setInt$(source.getInt$());
+      assertEquals(target.getInt$(), source.getInt$());
+
+      // Clone file descriptor, this creates a native file descriptor.
+      target.cloneForFork();
+      assertTrue(source.valid());
+      assertTrue(target.valid());
+      assertNotEquals(target.getInt$(), source.getInt$());
+
+      // Clean-up native resource we allocated in cloneForFork. Os.close() may throw
+      // an ErrnoException.
+      Os.close(target);
+    }
+  }
 }
diff --git a/ojluni/src/main/java/java/io/FileDescriptor.java b/ojluni/src/main/java/java/io/FileDescriptor.java
index ed3ebfe..98db443 100644
--- a/ojluni/src/main/java/java/io/FileDescriptor.java
+++ b/ojluni/src/main/java/java/io/FileDescriptor.java
@@ -82,8 +82,7 @@
      *
      * @see     java.lang.System#in
      */
-    // Android-changed: Duplicates of FDs needed for RuntimeInit#redirectLogStreams
-    public static final FileDescriptor in = dupFd(0);
+    public static final FileDescriptor in = new FileDescriptor(0);
 
     /**
      * A handle to the standard output stream. Usually, this file
@@ -91,8 +90,7 @@
      * known as <code>System.out</code>.
      * @see     java.lang.System#out
      */
-    // Android-changed: Duplicates of FDs needed for RuntimeInit#redirectLogStreams
-    public static final FileDescriptor out = dupFd(1);
+    public static final FileDescriptor out = new FileDescriptor(1);
 
     /**
      * A handle to the standard error stream. Usually, this file
@@ -101,8 +99,7 @@
      *
      * @see     java.lang.System#err
      */
-    // Android-changed: Duplicates of FDs needed for RuntimeInit#redirectLogStreams
-    public static final FileDescriptor err = dupFd(2);
+    public static final FileDescriptor err = new FileDescriptor(2);
 
     /**
      * Tests if this file descriptor object is valid.
@@ -169,6 +166,26 @@
         this.descriptor = fd;
     }
 
+    // BEGIN Android-added: Method to clone standard file descriptors.
+    // Required as a consequence of RuntimeInit#redirectLogStreams. Cloning is used in
+    // ZygoteHooks.onEndPreload().
+    /**
+     * Clones the current native file descriptor and uses this for this FileDescriptor instance.
+     *
+     * This method does not close the current native file descriptor.
+     *
+     * @hide internal use only
+     */
+    public void cloneForFork() {
+        try {
+            int newDescriptor = Os.fcntlInt(this, F_DUPFD_CLOEXEC, 0);
+            this.descriptor = newDescriptor;
+        } catch (ErrnoException e) {
+            throw new RuntimeException(e);
+        }
+    }
+    // END Android-added: Method to clone standard file descriptors.
+
     // BEGIN Android-added: Methods to enable ownership enforcement of Unix file descriptors.
     /**
      * Returns the owner ID of this FileDescriptor. It's highly unlikely you should be calling this.
@@ -214,15 +231,6 @@
         return isSocket(descriptor);
     }
 
-    // Android-added: Needed for RuntimeInit#redirectLogStreams.
-    private static FileDescriptor dupFd(int fd) {
-        try {
-            return new FileDescriptor(Os.fcntlInt(new FileDescriptor(fd), F_DUPFD_CLOEXEC, 0));
-        } catch (ErrnoException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
     private static native boolean isSocket(int descriptor);
     // Set up JavaIOFileDescriptorAccess in SharedSecrets
     static {