Merge "jni: liblog reading error API incorrect"
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index bdbb08c..3e8d6a0 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -10,8 +10,9 @@
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <utils/Log.h>
-#include <cutils/process_name.h>
 #include <cutils/memory.h>
+#include <cutils/process_name.h>
+#include <cutils/properties.h>
 #include <cutils/trace.h>
 #include <android_runtime/AndroidRuntime.h>
 
@@ -32,30 +33,20 @@
 public:
     AppRuntime(char* argBlockStart, const size_t argBlockLength)
         : AndroidRuntime(argBlockStart, argBlockLength)
-        , mParentDir(NULL)
-        , mClassName(NULL)
         , mClass(NULL)
-        , mArgC(0)
-        , mArgV(NULL)
     {
     }
 
-#if 0
-    // this appears to be unused
-    const char* getParentDir() const
-    {
-        return mParentDir;
-    }
-#endif
-
-    const char* getClassName() const
-    {
-        return mClassName;
+    void setClassNameAndArgs(const String8& className, int argc, char * const *argv) {
+        mClassName = className;
+        for (int i = 0; i < argc; ++i) {
+             mArgs.add(String8(argv[i]));
+        }
     }
 
     virtual void onVmCreated(JNIEnv* env)
     {
-        if (mClassName == NULL) {
+        if (mClassName.isEmpty()) {
             return; // Zygote. Nothing to do here.
         }
 
@@ -72,10 +63,10 @@
          * executing boot class Java code and thereby deny ourselves access to
          * non-boot classes.
          */
-        char* slashClassName = toSlashClassName(mClassName);
+        char* slashClassName = toSlashClassName(mClassName.string());
         mClass = env->FindClass(slashClassName);
         if (mClass == NULL) {
-            ALOGE("ERROR: could not find class '%s'\n", mClassName);
+            ALOGE("ERROR: could not find class '%s'\n", mClassName.string());
         }
         free(slashClassName);
 
@@ -89,7 +80,7 @@
         proc->startThreadPool();
 
         AndroidRuntime* ar = AndroidRuntime::getRuntime();
-        ar->callMain(mClassName, mClass, mArgC, mArgV);
+        ar->callMain(mClassName, mClass, mArgs);
 
         IPCThreadState::self()->stopProcess();
     }
@@ -115,11 +106,9 @@
     }
 
 
-    const char* mParentDir;
-    const char* mClassName;
+    String8 mClassName;
+    Vector<String8> mArgs;
     jclass mClass;
-    int mArgC;
-    const char* const* mArgV;
 };
 
 }
@@ -147,6 +136,12 @@
     return (end - start);
 }
 
