diff options
| author | Tobias Sargeant <tobiasjs@google.com> | 2016-01-19 16:34:54 +0000 |
|---|---|---|
| committer | Robert Sesek <rsesek@google.com> | 2016-09-09 12:42:43 -0400 |
| commit | b9679dc1fad508a001e30a941148e1bdc3fe953f (patch) | |
| tree | 843c4ff3654fe9805634d6cec71c45ce2c1004f8 /core/java | |
| parent | 63a07e80d6c60c86bbcd81660f9693236de5ed80 (diff) | |
Refactor ZygoteInit to support a WebView-specific zygote.
This is a non-functional change that separates out functionality
that should be shared between the system zygote and the WebView
zygote from that which is system zygote specific.
* Move MethodAndArgsCaller to Zygote.
* Split out server socket functions into ZygoteServer.
* Add a new (stub, for now) WebViewZygoteInit class.
Bug: 22084679
Bug: 21643067
(cherry picked from commit ba816e0c9efd8cd2aeef618a819a2ad46b742f87)
Merged-In: I4c508a42af7ab7b53d10570ad53b846df7782cc4
Change-Id: I54f04c03443d10dabe6426697d1ff8a0cc66b985
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/com/android/internal/os/RuntimeInit.java | 10 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/WebViewZygoteInit.java | 32 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/WrapperInit.java | 4 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/Zygote.java | 38 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/ZygoteConnection.java | 17 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/ZygoteInit.java | 193 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/ZygoteServer.java | 167 |
7 files changed, 266 insertions, 195 deletions
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index de671b1ab98d..c851e4ec959a 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -230,7 +230,7 @@ public class RuntimeInit { * @param classLoader the classLoader to load {@className} with */ private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader) - throws ZygoteInit.MethodAndArgsCaller { + throws Zygote.MethodAndArgsCaller { Class<?> cl; try { @@ -264,7 +264,7 @@ public class RuntimeInit { * clears up all the stack frames that were required in setting * up the process. */ - throw new ZygoteInit.MethodAndArgsCaller(m, argv); + throw new Zygote.MethodAndArgsCaller(m, argv); } public static final void main(String[] argv) { @@ -301,7 +301,7 @@ public class RuntimeInit { * @param argv arg strings */ public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) - throws ZygoteInit.MethodAndArgsCaller { + throws Zygote.MethodAndArgsCaller { if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote"); Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit"); @@ -324,14 +324,14 @@ public class RuntimeInit { * @param argv arg strings */ public static void wrapperInit(int targetSdkVersion, String[] argv) - throws ZygoteInit.MethodAndArgsCaller { + throws Zygote.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 ZygoteInit.MethodAndArgsCaller { + throws Zygote.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 new file mode 100644 index 000000000000..2ed7aa214ccd --- /dev/null +++ b/core/java/com/android/internal/os/WebViewZygoteInit.java @@ -0,0 +1,32 @@ +/* + * 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 c558cf8d1ee7..594b6ab72ef3 100644 --- a/core/java/com/android/internal/os/WrapperInit.java +++ b/core/java/com/android/internal/os/WrapperInit.java @@ -74,14 +74,14 @@ public class WrapperInit { } } - // Mimic Zygote preloading. + // Mimic system 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 (ZygoteInit.MethodAndArgsCaller caller) { + } catch (Zygote.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 66cc97598ae8..fc0ccb75de95 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -22,6 +22,9 @@ import dalvik.system.ZygoteHooks; import android.system.ErrnoException; import android.system.Os; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + /** @hide */ public final class Zygote { /* @@ -191,4 +194,39 @@ public final class Zygote { 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 85d84bb3f986..132b02236ab9 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -117,7 +117,7 @@ class ZygoteConnection { /** * Reads one start command from the command socket. If successful, - * a child is forked and a {@link ZygoteInit.MethodAndArgsCaller} + * a child is forked and a {@link Zygote.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 @@ class ZygoteConnection { * * @return false if command socket should continue to be read from, or * true if an end-of-file has been encountered. - * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main() + * @throws Zygote.MethodAndArgsCaller trampoline to invoke main() * method in child process */ - boolean runOnce() throws ZygoteInit.MethodAndArgsCaller { + boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller { String args[]; Arguments parsedArgs = null; @@ -214,7 +214,7 @@ class ZygoteConnection { fdsToClose[0] = fd.getInt$(); } - fd = ZygoteInit.getServerSocketFileDescriptor(); + fd = zygoteServer.getServerSocketFileDescriptor(); if (fd != null) { fdsToClose[1] = fd.getInt$(); @@ -238,12 +238,13 @@ class ZygoteConnection { 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 ZygoteInit.MethodAndArgsCaller or exec(). + // throw Zygote.MethodAndArgsCaller or exec(). return true; } else { // in parent...pid of < 0 means failure @@ -712,12 +713,12 @@ class ZygoteConnection { * @param newStderr null-ok; stream to use for stderr until stdio * is reopened. * - * @throws ZygoteInit.MethodAndArgsCaller on success to + * @throws Zygote.MethodAndArgsCaller on success to * trampoline to code that invokes static main. */ private void handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr) - throws ZygoteInit.MethodAndArgsCaller { + throws Zygote.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 @@ -725,8 +726,6 @@ class ZygoteConnection { */ 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 b57aea634bee..2e5b550b6f34 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -16,7 +16,6 @@ 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; @@ -35,7 +34,6 @@ 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; @@ -52,17 +50,13 @@ import dalvik.system.ZygoteHooks; 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. @@ -82,8 +76,6 @@ public class ZygoteInit { 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; @@ -94,11 +86,8 @@ public class ZygoteInit { private static final String SOCKET_NAME_ARG = "--socket-name="; - private static LocalServerSocket sServerSocket; - /** - * Used to pre-load resources. We hold a global reference on it so it - * never gets destroyed. + * Used to pre-load resources. */ private static Resources mResources; @@ -110,78 +99,6 @@ public class ZygoteInit { /** 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; @@ -504,9 +421,7 @@ public class ZygoteInit { */ private static void handleSystemServerProcess( ZygoteConnection.Arguments parsedArgs) - throws ZygoteInit.MethodAndArgsCaller { - - closeServerSocket(); + throws Zygote.MethodAndArgsCaller { // set umask to 0077 so new files and directories will default to owner-only permissions. Os.umask(S_IRWXG | S_IRWXO); @@ -609,8 +524,8 @@ public class ZygoteInit { /** * Prepare the arguments and fork for the system server process. */ - private static boolean startSystemServer(String abiList, String socketName) - throws MethodAndArgsCaller, RuntimeException { + private static boolean startSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) + throws Zygote.MethodAndArgsCaller, RuntimeException { long capabilities = posixCapabilitiesAsBits( OsConstants.CAP_IPC_LOCK, OsConstants.CAP_KILL, @@ -666,6 +581,7 @@ public class ZygoteInit { waitForSecondaryZygote(socketName); } + zygoteServer.closeServerSocket(); handleSystemServerProcess(parsedArgs); } @@ -687,6 +603,8 @@ public class ZygoteInit { } public static void main(String argv[]) { + ZygoteServer zygoteServer = new ZygoteServer(); + // Mark zygote start. This ensures that thread creation will throw // an error. ZygoteHooks.startZygoteNoThreadCreation(); @@ -716,7 +634,7 @@ public class ZygoteInit { throw new RuntimeException("No ABI list supplied."); } - registerZygoteSocket(socketName); + zygoteServer.registerServerSocket(socketName); Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygotePreload"); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); @@ -733,8 +651,6 @@ public class ZygoteInit { 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); @@ -745,18 +661,18 @@ public class ZygoteInit { ZygoteHooks.stopZygoteNoThreadCreation(); if (startSystemServer) { - startSystemServer(abiList, socketName); + startSystemServer(abiList, socketName, zygoteServer); } Log.i(TAG, "Accepting command socket connections"); - runSelectLoop(abiList); + zygoteServer.runSelectLoop(abiList); - closeServerSocket(); - } catch (MethodAndArgsCaller caller) { + zygoteServer.closeServerSocket(); + } catch (Zygote.MethodAndArgsCaller caller) { caller.run(); } catch (RuntimeException ex) { - Log.e(TAG, "Zygote died with exception", ex); - closeServerSocket(); + Log.e(TAG, "System zygote died with exception", ex); + zygoteServer.closeServerSocket(); throw ex; } } @@ -792,89 +708,8 @@ public class ZygoteInit { } /** - * 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 new file mode 100644 index 000000000000..ab87641006a5 --- /dev/null +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -0,0 +1,167 @@ +/* + * 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); + } + } + } + } + } +} |
