Merge "Fix parameter description"
am: 750b523aa2

Change-Id: I5edb7e6910b92d3c2f93a61fc9e68489ece8e72f
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 6aa9fac..f664e70 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -16,9 +16,34 @@
 
 package android.os;
 
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
 import android.system.Os;
 import android.util.Log;
+import com.android.internal.os.Zygote;
 import dalvik.system.VMRuntime;
+import java.io.BufferedWriter;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/*package*/ class ZygoteStartFailedEx extends Exception {
+    ZygoteStartFailedEx(String s) {
+        super(s);
+    }
+
+    ZygoteStartFailedEx(Throwable cause) {
+        super(cause);
+    }
+
+    ZygoteStartFailedEx(String s, Throwable cause) {
+        super(s, cause);
+    }
+}
 
 /**
  * Tools for managing OS processes.
@@ -362,11 +387,83 @@
     private static long sStartUptimeMillis;
 
     /**
-     * State associated with the zygote process.
-     * @hide
+     * State for communicating with the zygote process.
+     *
+     * @hide for internal use only.
      */
-    public static final ZygoteProcess zygoteProcess =
-            new ZygoteProcess(ZYGOTE_SOCKET, SECONDARY_ZYGOTE_SOCKET);
+    public static class ZygoteState {
+        final LocalSocket socket;
+        final DataInputStream inputStream;
+        final BufferedWriter writer;
+        final List<String> abiList;
+
+        boolean mClosed;
+
+        private ZygoteState(LocalSocket socket, DataInputStream inputStream,
+                BufferedWriter writer, List<String> abiList) {
+            this.socket = socket;
+            this.inputStream = inputStream;
+            this.writer = writer;
+            this.abiList = abiList;
+        }
+
+        public static ZygoteState connect(String socketAddress) throws IOException {
+            DataInputStream zygoteInputStream = null;
+            BufferedWriter zygoteWriter = null;
+            final LocalSocket zygoteSocket = new LocalSocket();
+
+            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.close();
+                } catch (IOException ignore) {
+                }
+
+                throw ex;
+            }
+
+            String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
+            Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString);
+
+            return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
+                    Arrays.asList(abiListString.split(",")));
+        }
+
+        boolean matches(String abi) {
+            return abiList.contains(abi);
+        }
+
+        public void close() {
+            try {
+                socket.close();
+            } catch (IOException ex) {
+                Log.e(LOG_TAG,"I/O exception on routine close", ex);
+            }
+
+            mClosed = true;
+        }
+
+        boolean isClosed() {
+            return mClosed;
+        }
+    }
+
+    /**
+     * The state of the connection to the primary zygote.
+     */
+    static ZygoteState primaryZygoteState;
+
+    /**
+     * The state of the connection to the secondary zygote.
+     */
+    static ZygoteState secondaryZygoteState;
 
     /**
      * Start a new process.
@@ -412,9 +509,263 @@
                                   String instructionSet,
                                   String appDataDir,
                                   String[] zygoteArgs) {
-        return zygoteProcess.start(processClass, niceName, uid, gid, gids,
+        try {
+            return startViaZygote(processClass, niceName, uid, gid, gids,
                     debugFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, zygoteArgs);
+        } catch (ZygoteStartFailedEx ex) {
+            Log.e(LOG_TAG,
+                    "Starting VM process through Zygote failed");
+            throw new RuntimeException(
+                    "Starting VM process through Zygote failed", ex);
+        }
+    }
+
+    /** retry interval for opening a zygote socket */
+    static final int ZYGOTE_RETRY_MILLIS = 500;
+
+    /**
+     * Queries the zygote for the list of ABIS it supports.
+     *
+     * @throws ZygoteStartFailedEx if the query failed.
+     */
+    private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
+            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();
+
+        // 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);
+    }
+
+    /**
+     * Sends an argument list to the zygote process, which starts a new child
+     * and returns the child's pid. Please note: the present implementation
+     * replaces newlines in the argument list with spaces.
+     *
+     * @throws ZygoteStartFailedEx if process start failed for any reason
+     */
+    private static ProcessStartResult zygoteSendArgsAndGetResult(
+            ZygoteState zygoteState, ArrayList<String> args)
+            throws ZygoteStartFailedEx {
+        try {
+            /**
+             * See com.android.internal.os.ZygoteInit.readArgumentList()
+             * Presently the wire format to the zygote process is:
+             * a) a count of arguments (argc, in essence)
+             * b) a number of newline-separated argument strings equal to count
+             *
+             * After the zygote process reads these it will write the pid of
+             * the child or -1 on failure, followed by boolean to
+             * indicate whether a wrapper process was used.
+             */
+            final BufferedWriter writer = zygoteState.writer;
+            final DataInputStream inputStream = zygoteState.inputStream;
+
+            writer.write(Integer.toString(args.size()));
+            writer.newLine();
+
+            int sz = args.size();
+            for (int i = 0; i < sz; i++) {
+                String arg = args.get(i);
+                if (arg.indexOf('\n') >= 0) {
+                    throw new ZygoteStartFailedEx(
+                            "embedded newlines not allowed");
+                }
+                writer.write(arg);
+                writer.newLine();
+            }
+
+            writer.flush();
+
+            // Should there be a timeout on this?
+            ProcessStartResult result = new ProcessStartResult();
+            result.pid = inputStream.readInt();
+            if (result.pid < 0) {
+                throw new ZygoteStartFailedEx("fork() failed");
+            }
+            result.usingWrapper = inputStream.readBoolean();
+            return result;
+        } catch (IOException ex) {
+            zygoteState.close();
+            throw new ZygoteStartFailedEx(ex);
+        }
+    }
+
+    /**
+     * Starts a new process via the zygote mechanism.
+     *
+     * @param processClass Class name whose static main() to run
+     * @param niceName 'nice' process name to appear in ps
+     * @param uid a POSIX uid that the new process should setuid() to
+     * @param gid a POSIX gid that the new process shuold setgid() to
+     * @param gids null-ok; a list of supplementary group IDs that the
+     * new process should setgroup() to.
+     * @param debugFlags Additional flags.
+     * @param targetSdkVersion The target SDK version for the app.
+     * @param seInfo null-ok SELinux information for the new process.
+     * @param abi the ABI the process should use.
+     * @param instructionSet null-ok the instruction set to use.
+     * @param appDataDir null-ok the data directory of the app.
+     * @param extraArgs Additional arguments to supply to the zygote process.
+     * @return An object that describes the result of the attempt to start the process.
+     * @throws ZygoteStartFailedEx if process start failed for any reason
+     */
+    private static ProcessStartResult startViaZygote(final String processClass,
+                                  final String niceName,
+                                  final int uid, final int gid,
+                                  final int[] gids,
+                                  int debugFlags, int mountExternal,
+                                  int targetSdkVersion,
+                                  String seInfo,
+                                  String abi,
+                                  String instructionSet,
+                                  String appDataDir,
+                                  String[] extraArgs)
+                                  throws ZygoteStartFailedEx {
+        synchronized(Process.class) {
+            ArrayList<String> argsForZygote = new ArrayList<String>();
+
+            // --runtime-args, --setuid=, --setgid=,
+            // and --setgroups= must go first
+            argsForZygote.add("--runtime-args");
+            argsForZygote.add("--setuid=" + uid);
+            argsForZygote.add("--setgid=" + gid);
+            if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
+                argsForZygote.add("--enable-jni-logging");
+            }
+            if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
+                argsForZygote.add("--enable-safemode");
+            }
+            if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
+                argsForZygote.add("--enable-debugger");
+            }
+            if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
+                argsForZygote.add("--enable-checkjni");
+            }
+            if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
+                argsForZygote.add("--generate-debug-info");
+            }
+            if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
+                argsForZygote.add("--always-jit");
+            }
+            if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
+                argsForZygote.add("--native-debuggable");
+            }
+            if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
+                argsForZygote.add("--enable-assert");
+            }
+            if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
+                argsForZygote.add("--mount-external-default");
+            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
+                argsForZygote.add("--mount-external-read");
+            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
+                argsForZygote.add("--mount-external-write");
+            }
+            argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
+
+            //TODO optionally enable debuger
+            //argsForZygote.add("--enable-debugger");
+
+            // --setgroups is a comma-separated list
+            if (gids != null && gids.length > 0) {
+                StringBuilder sb = new StringBuilder();
+                sb.append("--setgroups=");
+
+                int sz = gids.length;
+                for (int i = 0; i < sz; i++) {
+                    if (i != 0) {
+                        sb.append(',');
+                    }
+                    sb.append(gids[i]);
+                }
+
+                argsForZygote.add(sb.toString());
+            }
+
+            if (niceName != null) {
+                argsForZygote.add("--nice-name=" + niceName);
+            }
+
+            if (seInfo != null) {
+                argsForZygote.add("--seinfo=" + seInfo);
+            }
+
+            if (instructionSet != null) {
+                argsForZygote.add("--instruction-set=" + instructionSet);
+            }
+
+            if (appDataDir != null) {
+                argsForZygote.add("--app-data-dir=" + appDataDir);
+            }
+
+            argsForZygote.add(processClass);
+
+            if (extraArgs != null) {
+                for (String arg : extraArgs) {
+                    argsForZygote.add(arg);
+                }
+            }
+
+            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
+        }
+    }
+
+    /**
+     * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block and retry if the
+     * zygote is unresponsive. This method is a no-op if a connection is already open.
+     *
+     * @hide
+     */
+    public static void establishZygoteConnectionForAbi(String abi) {
+        try {
+            openZygoteSocketIfNeeded(abi);
+        } catch (ZygoteStartFailedEx ex) {
+            throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex);
+        }
+    }
+
+    /**
+     * 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()) {
+            try {
+                primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);
+            } catch (IOException ioe) {
+                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
+            }
+        }
+
+        if (primaryZygoteState.matches(abi)) {
+            return primaryZygoteState;
+        }
+
+        // The primary zygote didn't match. Try the secondary.
+        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
+            try {
+            secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);
+            } catch (IOException ioe) {
+                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
+            }
+        }
+
+        if (secondaryZygoteState.matches(abi)) {
+            return secondaryZygoteState;
+        }
+
+        throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
     }
 
     /**
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
deleted file mode 100644
index d7a7296..0000000
--- a/core/java/android/os/ZygoteProcess.java
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.os;
-
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
-import android.util.Log;
-import com.android.internal.os.Zygote;
-import java.io.BufferedWriter;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/*package*/ class ZygoteStartFailedEx extends Exception {
-    ZygoteStartFailedEx(String s) {
-        super(s);
-    }
-
-    ZygoteStartFailedEx(Throwable cause) {
-        super(cause);
-    }
-
-    ZygoteStartFailedEx(String s, Throwable cause) {
-        super(s, cause);
-    }
-}
-
-/**
- * Maintains communication state with the zygote processes. This class is responsible
- * for the sockets opened to the zygotes and for starting processes on behalf of the
- * {@link android.os.Process} class.
- *
- * {@hide}
- */
-public class ZygoteProcess {
-    private static final String LOG_TAG = "ZygoteProcess";
-
-    /**
-     * The name of the socket used to communicate with the primary zygote.
-     */
-    private final String mSocket;
-
-    /**
-     * The name of the secondary (alternate ABI) zygote socket.
-     */
-    private final String mSecondarySocket;
-
-    public ZygoteProcess(String primarySocket, String secondarySocket) {
-        mSocket = primarySocket;
-        mSecondarySocket = secondarySocket;
-    }
-
-    /**
-     * State for communicating with the zygote process.
-     */
-    public static class ZygoteState {
-        final LocalSocket socket;
-        final DataInputStream inputStream;
-        final BufferedWriter writer;
-        final List<String> abiList;
-
-        boolean mClosed;
-
-        private ZygoteState(LocalSocket socket, DataInputStream inputStream,
-                BufferedWriter writer, List<String> abiList) {
-            this.socket = socket;
-            this.inputStream = inputStream;
-            this.writer = writer;
-            this.abiList = abiList;
-        }
-
-        public static ZygoteState connect(String socketAddress) throws IOException {
-            DataInputStream zygoteInputStream = null;
-            BufferedWriter zygoteWriter = null;
-            final LocalSocket zygoteSocket = new LocalSocket();
-
-            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.close();
-                } catch (IOException ignore) {
-                }
-
-                throw ex;
-            }
-
-            String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
-            Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString);
-
-            return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
-                    Arrays.asList(abiListString.split(",")));
-        }
-
-        boolean matches(String abi) {
-            return abiList.contains(abi);
-        }
-
-        public void close() {
-            try {
-                socket.close();
-            } catch (IOException ex) {
-                Log.e(LOG_TAG,"I/O exception on routine close", ex);
-            }
-
-            mClosed = true;
-        }
-
-        boolean isClosed() {
-            return mClosed;
-        }
-    }
-
-    /**
-     * The state of the connection to the primary zygote.
-     */
-    private ZygoteState primaryZygoteState;
-
-    /**
-     * The state of the connection to the secondary zygote.
-     */
-    private ZygoteState secondaryZygoteState;
-
-    /**
-     * Start a new process.
-     *
-     * <p>If processes are enabled, a new process is created and the
-     * static main() function of a <var>processClass</var> is executed there.
-     * The process will continue running after this function returns.
-     *
-     * <p>If processes are not enabled, a new thread in the caller's
-     * process is created and main() of <var>processClass</var> called there.
-     *
-     * <p>The niceName parameter, if not an empty string, is a custom name to
-     * give to the process instead of using processClass.  This allows you to
-     * make easily identifyable processes even if you are using the same base
-     * <var>processClass</var> to start them.
-     *
-     * @param processClass The class to use as the process's main entry
-     *                     point.
-     * @param niceName A more readable name to use for the process.
-     * @param uid The user-id under which the process will run.
-     * @param gid The group-id under which the process will run.
-     * @param gids Additional group-ids associated with the process.
-     * @param debugFlags Additional flags.
-     * @param targetSdkVersion The target SDK version for the app.
-     * @param seInfo null-ok SELinux information for the new process.
-     * @param abi non-null the ABI this app should be started with.
-     * @param instructionSet null-ok the instruction set to use.
-     * @param appDataDir null-ok the data directory of the app.
-     * @param zygoteArgs Additional arguments to supply to the zygote process.
-     *
-     * @return An object that describes the result of the attempt to start the process.
-     * @throws RuntimeException on fatal start failure
-     */
-    public final Process.ProcessStartResult start(final String processClass,
-                                                  final String niceName,
-                                                  int uid, int gid, int[] gids,
-                                                  int debugFlags, int mountExternal,
-                                                  int targetSdkVersion,
-                                                  String seInfo,
-                                                  String abi,
-                                                  String instructionSet,
-                                                  String appDataDir,
-                                                  String[] zygoteArgs) {
-        try {
-            return startViaZygote(processClass, niceName, uid, gid, gids,
-                    debugFlags, mountExternal, targetSdkVersion, seInfo,
-                    abi, instructionSet, appDataDir, zygoteArgs);
-        } catch (ZygoteStartFailedEx ex) {
-            Log.e(LOG_TAG,
-                    "Starting VM process through Zygote failed");
-            throw new RuntimeException(
-                    "Starting VM process through Zygote failed", ex);
-        }
-    }
-
-    /** retry interval for opening a zygote socket */
-    static final int ZYGOTE_RETRY_MILLIS = 500;
-
-    /**
-     * Queries the zygote for the list of ABIS it supports.
-     *
-     * @throws ZygoteStartFailedEx if the query failed.
-     */
-    private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
-            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();
-
-        // 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);
-    }
-
-    /**
-     * Sends an argument list to the zygote process, which starts a new child
-     * and returns the child's pid. Please note: the present implementation
-     * replaces newlines in the argument list with spaces.
-     *
-     * @throws ZygoteStartFailedEx if process start failed for any reason
-     */
-    private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
-            ZygoteState zygoteState, ArrayList<String> args)
-            throws ZygoteStartFailedEx {
-        try {
-            /**
-             * See com.android.internal.os.SystemZygoteInit.readArgumentList()
-             * Presently the wire format to the zygote process is:
-             * a) a count of arguments (argc, in essence)
-             * b) a number of newline-separated argument strings equal to count
-             *
-             * After the zygote process reads these it will write the pid of
-             * the child or -1 on failure, followed by boolean to
-             * indicate whether a wrapper process was used.
-             */
-            final BufferedWriter writer = zygoteState.writer;
-            final DataInputStream inputStream = zygoteState.inputStream;
-
-            writer.write(Integer.toString(args.size()));
-            writer.newLine();
-
-            int sz = args.size();
-            for (int i = 0; i < sz; i++) {
-                String arg = args.get(i);
-                if (arg.indexOf('\n') >= 0) {
-                    throw new ZygoteStartFailedEx(
-                            "embedded newlines not allowed");
-                }
-                writer.write(arg);
-                writer.newLine();
-            }
-
-            writer.flush();
-
-            // Should there be a timeout on this?
-            Process.ProcessStartResult result = new Process.ProcessStartResult();
-            result.pid = inputStream.readInt();
-            if (result.pid < 0) {
-                throw new ZygoteStartFailedEx("fork() failed");
-            }
-            result.usingWrapper = inputStream.readBoolean();
-            return result;
-        } catch (IOException ex) {
-            zygoteState.close();
-            throw new ZygoteStartFailedEx(ex);
-        }
-    }
-
-    /**
-     * Starts a new process via the zygote mechanism.
-     *
-     * @param processClass Class name whose static main() to run
-     * @param niceName 'nice' process name to appear in ps
-     * @param uid a POSIX uid that the new process should setuid() to
-     * @param gid a POSIX gid that the new process shuold setgid() to
-     * @param gids null-ok; a list of supplementary group IDs that the
-     * new process should setgroup() to.
-     * @param debugFlags Additional flags.
-     * @param targetSdkVersion The target SDK version for the app.
-     * @param seInfo null-ok SELinux information for the new process.
-     * @param abi the ABI the process should use.
-     * @param instructionSet null-ok the instruction set to use.
-     * @param appDataDir null-ok the data directory of the app.
-     * @param extraArgs Additional arguments to supply to the zygote process.
-     * @return An object that describes the result of the attempt to start the process.
-     * @throws ZygoteStartFailedEx if process start failed for any reason
-     */
-    private Process.ProcessStartResult startViaZygote(final String processClass,
-                                                      final String niceName,
-                                                      final int uid, final int gid,
-                                                      final int[] gids,
-                                                      int debugFlags, int mountExternal,
-                                                      int targetSdkVersion,
-                                                      String seInfo,
-                                                      String abi,
-                                                      String instructionSet,
-                                                      String appDataDir,
-                                                      String[] extraArgs)
-                                                      throws ZygoteStartFailedEx {
-        synchronized(Process.class) {
-            ArrayList<String> argsForZygote = new ArrayList<String>();
-
-            // --runtime-args, --setuid=, --setgid=,
-            // and --setgroups= must go first
-            argsForZygote.add("--runtime-args");
-            argsForZygote.add("--setuid=" + uid);
-            argsForZygote.add("--setgid=" + gid);
-            if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
-                argsForZygote.add("--enable-jni-logging");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
-                argsForZygote.add("--enable-safemode");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
-                argsForZygote.add("--enable-debugger");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
-                argsForZygote.add("--enable-checkjni");
-            }
-            if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
-                argsForZygote.add("--generate-debug-info");
-            }
-            if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
-                argsForZygote.add("--always-jit");
-            }
-            if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
-                argsForZygote.add("--native-debuggable");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
-                argsForZygote.add("--enable-assert");
-            }
-            if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
-                argsForZygote.add("--mount-external-default");
-            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
-                argsForZygote.add("--mount-external-read");
-            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
-                argsForZygote.add("--mount-external-write");
-            }
-            argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
-
-            //TODO optionally enable debuger
-            //argsForZygote.add("--enable-debugger");
-
-            // --setgroups is a comma-separated list
-            if (gids != null && gids.length > 0) {
-                StringBuilder sb = new StringBuilder();
-                sb.append("--setgroups=");
-
-                int sz = gids.length;
-                for (int i = 0; i < sz; i++) {
-                    if (i != 0) {
-                        sb.append(',');
-                    }
-                    sb.append(gids[i]);
-                }
-
-                argsForZygote.add(sb.toString());
-            }
-
-            if (niceName != null) {
-                argsForZygote.add("--nice-name=" + niceName);
-            }
-
-            if (seInfo != null) {
-                argsForZygote.add("--seinfo=" + seInfo);
-            }
-
-            if (instructionSet != null) {
-                argsForZygote.add("--instruction-set=" + instructionSet);
-            }
-
-            if (appDataDir != null) {
-                argsForZygote.add("--app-data-dir=" + appDataDir);
-            }
-
-            argsForZygote.add(processClass);
-
-            if (extraArgs != null) {
-                for (String arg : extraArgs) {
-                    argsForZygote.add(arg);
-                }
-            }
-
-            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
-        }
-    }
-
-    /**
-     * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block
-     * and retry if the zygote is unresponsive. This method is a no-op if a connection is
-     * already open.
-     */
-    public void establishZygoteConnectionForAbi(String abi) {
-        try {
-            openZygoteSocketIfNeeded(abi);
-        } catch (ZygoteStartFailedEx ex) {
-            throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex);
-        }
-    }
-
-    /**
-     * Tries to open socket to Zygote process if not already open. If
-     * already open, does nothing.  May block and retry.
-     */
-    private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
-        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
-            try {
-                primaryZygoteState = ZygoteState.connect(mSocket);
-            } catch (IOException ioe) {
-                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
-            }
-        }
-
-        if (primaryZygoteState.matches(abi)) {
-            return primaryZygoteState;
-        }
-
-        // The primary zygote didn't match. Try the secondary.
-        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
-            try {
-                secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
-            } catch (IOException ioe) {
-                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
-            }
-        }
-
-        if (secondaryZygoteState.matches(abi)) {
-            return secondaryZygoteState;
-        }
-
-        throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
-    }
-}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index c851e4e..de671b1 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -230,7 +230,7 @@
      * @param classLoader the classLoader to load {@className} with
      */
     private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