+#if defined(__LP64__)
+static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64";
+#else
+static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist32";
+#endif
+
 int main(int argc, char* const argv[])
 {
     AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
@@ -155,7 +150,26 @@
     argc--;
     argv++;
 
-    // Everything up to '--' or first non '-' arg goes to the vm
+    // Everything up to '--' or first non '-' arg goes to the vm.
+    //
+    // The first argument after the VM args is the "parent dir", which
+    // is currently unused.
+    //
+    // After the parent dir, we expect one or more the following internal
+    // arguments :
+    //
+    // --zygote : Start in zygote mode
+    // --start-system-server : Start the system server.
+    // --application : Start in application (stand alone, non zygote) mode.
+    // --nice-name : The nice name for this process.
+    //
+    // For non zygote starts, these arguments will be followed by
+    // the main class name. All remaining arguments are passed to
+    // the main method of this class.
+    //
+    // For zygote starts, all remaining arguments are passed to the zygote.
+    // main function.
+
 
     int i = runtime.addVmArguments(argc, argv);
 
@@ -163,14 +177,13 @@
     bool zygote = false;
     bool startSystemServer = false;
     bool application = false;
-    const char* parentDir = NULL;
     const char* niceName = NULL;
-    const char* className = NULL;
+    String8 className;
+
+    ++i;  // Skip unused "parent dir" argument.
     while (i < argc) {
         const char* arg = argv[i++];
-        if (!parentDir) {
-            parentDir = arg;
-        } else if (strcmp(arg, "--zygote") == 0) {
+        if (strcmp(arg, "--zygote") == 0) {
             zygote = true;
             niceName = "zygote";
         } else if (strcmp(arg, "--start-system-server") == 0) {
@@ -180,28 +193,52 @@
         } else if (strncmp(arg, "--nice-name=", 12) == 0) {
             niceName = arg + 12;
         } else {
-            className = arg;
+            className.setTo(arg);
             break;
         }
     }
 
+    Vector<String8> args;
+    if (!className.isEmpty()) {
+        // We're not in zygote mode, the only argument we need to pass
+        // to RuntimeInit is the application argument.
+        //
+        // The Remainder of args get passed to startup class main(). Make
+        // copies of them before we overwrite them with the process name.
+        args.add(application ? String8("application") : String8("tool"));
+        runtime.setClassNameAndArgs(className, argc - i, argv + i);
+    } else {
+        if (startSystemServer) {
+            args.add(String8("start-system-server"));
+        }
+
+        char prop[PROP_VALUE_MAX];
+        if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
+            LOG_ALWAYS_FATAL("app_process: Unable to deterimine ABI list from property %s.",
+                ABI_LIST_PROPERTY);
+            return 11;
+        }
+
+        String8 abiFlag("--abi-list=");
+        abiFlag.append(prop);
+        args.add(abiFlag);
+
+        // In zygote mode, pass all remaining arguments to the zygote
+        // main() method.
+        for (; i < argc; ++i) {
+            args.add(String8(argv[i]));
+        }
+    }
+
     if (niceName && *niceName) {
         runtime.setArgv0(niceName);
         set_process_name(niceName);
     }
 
-    runtime.mParentDir = parentDir;
-
     if (zygote) {
-        runtime.start("com.android.internal.os.ZygoteInit",
-                startSystemServer ? "start-system-server" : "");
+        runtime.start("com.android.internal.os.ZygoteInit", args);
     } else if (className) {
-        // Remainder of args get passed to startup class main()
-        runtime.mClassName = className;
-        runtime.mArgC = argc - i;
-        runtime.mArgV = argv + i;
-        runtime.start("com.android.internal.os.RuntimeInit",
-                application ? "application" : "tool");
+        runtime.start("com.android.internal.os.RuntimeInit", args);
     } else {
         fprintf(stderr, "Error: no class name or --zygote supplied.\n");
         app_usage();
diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java
index ee7a4c6..6cec55a 100644
--- a/core/java/android/os/MemoryFile.java
+++ b/core/java/android/os/MemoryFile.java
@@ -63,12 +63,17 @@
      * Allocates a new ashmem region. The region is initially not purgable.
      *
      * @param name optional name for the file (can be null).
-     * @param length of the memory file in bytes.
+     * @param length of the memory file in bytes, must be non-negative.
      * @throws IOException if the memory file could not be created.
      */
     public MemoryFile(String name, int length) throws IOException {
         mLength = length;
-        mFD = native_open(name, length);
+        if (length >= 0) {
+            mFD = native_open(name, length);
+        } else {
+            throw new IOException("Invalid length: " + length);
+        }
+
         if (length > 0) {
             mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
         } else {
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 4cf8767..7d2d051 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -19,15 +19,15 @@
 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;
 import libcore.io.Libcore;
 
 /*package*/ class ZygoteStartFailedEx extends Exception {
@@ -48,17 +48,7 @@
 
     private static final String ZYGOTE_SOCKET = "zygote";
 
-    /**
-     * Name of a process for running the platform's media services.
-     * {@hide}
-     */
-    public static final String ANDROID_SHARED_MEDIA = "com.android.process.media";
-
-    /**
-     * Name of the process that Google content providers can share.
-     * {@hide}
-     */
-    public static final String GOOGLE_SHARED_APP_CONTENT = "com.google.process.content";
+    private static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary";
 
     /**
      * Defines the UID/GID under which system code runs.
@@ -345,15 +335,112 @@
     public static final int SIGNAL_QUIT = 3;
     public static final int SIGNAL_KILL = 9;
     public static final int SIGNAL_USR1 = 10;
-    
-    // State for communicating with zygote process
 
-    static LocalSocket sZygoteSocket;
-    static DataInputStream sZygoteInputStream;
-    static BufferedWriter sZygoteWriter;
+    /**
+     * State for communicating with the zygote process.
+     */
+    static class ZygoteState {
+        final LocalSocket socket;
+        final DataInputStream inputStream;
+        final BufferedWriter writer;
+        final List<String> abiList;
 
-    /** true if previous zygote open failed */
-    static boolean sPreviousZygoteOpenFailed;
+        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;
+        }
+
+        static ZygoteState connect(String socketAddress, int tries) throws ZygoteStartFailedEx {
+            LocalSocket zygoteSocket = null;
+            DataInputStream zygoteInputStream = null;
+            BufferedWriter zygoteWriter = null;
+
+            /*
+             * 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("Zygote", "Zygote not up yet, sleeping...");
+                        Thread.sleep(ZYGOTE_RETRY_MILLIS);
+                    } catch (InterruptedException ex) {
+                        throw new ZygoteStartFailedEx(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;
+                }
+            }
+
+            if (zygoteSocket == null) {
+                throw new ZygoteStartFailedEx("connect failed");
+            }
+
+            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);
+        }
+
+        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.
@@ -395,7 +482,9 @@
                                   String[] zygoteArgs) {
         try {
             return startViaZygote(processClass, niceName, uid, gid, gids,
-                    debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs);
+                    debugFlags, mountExternal, targetSdkVersion, seInfo,
+                    null, /* zygoteAbi TODO: Replace this with the real ABI */
+                    zygoteArgs);
         } catch (ZygoteStartFailedEx ex) {
             Log.e(LOG_TAG,
                     "Starting VM process through Zygote failed");
@@ -408,78 +497,31 @@
     static final int ZYGOTE_RETRY_MILLIS = 500;
 
     /**
-     * Tries to open socket to Zygote process if not already open. If
-     * already open, does nothing.  May block and retry.
+     * Queries the zygote for the list of ABIS it supports.
+     *
+     * @throws ZygoteStartFailedEx if the query failed.
      */
-    private static void openZygoteSocketIfNeeded() 
+    private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
             throws ZygoteStartFailedEx {
+        try {
 
-        int retryCount;
+            // 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();
 
-        if (sPreviousZygoteOpenFailed) {
-            /*
-             * If we've failed before, expect that we'll fail again and
-             * don't pause for retries.
-             */
-            retryCount = 0;
-        } else {
-            retryCount = 10;            
-        }
+            // The response is a length prefixed stream of ASCII bytes.
+            int numBytes = inputStream.readInt();
+            byte[] bytes = new byte[numBytes];
+            inputStream.readFully(bytes);
 
-        /*
-         * 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...
-         */
-        for (int retry = 0
-                ; (sZygoteSocket == null) && (retry < (retryCount + 1))
-                ; retry++ ) {
-
-            if (retry > 0) {
-                try {
-                    Log.i("Zygote", "Zygote not up yet, sleeping...");
-                    Thread.sleep(ZYGOTE_RETRY_MILLIS);
-                } catch (InterruptedException ex) {
-                    // should never happen
-                }
-            }
-
-            try {
-                sZygoteSocket = new LocalSocket();
-
-                sZygoteSocket.connect(new LocalSocketAddress(ZYGOTE_SOCKET, 
-                        LocalSocketAddress.Namespace.RESERVED));
-
-                sZygoteInputStream
-                        = new DataInputStream(sZygoteSocket.getInputStream());
-
-                sZygoteWriter =
-                    new BufferedWriter(
-                            new OutputStreamWriter(
-                                    sZygoteSocket.getOutputStream()),
-                            256);
-
-                Log.i("Zygote", "Process: zygote socket opened");
-
-                sPreviousZygoteOpenFailed = false;
-                break;
-            } catch (IOException ex) {
-                if (sZygoteSocket != null) {
-                    try {
-                        sZygoteSocket.close();
-                    } catch (IOException ex2) {
-                        Log.e(LOG_TAG,"I/O exception on close after exception",
-                                ex2);
-                    }
-                }
-
-                sZygoteSocket = null;
-            }
-        }
-
-        if (sZygoteSocket == null) {
-            sPreviousZygoteOpenFailed = true;
-            throw new ZygoteStartFailedEx("connect failed");                 
+            return new String(bytes, StandardCharsets.US_ASCII);
+        } catch (IOException ioe) {
+            throw new ZygoteStartFailedEx(ioe);
         }
     }
 
@@ -487,14 +529,12 @@
      * 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.
-     * @param args argument list
-     * @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 zygoteSendArgsAndGetResult(ArrayList<String> args)
+    private static ProcessStartResult zygoteSendArgsAndGetResult(
+            ZygoteState zygoteState, ArrayList<String> args)
             throws ZygoteStartFailedEx {
-        openZygoteSocketIfNeeded();
-
         try {
             /**
              * See com.android.internal.os.ZygoteInit.readArgumentList()
@@ -506,9 +546,11 @@
              * 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;
 
-            sZygoteWriter.write(Integer.toString(args.size()));
-            sZygoteWriter.newLine();
+            writer.write(Integer.toString(args.size()));
+            writer.newLine();
 
             int sz = args.size();
             for (int i = 0; i < sz; i++) {
@@ -517,32 +559,22 @@
                     throw new ZygoteStartFailedEx(
                             "embedded newlines not allowed");
                 }
-                sZygoteWriter.write(arg);
-                sZygoteWriter.newLine();
+                writer.write(arg);
+                writer.newLine();
             }
 
-            sZygoteWriter.flush();
+            writer.flush();
 
             // Should there be a timeout on this?
             ProcessStartResult result = new ProcessStartResult();
-            result.pid = sZygoteInputStream.readInt();
+            result.pid = inputStream.readInt();
             if (result.pid < 0) {
                 throw new ZygoteStartFailedEx("fork() failed");
             }
-            result.usingWrapper = sZygoteInputStream.readBoolean();
+            result.usingWrapper = inputStream.readBoolean();
             return result;
         } catch (IOException ex) {
-            try {
-                if (sZygoteSocket != null) {
-                    sZygoteSocket.close();
-                }
-            } catch (IOException ex2) {
-                // we're going to fail anyway
-                Log.e(LOG_TAG,"I/O exception on routine close", ex2);
-            }
-
-            sZygoteSocket = null;
-
+            zygoteState.close();
             throw new ZygoteStartFailedEx(ex);
         }
     }
@@ -559,6 +591,7 @@
      * @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 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
@@ -570,6 +603,7 @@
                                   int debugFlags, int mountExternal,
                                   int targetSdkVersion,
                                   String seInfo,
+                                  String abi,
                                   String[] extraArgs)
                                   throws ZygoteStartFailedEx {
         synchronized(Process.class) {
@@ -637,10 +671,61 @@
                 }
             }
 
-            return zygoteSendArgsAndGetResult(argsForZygote);
+            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
         }
     }
-    
+
+    /**
+     * 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));
+        }
+
+        // TODO: Revert this temporary change. This is required to test
+        // and submit this change ahead of the package manager changes
+        // that supply this abi.
+        if (abi == null) {
+            return primaryZygoteState;
+        }
+
+        if (primaryZygoteState.matches(abi)) {
+            return primaryZygoteState;
+        }
+
+        // The primary zygote didn't match. Try the secondary.
+        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
+            secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET,
+                    getNumTries(secondaryZygoteState));
+        }
+
+        if (secondaryZygoteState.matches(abi)) {
+            return secondaryZygoteState;
+        }
+
+        throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
+    }
+
     /**
      * Returns elapsed milliseconds of the time this process has run.
      * @return  Returns the number of milliseconds this process has return.
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index aad534c..58a8e62 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -22,9 +22,7 @@
 import android.os.SELinux;
 import android.os.SystemProperties;
 import android.util.Log;
-
 import dalvik.system.PathClassLoader;
-
 import java.io.BufferedReader;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -34,8 +32,8 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-
 import libcore.io.ErrnoException;
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
@@ -59,7 +57,7 @@
     private static final int CONNECTION_TIMEOUT_MILLIS = 1000;
 
     /** max number of arguments that a connection can specify */
-    private static final int MAX_ZYGOTE_ARGC=1024;
+    private static final int MAX_ZYGOTE_ARGC = 1024;
 
     /**
      * The command socket.
@@ -73,15 +71,18 @@
     private final BufferedReader mSocketReader;
     private final Credentials peer;
     private final String peerSecurityContext;
+    private final String abiList;
 
     /**
      * Constructs instance from connected socket.
      *
      * @param socket non-null; connected socket
+     * @param abiList non-null; a list of ABIs this zygote supports.
      * @throws IOException
      */
-    ZygoteConnection(LocalSocket socket) throws IOException {
+    ZygoteConnection(LocalSocket socket, String abiList) throws IOException {
         mSocket = socket;
+        this.abiList = abiList;
 
         mSocketOutStream
                 = new DataOutputStream(socket.getOutputStream());
@@ -111,43 +112,6 @@
     }
 
     /**
-     * Reads start commands from an open command socket.
-     * Start commands are presently a pair of newline-delimited lines
-     * indicating a) class to invoke main() on b) nice name to set argv[0] to.
-     * Continues to read commands and forkAndSpecialize children until
-     * the socket is closed. This method is used in ZYGOTE_FORK_MODE
-     *
-     * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main()
-     * method in child process
-     */
-    void run() throws ZygoteInit.MethodAndArgsCaller {
-
-        int loopCount = ZygoteInit.GC_LOOP_COUNT;
-
-        while (true) {
-            /*
-             * Call gc() before we block in readArgumentList().
-             * It's work that has to be done anyway, and it's better
-             * to avoid making every child do it.  It will also
-             * madvise() any free memory as a side-effect.
-             *
-             * Don't call it every time, because walking the entire
-             * heap is a lot of overhead to free a few hundred bytes.
-             */
-            if (loopCount <= 0) {
-                ZygoteInit.gc();
-                loopCount = ZygoteInit.GC_LOOP_COUNT;
-            } else {
-                loopCount--;
-            }
-
-            if (runOnce()) {
-                break;
-            }
-        }
-    }
-
-    /**
      * Reads one start command from the command socket. If successful,
      * a child is forked and a {@link ZygoteInit.MethodAndArgsCaller}
      * exception is thrown in that child while in the parent process,
@@ -196,6 +160,11 @@
 
         try {
             parsedArgs = new Arguments(args);
+
+            if (parsedArgs.abiListQuery) {
+                return handleAbiListQuery();
+            }
+
             if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
                 throw new ZygoteSecurityException("Client may not specify capabilities: " +
                         "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
@@ -287,6 +256,18 @@
         }
     }
 
+    private boolean handleAbiListQuery() {
+        try {
+            final byte[] abiListBytes = abiList.getBytes(StandardCharsets.US_ASCII);
+            mSocketOutStream.writeInt(abiListBytes.length);
+            mSocketOutStream.write(abiListBytes);
+            return false;
+        } catch (IOException ioe) {
+            Log.e(TAG, "Error writing to command socket", ioe);
+            return true;
+        }
+    }
+
     /**
      * Closes socket associated with this connection.
      */
@@ -388,6 +369,11 @@
         String remainingArgs[];
 
         /**
+         * Whether the current arguments constitute an ABI list query.
+         */
+        boolean abiListQuery;
+
+        /**
          * Constructs instance and parses args
          * @param args zygote command-line args
          * @throws IllegalArgumentException
@@ -540,6 +526,8 @@
                     mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
                 } else if (arg.equals("--mount-external-multiuser-all")) {
                     mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
+                } else if (arg.equals("--query-abi-list")) {
+                    abiListQuery = true;
                 } else {
                     break;
                 }
@@ -975,7 +963,7 @@
             mSocketOutStream.writeInt(pid);
             mSocketOutStream.writeBoolean(usingWrapper);
         } catch (IOException ex) {
-            Log.e(TAG, "Error reading from command socket", ex);
+            Log.e(TAG, "Error writing to command socket", ex);
             return true;
         }
 
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 5be29b4..495cc5f 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -64,7 +64,7 @@
 
     private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
 
-    private static final String ANDROID_SOCKET_ENV = "ANDROID_SOCKET_zygote";
+    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;
@@ -72,8 +72,9 @@
     /** when preloading, GC after allocating this many bytes */
     private static final int PRELOAD_GC_THRESHOLD = 50000;
 
-    public static final String USAGE_STRING =
-            " <\"start-system-server\"|\"\" for startSystemServer>";
+    private static final String ABI_LIST_ARG = "--abi-list=";
+
+    private static final String SOCKET_NAME_ARG = "--socket-name=";
 
     private static LocalServerSocket sServerSocket;
 
@@ -150,15 +151,15 @@
      *
      * @throws RuntimeException when open fails
      */
-    private static void registerZygoteSocket() {
+    private static void registerZygoteSocket(String socketName) {
         if (sServerSocket == null) {
             int fileDesc;
+            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
             try {
-                String env = System.getenv(ANDROID_SOCKET_ENV);
+                String env = System.getenv(fullSocketName);
                 fileDesc = Integer.parseInt(env);
             } catch (RuntimeException ex) {
-                throw new RuntimeException(
-                        ANDROID_SOCKET_ENV + " unset or invalid", ex);
+                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
             }
 
             try {
@@ -175,9 +176,9 @@
      * Waits for and accepts a single command connection. Throws
      * RuntimeException on failure.
      */
-    private static ZygoteConnection acceptCommandPeer() {
+    private static ZygoteConnection acceptCommandPeer(String abiList) {
         try {
-            return new ZygoteConnection(sServerSocket.accept());
+            return new ZygoteConnection(sServerSocket.accept(), abiList);
         } catch (IOException ex) {
             throw new RuntimeException(
                     "IOException during accept()", ex);
@@ -567,7 +568,26 @@
             // Start profiling the zygote initialization.
             SamplingProfilerIntegration.start();
 
-            registerZygoteSocket();
+            boolean startSystemServer = false;
+            String socketName = "zygote";
+            String abiList = null;
+            for (int i = 1; i < argv.length; i++) {
+                if ("start-system-server".equals(argv[i])) {
+                    startSystemServer = true;
+                } else if (argv[i].startsWith(ABI_LIST_ARG)) {
+                    abiList = argv[i].substring(ABI_LIST_ARG.length());
+                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
+                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());
+                } else {
+                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
+                }
+            }
+
+            if (abiList == null) {
+                throw new RuntimeException("No ABI list supplied.");
+            }
+
+            registerZygoteSocket(socketName);
             EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                 SystemClock.uptimeMillis());
             preload();
@@ -584,20 +604,12 @@
             // Zygote.
             Trace.setTracingEnabled(false);
 
-            // If requested, start system server directly from Zygote
-            if (argv.length != 2) {
-                throw new RuntimeException(argv[0] + USAGE_STRING);
-            }
-
-            if (argv[1].equals("start-system-server")) {
+            if (startSystemServer) {
                 startSystemServer();
-            } else if (!argv[1].equals("")) {
-                throw new RuntimeException(argv[0] + USAGE_STRING);
             }
 
             Log.i(TAG, "Accepting command socket connections");
-
-            runSelectLoop();
+            runSelectLoop(abiList);
 
             closeServerSocket();
         } catch (MethodAndArgsCaller caller) {
@@ -617,7 +629,7 @@
      * @throws MethodAndArgsCaller in a child process when a main() should
      * be executed.
      */
-    private static void runSelectLoop() throws MethodAndArgsCaller {
+    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
         ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
         ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
         FileDescriptor[] fdArray = new FileDescriptor[4];
@@ -656,7 +668,7 @@
             if (index < 0) {
                 throw new RuntimeException("Error in select()");
             } else if (index == 0) {
-                ZygoteConnection newPeer = acceptCommandPeer();
+                ZygoteConnection newPeer = acceptCommandPeer(abiList);
                 peers.add(newPeer);
                 fds.add(newPeer.getFileDesciptor());
             } else {
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index cce4e0d..362a54e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -270,13 +270,13 @@
     strlcpy(mArgBlockStart, argv0, mArgBlockLength);
 }
 
-status_t AndroidRuntime::callMain(const char* className,
-    jclass clazz, int argc, const char* const argv[])
+status_t AndroidRuntime::callMain(const String8& className, jclass clazz,
+    const Vector<String8>& args)
 {
     JNIEnv* env;
     jmethodID methodId;
 
-    ALOGD("Calling main entry %s", className);
+    ALOGD("Calling main entry %s", className.string());
 
     env = getJNIEnv();
     if (clazz == NULL || env == NULL) {
@@ -285,7 +285,7 @@
 
     methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V");
     if (methodId == NULL) {
-        ALOGE("ERROR: could not find method %s.main(String[])\n", className);
+        ALOGE("ERROR: could not find method %s.main(String[])\n", className.string());
         return UNKNOWN_ERROR;
     }
 
@@ -296,11 +296,12 @@
     jclass stringClass;
     jobjectArray strArray;
 
+    const size_t numArgs = args.size();
     stringClass = env->FindClass("java/lang/String");
-    strArray = env->NewObjectArray(argc, stringClass, NULL);
+    strArray = env->NewObjectArray(numArgs, stringClass, NULL);
 
-    for (int i = 0; i < argc; i++) {
-        jstring argStr = env->NewStringUTF(argv[i]);
+    for (size_t i = 0; i < numArgs; i++) {
+        jstring argStr = env->NewStringUTF(args[i].string());
         env->SetObjectArrayElement(strArray, i, argStr);
     }
 
@@ -872,20 +873,23 @@
  * Passes the main function two arguments, the class name and the specified
  * options string.
  */
-void AndroidRuntime::start(const char* className, const char* options)
+void AndroidRuntime::start(const char* className, const Vector<String8>& options)
 {
     ALOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
             className != NULL ? className : "(unknown)");
 
+    static const String8 startSystemServer("start-system-server");
+
     /*
      * 'startSystemServer == true' means runtime is obsolete and not run from
      * init.rc anymore, so we print out the boot start event here.
      */
-    if (strcmp(options, "start-system-server") == 0) {
-        /* track our progress through the boot sequence */
-        const int LOG_BOOT_PROGRESS_START = 3000;
-        LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
-                       ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
+    for (size_t i = 0; i < options.size(); ++i) {
+        if (options[i] == startSystemServer) {
+           /* track our progress through the boot sequence */
+           const int LOG_BOOT_PROGRESS_START = 3000;
+           LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
+        }
     }
 
     const char* rootDir = getenv("ANDROID_ROOT");
@@ -926,17 +930,20 @@
     jclass stringClass;
     jobjectArray strArray;
     jstring classNameStr;
-    jstring optionsStr;
 
     stringClass = env->FindClass("java/lang/String");
     assert(stringClass != NULL);
-    strArray = env->NewObjectArray(2, stringClass, NULL);
+    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
     assert(strArray != NULL);
     classNameStr = env->NewStringUTF(className);
     assert(classNameStr != NULL);
     env->SetObjectArrayElement(strArray, 0, classNameStr);
-    optionsStr = env->NewStringUTF(options);
-    env->SetObjectArrayElement(strArray, 1, optionsStr);
+
+    for (size_t i = 0; i < options.size(); ++i) {
+        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
+        assert(optionsStr != NULL);
+        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
+    }
 
     /*
      * Start VM.  This thread becomes the main thread of the VM, and will
diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h
index 6f2af90..3dfdb46 100644
--- a/include/android_runtime/AndroidRuntime.h
+++ b/include/android_runtime/AndroidRuntime.h
@@ -55,8 +55,7 @@
     /**
      * Call a class's static main method with the given arguments,
      */
-    status_t callMain(const char* className, jclass clazz, int argc,
-        const char* const argv[]);
+    status_t callMain(const String8& className, jclass clazz, const Vector<String8>& args);
 
     /**
      * Find a class, with the input either of the form
@@ -66,7 +65,7 @@
 
     int addVmArguments(int argc, const char* const argv[]);
 
-    void start(const char *classname, const char* options);
+    void start(const char *classname, const Vector<String8>& options);
 
     void exit(int code);