Wait for secondary zygote before bringing up the system_server.

The zygote that's responsible for starting up the system server
now checks if there's another zygote on the system, and waits
for it to start up. Also, a few minor clean ups :

- Address a long standing TODO about zygote retries.
- Have functions throw IOException where appropriate and
  wrap them in ZygoteStartFailedEx with a filled in cause.

bug: 14869939

Change-Id: I9e514659b79b3d2c98a4c5f93c0c376843f6c881
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index a4c4a87..28797ce 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -31,13 +31,17 @@
 import java.util.List;
 
 /*package*/ class ZygoteStartFailedEx extends Exception {
-    /**
-     * Something prevented the zygote process startup from happening normally
-     */
+    ZygoteStartFailedEx(String s) {
+        super(s);
+    }
 
-    ZygoteStartFailedEx() {};
-    ZygoteStartFailedEx(String s) {super(s);}
-    ZygoteStartFailedEx(Throwable cause) {super(cause);}
+    ZygoteStartFailedEx(Throwable cause) {
+        super(cause);
+    }
+
+    ZygoteStartFailedEx(String s, Throwable cause) {
+        super(s, cause);
+    }
 }
 
 /**
@@ -46,9 +50,15 @@
 public class Process {
     private static final String LOG_TAG = "Process";
 
-    private static final String ZYGOTE_SOCKET = "zygote";
+    /**
+     * @hide for internal use only.
+     */
+    public static final String ZYGOTE_SOCKET = "zygote";
 
-    private static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary";
+    /**
+     * @hide for internal use only.
+     */
+    public static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary";
 
     /**
      * Defines the UID/GID under which system code runs.
@@ -338,8 +348,10 @@
 
     /**
      * State for communicating with the zygote process.
+     *
+     * @hide for internal use only.
      */
-    static class ZygoteState {
+    public static class ZygoteState {
         final LocalSocket socket;
         final DataInputStream inputStream;
         final BufferedWriter writer;
@@ -355,55 +367,26 @@
             this.abiList = abiList;
         }
 
-        static ZygoteState connect(String socketAddress, int tries) throws ZygoteStartFailedEx {
-            LocalSocket zygoteSocket = null;
+        public static ZygoteState connect(String socketAddress) throws IOException {
             DataInputStream zygoteInputStream = null;
             BufferedWriter zygoteWriter = null;
+            final LocalSocket zygoteSocket = new LocalSocket();
 
-            /*
-             * See bug #811181: Sometimes runtime can make it up before zygote.
-             * Really, we'd like to do something better to avoid this condition,
-             * but for now just wait a bit...
-             *
-             * TODO: This bug was filed in 2007. Get rid of this code. The zygote
-             * forks the system_server so it shouldn't be possible for the zygote
-             * socket to be brought up after the system_server is.
-             */
-            for (int i = 0; i < tries; i++) {
-                if (i > 0) {
-                    try {
-                        Log.i(LOG_TAG, "Zygote not up yet, sleeping...");
-                        Thread.sleep(ZYGOTE_RETRY_MILLIS);
-                    } catch (InterruptedException ex) {
-                        throw new ZygoteStartFailedEx(ex);
-                    }
-                }
+            try {
+                zygoteSocket.connect(new LocalSocketAddress(socketAddress,
+                        LocalSocketAddress.Namespace.RESERVED));
 
+                zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
+
+                zygoteWriter = new BufferedWriter(new OutputStreamWriter(
+                        zygoteSocket.getOutputStream()), 256);
+            } catch (IOException ex) {
                 try {
-                    zygoteSocket = new LocalSocket();
-                    zygoteSocket.connect(new LocalSocketAddress(socketAddress,
-                            LocalSocketAddress.Namespace.RESERVED));
-
-                    zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
-
-                    zygoteWriter = new BufferedWriter(new OutputStreamWriter(
-                            zygoteSocket.getOutputStream()), 256);
-                    break;
-                } catch (IOException ex) {
-                    if (zygoteSocket != null) {
-                        try {
-                            zygoteSocket.close();
-                        } catch (IOException ex2) {
-                            Log.e(LOG_TAG,"I/O exception on close after exception", ex2);
-                        }
-                    }
-
-                    zygoteSocket = null;
+                    zygoteSocket.close();
+                } catch (IOException ignore) {
                 }
-            }
 
-            if (zygoteSocket == null) {
-                throw new ZygoteStartFailedEx("connect failed");
+                throw ex;
             }
 
             String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
@@ -417,7 +400,7 @@
             return abiList.contains(abi);
         }
 
-        void close() {
+        public void close() {
             try {
                 socket.close();
             } catch (IOException ex) {
@@ -503,27 +486,22 @@
      * @throws ZygoteStartFailedEx if the query failed.
      */
     private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
-            throws ZygoteStartFailedEx {
-        try {
+            throws IOException {
+        // Each query starts with the argument count (1 in this case)
+        writer.write("1");
+        // ... followed by a new-line.
+        writer.newLine();
+        // ... followed by our only argument.
+        writer.write("--query-abi-list");
+        writer.newLine();
+        writer.flush();
 
-            // Each query starts with the argument count (1 in this case)
-            writer.write("1");
-            // ... followed by a new-line.
-            writer.newLine();
-            // ... followed by our only argument.
-            writer.write("--query-abi-list");
-            writer.newLine();
-            writer.flush();
+        // The response is a length prefixed stream of ASCII bytes.
+        int numBytes = inputStream.readInt();
+        byte[] bytes = new byte[numBytes];
+        inputStream.readFully(bytes);
 
-            // The response is a length prefixed stream of ASCII bytes.
-            int numBytes = inputStream.readInt();
-            byte[] bytes = new byte[numBytes];
-            inputStream.readFully(bytes);
-
-            return new String(bytes, StandardCharsets.US_ASCII);
-        } catch (IOException ioe) {
-            throw new ZygoteStartFailedEx(ioe);
-        }
+        return new String(bytes, StandardCharsets.US_ASCII);
     }
 
     /**
@@ -677,30 +655,16 @@
     }
 
     /**
-     * Returns the number of times we attempt a connection to the zygote. We
-     * sleep for {@link #ZYGOTE_RETRY_MILLIS} milliseconds between each try.
-     *
-     * This could probably be removed, see TODO in {@code ZygoteState#connect}.
-     */
-    private static int getNumTries(ZygoteState state) {
-        // Retry 10 times for the first connection to each zygote.
-        if (state == null) {
-            return 11;
-        }
-
-        // This means the connection has already been established, but subsequently
-        // closed, possibly due to an IOException. We retry just once if that's the
-        // case.
-        return 1;
-    }
-
-    /**
      * Tries to open socket to Zygote process if not already open. If
      * already open, does nothing.  May block and retry.
      */
     private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
         if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
-            primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET, getNumTries(primaryZygoteState));
+            try {
+                primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);
+            } catch (IOException ioe) {
+                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
+            }
         }
 
         if (primaryZygoteState.matches(abi)) {
@@ -709,8 +673,11 @@
 
         // The primary zygote didn't match. Try the secondary.
         if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
-            secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET,
-                    getNumTries(secondaryZygoteState));
+            try {
+            secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);
+            } catch (IOException ioe) {
+                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
+            }
         }
 
         if (secondaryZygoteState.matches(abi)) {