-            throws Zygote.MethodAndArgsCaller {
+            throws ZygoteInit.MethodAndArgsCaller {
         Class<?> cl;
 
         try {
@@ -264,7 +264,7 @@
          * clears up all the stack frames that were required in setting
          * up the process.
          */
-        throw new Zygote.MethodAndArgsCaller(m, argv);
+        throw new ZygoteInit.MethodAndArgsCaller(m, argv);
     }
 
     public static final void main(String[] argv) {
@@ -301,7 +301,7 @@
      * @param argv arg strings
      */
     public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
-            throws Zygote.MethodAndArgsCaller {
+            throws ZygoteInit.MethodAndArgsCaller {
         if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
 
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
@@ -324,14 +324,14 @@
      * @param argv arg strings
      */
     public static void wrapperInit(int targetSdkVersion, String[] argv)
-            throws Zygote.MethodAndArgsCaller {
+            throws ZygoteInit.MethodAndArgsCaller {
         if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper");
 
         applicationInit(targetSdkVersion, argv, null);
     }
 
     private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
-            throws Zygote.MethodAndArgsCaller {
+            throws ZygoteInit.MethodAndArgsCaller {
         // If the application calls System.exit(), terminate the process
         // immediately without running any shutdown hooks.  It is not possible to
         // shutdown an Android application gracefully.  Among other things, the
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
deleted file mode 100644
index 2ed7aa2..0000000
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2016 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 com.android.internal.os;
-
-/**
- * Startup class for the WebView zygote process.
- *
- * See {@link ZygoteInit} for generic zygote startup documentation.
- *
- * @hide
- */
-class WebViewZygoteInit {
-    public static final String TAG = "WebViewZygoteInit";
-
-    public static void main(String argv[]) {
-        throw new RuntimeException("Not implemented yet");
-    }
-}
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index 594b6ab..c558cf8 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -74,14 +74,14 @@
                 }
             }
 
-            // Mimic system Zygote preloading.
+            // Mimic Zygote preloading.
             ZygoteInit.preload();
 
             // Launch the application.
             String[] runtimeArgs = new String[args.length - 2];
             System.arraycopy(args, 2, runtimeArgs, 0, runtimeArgs.length);
             RuntimeInit.wrapperInit(targetSdkVersion, runtimeArgs);
-        } catch (Zygote.MethodAndArgsCaller caller) {
+        } catch (ZygoteInit.MethodAndArgsCaller caller) {
             caller.run();
         }
     }
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index fc0ccb7..66cc975 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -22,9 +22,6 @@
 import android.system.ErrnoException;
 import android.system.Os;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
 /** @hide */
 public final class Zygote {
     /*
@@ -194,39 +191,4 @@
             command.append(" '").append(arg.replace("'", "'\\''")).append("'");
         }
     }
-
-    /**
-     * Helper exception class which holds a method and arguments and
-     * can call them. This is used as part of a trampoline to get rid of
-     * the initial process setup stack frames.
-     */
-    public static class MethodAndArgsCaller extends Exception
-            implements Runnable {
-        /** method to call */
-        private final Method mMethod;
-
-        /** argument array */
-        private final String[] mArgs;
-
-        public MethodAndArgsCaller(Method method, String[] args) {
-            mMethod = method;
-            mArgs = args;
-        }
-
-        public void run() {
-            try {
-                mMethod.invoke(null, new Object[] { mArgs });
-            } catch (IllegalAccessException ex) {
-                throw new RuntimeException(ex);
-            } catch (InvocationTargetException ex) {
-                Throwable cause = ex.getCause();
-                if (cause instanceof RuntimeException) {
-                    throw (RuntimeException) cause;
-                } else if (cause instanceof Error) {
-                    throw (Error) cause;
-                }
-                throw new RuntimeException(ex);
-            }
-        }
-    }
 }
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 132b022..85d84bb 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -117,7 +117,7 @@
 
     /**
      * Reads one start command from the command socket. If successful,
-     * a child is forked and a {@link Zygote.MethodAndArgsCaller}
+     * a child is forked and a {@link ZygoteInit.MethodAndArgsCaller}
      * exception is thrown in that child while in the parent process,
      * the method returns normally. On failure, the child is not
      * spawned and messages are printed to the log and stderr. Returns
@@ -126,10 +126,10 @@
      *
      * @return false if command socket should continue to be read from, or
      * true if an end-of-file has been encountered.
-     * @throws Zygote.MethodAndArgsCaller trampoline to invoke main()
+     * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main()
      * method in child process
      */
-    boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
+    boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
 
         String args[];
         Arguments parsedArgs = null;
@@ -214,7 +214,7 @@
                 fdsToClose[0] = fd.getInt$();
             }
 
-            fd = zygoteServer.getServerSocketFileDescriptor();
+            fd = ZygoteInit.getServerSocketFileDescriptor();
 
             if (fd != null) {
                 fdsToClose[1] = fd.getInt$();
@@ -238,13 +238,12 @@
         try {
             if (pid == 0) {
                 // in child
-                zygoteServer.closeServerSocket();
                 IoUtils.closeQuietly(serverPipeFd);
                 serverPipeFd = null;
                 handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
 
                 // should never get here, the child is expected to either
-                // throw Zygote.MethodAndArgsCaller or exec().
+                // throw ZygoteInit.MethodAndArgsCaller or exec().
                 return true;
             } else {
                 // in parent...pid of < 0 means failure
@@ -713,12 +712,12 @@
      * @param newStderr null-ok; stream to use for stderr until stdio
      * is reopened.
      *
-     * @throws Zygote.MethodAndArgsCaller on success to
+     * @throws ZygoteInit.MethodAndArgsCaller on success to
      * trampoline to code that invokes static main.
      */
     private void handleChildProc(Arguments parsedArgs,
             FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
-            throws Zygote.MethodAndArgsCaller {
+            throws ZygoteInit.MethodAndArgsCaller {
         /**
          * By the time we get here, the native code has closed the two actual Zygote
          * socket connections, and substituted /dev/null in their place.  The LocalSocket
@@ -726,6 +725,8 @@
          */
 
         closeSocket();
+        ZygoteInit.closeServerSocket();
+
         if (descriptors != null) {
             try {
                 Os.dup2(descriptors[0], STDIN_FILENO);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 52b72a4..9efa833 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.os;
 
+import static android.system.OsConstants.POLLIN;
 import static android.system.OsConstants.S_IRWXG;
 import static android.system.OsConstants.S_IRWXO;
 
@@ -30,11 +31,11 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
-import android.os.ZygoteProcess;
 import android.security.keystore.AndroidKeyStoreProvider;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
+import android.system.StructPollfd;
 import android.text.Hyphenator;
 import android.util.EventLog;
 import android.util.Log;
@@ -51,13 +52,17 @@
 import libcore.io.IoUtils;
 
 import java.io.BufferedReader;
+import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.security.Security;
 import java.security.Provider;
+import java.util.ArrayList;
 
 /**
  * Startup class for the zygote process.
@@ -77,6 +82,8 @@
     private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
     private static final String PROPERTY_RUNNING_IN_CONTAINER = "ro.boot.container";
 
+    private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
+
     private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
     private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
 
@@ -87,8 +94,11 @@
 
     private static final String SOCKET_NAME_ARG = "--socket-name=";
 
+    private static LocalServerSocket sServerSocket;
+
     /**
-     * Used to pre-load resources.
+     * Used to pre-load resources.  We hold a global reference on it so it
+     * never gets destroyed.
      */
     private static Resources mResources;
 
@@ -100,6 +110,78 @@
     /** Controls whether we should preload resources during zygote init. */
     public static final boolean PRELOAD_RESOURCES = true;
 
+    /**
+     * Registers a server socket for zygote command connections
+     *
+     * @throws RuntimeException when open fails
+     */
+    private static void registerZygoteSocket(String socketName) {
+        if (sServerSocket == null) {
+            int fileDesc;
+            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
+            try {
+                String env = System.getenv(fullSocketName);
+                fileDesc = Integer.parseInt(env);
+            } catch (RuntimeException ex) {
+                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
+            }
+
+            try {
+                FileDescriptor fd = new FileDescriptor();
+                fd.setInt$(fileDesc);
+                sServerSocket = new LocalServerSocket(fd);
+            } catch (IOException ex) {
+                throw new RuntimeException(
+                        "Error binding to local socket '" + fileDesc + "'", ex);
+            }
+        }
+    }
+
+    /**
+     * Waits for and accepts a single command connection. Throws
+     * RuntimeException on failure.
+     */
+    private static ZygoteConnection acceptCommandPeer(String abiList) {
+        try {
+            return new ZygoteConnection(sServerSocket.accept(), abiList);
+        } catch (IOException ex) {
+            throw new RuntimeException(
+                    "IOException during accept()", ex);
+        }
+    }
+
+    /**
+     * Close and clean up zygote sockets. Called on shutdown and on the
+     * child's exit path.
+     */
+    static void closeServerSocket() {
+        try {
+            if (sServerSocket != null) {
+                FileDescriptor fd = sServerSocket.getFileDescriptor();
+                sServerSocket.close();
+                if (fd != null) {
+                    Os.close(fd);
+                }
+            }
+        } catch (IOException ex) {
+            Log.e(TAG, "Zygote:  error closing sockets", ex);
+        } catch (ErrnoException ex) {
+            Log.e(TAG, "Zygote:  error closing descriptor", ex);
+        }
+
+        sServerSocket = null;
+    }
+
+    /**
+     * Return the server socket's underlying file descriptor, so that
+     * ZygoteConnection can pass it to the native code for proper
+     * closure after a child process is forked off.
+     */
+
+    static FileDescriptor getServerSocketFileDescriptor() {
+        return sServerSocket.getFileDescriptor();
+    }
+
     private static final int UNPRIVILEGED_UID = 9999;
     private static final int UNPRIVILEGED_GID = 9999;
 
@@ -422,7 +504,9 @@
      */
     private static void handleSystemServerProcess(
             ZygoteConnection.Arguments parsedArgs)
-            throws Zygote.MethodAndArgsCaller {
+            throws ZygoteInit.MethodAndArgsCaller {
+
+        closeServerSocket();
 
         // set umask to 0077 so new files and directories will default to owner-only permissions.
         Os.umask(S_IRWXG | S_IRWXO);
@@ -525,8 +609,8 @@
     /**
      * Prepare the arguments and fork for the system server process.
      */
-    private static boolean startSystemServer(String abiList, String socketName, ZygoteServer zygoteServer)
-            throws Zygote.MethodAndArgsCaller, RuntimeException {
+    private static boolean startSystemServer(String abiList, String socketName)
+            throws MethodAndArgsCaller, RuntimeException {
         long capabilities = posixCapabilitiesAsBits(
             OsConstants.CAP_IPC_LOCK,
             OsConstants.CAP_KILL,
@@ -582,7 +666,6 @@
                 waitForSecondaryZygote(socketName);
             }
 
-            zygoteServer.closeServerSocket();
             handleSystemServerProcess(parsedArgs);
         }
 
@@ -604,8 +687,6 @@
     }
 
     public static void main(String argv[]) {
-        ZygoteServer zygoteServer = new ZygoteServer();
-
         // Mark zygote start. This ensures that thread creation will throw
         // an error.
         ZygoteHooks.startZygoteNoThreadCreation();
@@ -642,7 +723,7 @@
                 throw new RuntimeException("No ABI list supplied.");
             }
 
-            zygoteServer.registerServerSocket(socketName);
+            registerZygoteSocket(socketName);
             Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygotePreload");
             EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                 SystemClock.uptimeMillis());
@@ -659,6 +740,8 @@
             gcAndFinalize();
             Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
 
+            Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
+
             // Disable tracing so that forked processes do not inherit stale tracing tags from
             // Zygote.
             Trace.setTracingEnabled(false);
@@ -669,18 +752,18 @@
             ZygoteHooks.stopZygoteNoThreadCreation();
 
             if (startSystemServer) {
-                startSystemServer(abiList, socketName, zygoteServer);
+                startSystemServer(abiList, socketName);
             }
 
             Log.i(TAG, "Accepting command socket connections");
-            zygoteServer.runSelectLoop(abiList);
+            runSelectLoop(abiList);
 
-            zygoteServer.closeServerSocket();
-        } catch (Zygote.MethodAndArgsCaller caller) {
+            closeServerSocket();
+        } catch (MethodAndArgsCaller caller) {
             caller.run();
         } catch (RuntimeException ex) {
-            Log.e(TAG, "System zygote died with exception", ex);
-            zygoteServer.closeServerSocket();
+            Log.e(TAG, "Zygote died with exception", ex);
+            closeServerSocket();
             throw ex;
         }
     }
@@ -701,8 +784,7 @@
                 Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
         while (true) {
             try {
-                final ZygoteProcess.ZygoteState zs =
-                        ZygoteProcess.ZygoteState.connect(otherZygoteName);
+                final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName);
                 zs.close();
                 break;
             } catch (IOException ioe) {
@@ -717,8 +799,89 @@
     }
 
     /**
+     * Runs the zygote process's select loop. Accepts new connections as
+     * they happen, and reads commands from connections one spawn-request's
+     * worth at a time.
+     *
+     * @throws MethodAndArgsCaller in a child process when a main() should
+     * be executed.
+     */
+    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
+        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
+        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
+
+        fds.add(sServerSocket.getFileDescriptor());
+        peers.add(null);
+
+        while (true) {
+            StructPollfd[] pollFds = new StructPollfd[fds.size()];
+            for (int i = 0; i < pollFds.length; ++i) {
+                pollFds[i] = new StructPollfd();
+                pollFds[i].fd = fds.get(i);
+                pollFds[i].events = (short) POLLIN;
+            }
+            try {
+                Os.poll(pollFds, -1);
+            } catch (ErrnoException ex) {
+                throw new RuntimeException("poll failed", ex);
+            }
+            for (int i = pollFds.length - 1; i >= 0; --i) {
+                if ((pollFds[i].revents & POLLIN) == 0) {
+                    continue;
+                }
+                if (i == 0) {
+                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
+                    peers.add(newPeer);
+                    fds.add(newPeer.getFileDesciptor());
+                } else {
+                    boolean done = peers.get(i).runOnce();
+                    if (done) {
+                        peers.remove(i);
+                        fds.remove(i);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
      * Class not instantiable.
      */
     private ZygoteInit() {
     }
+
+    /**
+     * Helper exception class which holds a method and arguments and
+     * can call them. This is used as part of a trampoline to get rid of
+     * the initial process setup stack frames.
+     */
+    public static class MethodAndArgsCaller extends Exception
+            implements Runnable {
+        /** method to call */
+        private final Method mMethod;
+
+        /** argument array */
+        private final String[] mArgs;
+
+        public MethodAndArgsCaller(Method method, String[] args) {
+            mMethod = method;
+            mArgs = args;
+        }
+
+        public void run() {
+            try {
+                mMethod.invoke(null, new Object[] { mArgs });
+            } catch (IllegalAccessException ex) {
+                throw new RuntimeException(ex);
+            } catch (InvocationTargetException ex) {
+                Throwable cause = ex.getCause();
+                if (cause instanceof RuntimeException) {
+                    throw (RuntimeException) cause;
+                } else if (cause instanceof Error) {
+                    throw (Error) cause;
+                }
+                throw new RuntimeException(ex);
+            }
+        }
+    }
 }
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
deleted file mode 100644
index ab876410..0000000
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2007 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 com.android.internal.os;
-
-import static android.system.OsConstants.POLLIN;
-
-import android.net.LocalServerSocket;
-import android.system.Os;
-import android.system.ErrnoException;
-import android.system.StructPollfd;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.FileDescriptor;
-import java.util.ArrayList;
-
-/**
- * Server socket class for zygote processes.
- *
- * Provides functions to wait for commands on a UNIX domain socket, and fork
- * off child processes that inherit the initial state of the VM.%
- *
- * Please see {@link ZygoteConnection.Arguments} for documentation on the
- * client protocol.
- */
-class ZygoteServer {
-    public static final String TAG = "ZygoteServer";
-
-    private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
-
-    private LocalServerSocket mServerSocket;
-
-    ZygoteServer() {
-    }
-
-    /**
-     * Registers a server socket for zygote command connections
-     *
-     * @throws RuntimeException when open fails
-     */
-    void registerServerSocket(String socketName) {
-        if (mServerSocket == null) {
-            int fileDesc;
-            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
-            try {
-                String env = System.getenv(fullSocketName);
-                fileDesc = Integer.parseInt(env);
-            } catch (RuntimeException ex) {
-                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
-            }
-
-            try {
-                FileDescriptor fd = new FileDescriptor();
-                fd.setInt$(fileDesc);
-                mServerSocket = new LocalServerSocket(fd);
-            } catch (IOException ex) {
-                throw new RuntimeException(
-                        "Error binding to local socket '" + fileDesc + "'", ex);
-            }
-        }
-    }
-
-    /**
-     * Waits for and accepts a single command connection. Throws
-     * RuntimeException on failure.
-     */
-    private ZygoteConnection acceptCommandPeer(String abiList) {
-        try {
-            return new ZygoteConnection(mServerSocket.accept(), abiList);
-        } catch (IOException ex) {
-            throw new RuntimeException(
-                    "IOException during accept()", ex);
-        }
-    }
-
-    /**
-     * Close and clean up zygote sockets. Called on shutdown and on the
-     * child's exit path.
-     */
-    void closeServerSocket() {
-        try {
-            if (mServerSocket != null) {
-                FileDescriptor fd = mServerSocket.getFileDescriptor();
-                mServerSocket.close();
-                if (fd != null) {
-                    Os.close(fd);
-                }
-            }
-        } catch (IOException ex) {
-            Log.e(TAG, "Zygote:  error closing sockets", ex);
-        } catch (ErrnoException ex) {
-            Log.e(TAG, "Zygote:  error closing descriptor", ex);
-        }
-
-        mServerSocket = null;
-    }
-
-    /**
-     * Return the server socket's underlying file descriptor, so that
-     * ZygoteConnection can pass it to the native code for proper
-     * closure after a child process is forked off.
-     */
-
-    FileDescriptor getServerSocketFileDescriptor() {
-        return mServerSocket.getFileDescriptor();
-    }
-
-    /**
-     * Runs the zygote process's select loop. Accepts new connections as
-     * they happen, and reads commands from connections one spawn-request's
-     * worth at a time.
-     *
-     * @throws Zygote.MethodAndArgsCaller in a child process when a main()
-     * should be executed.
-     */
-    void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
-        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
-        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
-
-        fds.add(mServerSocket.getFileDescriptor());
-        peers.add(null);
-
-        while (true) {
-            StructPollfd[] pollFds = new StructPollfd[fds.size()];
-            for (int i = 0; i < pollFds.length; ++i) {
-                pollFds[i] = new StructPollfd();
-                pollFds[i].fd = fds.get(i);
-                pollFds[i].events = (short) POLLIN;
-            }
-            try {
-                Os.poll(pollFds, -1);
-            } catch (ErrnoException ex) {
-                throw new RuntimeException("poll failed", ex);
-            }
-            for (int i = pollFds.length - 1; i >= 0; --i) {
-                if ((pollFds[i].revents & POLLIN) == 0) {
-                    continue;
-                }
-                if (i == 0) {
-                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
-                    peers.add(newPeer);
-                    fds.add(newPeer.getFileDesciptor());
-                } else {
-                    boolean done = peers.get(i).runOnce(this);
-                    if (done) {
-                        peers.remove(i);
-                        fds.remove(i);
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 867168a..08b204a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6703,7 +6703,7 @@
 
         ArraySet<String> completedIsas = new ArraySet<String>();
         for (String abi : Build.SUPPORTED_ABIS) {
-            Process.zygoteProcess.establishZygoteConnectionForAbi(abi);
+            Process.establishZygoteConnectionForAbi(abi);
             final String instructionSet = VMRuntime.getInstructionSet(abi);
             if (!completedIsas.contains(instructionSet)) {
                 try {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 5ae408b..330f46a 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -163,6 +163,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
@@ -365,6 +366,7 @@
 
     private final AppOpsManager mAppOps;
 
+    private final MyPackageMonitor mPackageMonitor;
     private final IPackageManager mIPm;
 
 
@@ -409,6 +411,8 @@
 
         mAppOps = context.getSystemService(AppOpsManager.class);
 
+        mPackageMonitor = new MyPackageMonitor();
+
         // Expose private service for system components to use.
         LocalServices.addService(NetworkPolicyManagerInternal.class,
                 new NetworkPolicyManagerInternalImpl());
@@ -536,6 +540,8 @@
 
         mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
 
+        mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
+
         synchronized (mRulesLock) {
             updatePowerSaveWhitelistLocked();
             mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
@@ -725,7 +731,6 @@
             if (LOGV) Slog.v(TAG, "ACTION_UID_REMOVED for uid=" + uid);
             synchronized (mRulesLock) {
                 mUidPolicy.delete(uid);
-                removeRestrictBackgroundWhitelistedUidLocked(uid, true, true);
                 updateRestrictionRulesForUidLocked(uid);
                 writePolicyLocked();
             }
@@ -3474,6 +3479,18 @@
         }
     }
 
+    private class MyPackageMonitor extends PackageMonitor {
+
+        @Override
+        public void onPackageRemoved(String packageName, int uid) {
+            if (LOGV) Slog.v(TAG, "onPackageRemoved: " + packageName + " ->" + uid);
+            synchronized (mRulesLock) {
+                removeRestrictBackgroundWhitelistedUidLocked(uid, true, true);
+                updateRestrictionRulesForUidLocked(uid);
+            }
+        }
+    }
+
     private class NetworkPolicyManagerInternalImpl extends NetworkPolicyManagerInternal {
 
         @Override