OSDN Git Service

66e701342c14cf2db96ba973052a85263d4b67dc
[android-x86/frameworks-base.git] / core / java / com / android / internal / os / ZygoteInit.java
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.internal.os;
18
19 import static android.system.OsConstants.POLLIN;
20 import static android.system.OsConstants.S_IRWXG;
21 import static android.system.OsConstants.S_IRWXO;
22
23 import android.content.res.Resources;
24 import android.content.res.TypedArray;
25 import android.net.LocalServerSocket;
26 import android.opengl.EGL14;
27 import android.os.Process;
28 import android.os.SystemClock;
29 import android.os.SystemProperties;
30 import android.os.Trace;
31 import android.system.ErrnoException;
32 import android.system.Os;
33 import android.system.OsConstants;
34 import android.system.StructPollfd;
35 import android.text.Hyphenator;
36 import android.util.EventLog;
37 import android.util.Log;
38 import android.webkit.WebViewFactory;
39
40 import dalvik.system.DexFile;
41 import dalvik.system.PathClassLoader;
42 import dalvik.system.VMRuntime;
43
44 import libcore.io.IoUtils;
45
46 import java.io.BufferedReader;
47 import java.io.FileDescriptor;
48 import java.io.FileInputStream;
49 import java.io.FileNotFoundException;
50 import java.io.IOException;
51 import java.io.InputStream;
52 import java.io.InputStreamReader;
53 import java.lang.reflect.InvocationTargetException;
54 import java.lang.reflect.Method;
55 import java.util.ArrayList;
56
57 /**
58  * Startup class for the zygote process.
59  *
60  * Pre-initializes some classes, and then waits for commands on a UNIX domain
61  * socket. Based on these commands, forks off child processes that inherit
62  * the initial state of the VM.
63  *
64  * Please see {@link ZygoteConnection.Arguments} for documentation on the
65  * client protocol.
66  *
67  * @hide
68  */
69 public class ZygoteInit {
70     private static final String TAG = "Zygote";
71
72     private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
73
74     private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
75
76     private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
77     private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
78
79     /** when preloading, GC after allocating this many bytes */
80     private static final int PRELOAD_GC_THRESHOLD = 50000;
81
82     private static final String ABI_LIST_ARG = "--abi-list=";
83
84     private static final String SOCKET_NAME_ARG = "--socket-name=";
85
86     private static LocalServerSocket sServerSocket;
87
88     /**
89      * Used to pre-load resources.  We hold a global reference on it so it
90      * never gets destroyed.
91      */
92     private static Resources mResources;
93
94     /**
95      * The path of a file that contains classes to preload.
96      */
97     private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
98
99     /** Controls whether we should preload resources during zygote init. */
100     private static final boolean PRELOAD_RESOURCES = true;
101
102     /**
103      * Registers a server socket for zygote command connections
104      *
105      * @throws RuntimeException when open fails
106      */
107     private static void registerZygoteSocket(String socketName) {
108         if (sServerSocket == null) {
109             int fileDesc;
110             final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
111             try {
112                 String env = System.getenv(fullSocketName);
113                 fileDesc = Integer.parseInt(env);
114             } catch (RuntimeException ex) {
115                 throw new RuntimeException(fullSocketName + " unset or invalid", ex);
116             }
117
118             try {
119                 FileDescriptor fd = new FileDescriptor();
120                 fd.setInt$(fileDesc);
121                 sServerSocket = new LocalServerSocket(fd);
122             } catch (IOException ex) {
123                 throw new RuntimeException(
124                         "Error binding to local socket '" + fileDesc + "'", ex);
125             }
126         }
127     }
128
129     /**
130      * Waits for and accepts a single command connection. Throws
131      * RuntimeException on failure.
132      */
133     private static ZygoteConnection acceptCommandPeer(String abiList) {
134         try {
135             return new ZygoteConnection(sServerSocket.accept(), abiList);
136         } catch (IOException ex) {
137             throw new RuntimeException(
138                     "IOException during accept()", ex);
139         }
140     }
141
142     /**
143      * Close and clean up zygote sockets. Called on shutdown and on the
144      * child's exit path.
145      */
146     static void closeServerSocket() {
147         try {
148             if (sServerSocket != null) {
149                 FileDescriptor fd = sServerSocket.getFileDescriptor();
150                 sServerSocket.close();
151                 if (fd != null) {
152                     Os.close(fd);
153                 }
154             }
155         } catch (IOException ex) {
156             Log.e(TAG, "Zygote:  error closing sockets", ex);
157         } catch (ErrnoException ex) {
158             Log.e(TAG, "Zygote:  error closing descriptor", ex);
159         }
160
161         sServerSocket = null;
162     }
163
164     /**
165      * Return the server socket's underlying file descriptor, so that
166      * ZygoteConnection can pass it to the native code for proper
167      * closure after a child process is forked off.
168      */
169
170     static FileDescriptor getServerSocketFileDescriptor() {
171         return sServerSocket.getFileDescriptor();
172     }
173
174     private static final int UNPRIVILEGED_UID = 9999;
175     private static final int UNPRIVILEGED_GID = 9999;
176
177     private static final int ROOT_UID = 0;
178     private static final int ROOT_GID = 0;
179
180     static void preload() {
181         Log.d(TAG, "begin preload");
182         Thread classPreload = null;
183         if (true) {
184             classPreload = new Thread() {
185                 public void run() {
186                     preloadClasses();
187                 }
188             };
189             classPreload.start();
190         } else {
191             preloadClasses();
192         }
193         preloadResources();
194         preloadOpenGL();
195         preloadSharedLibraries();
196         preloadTextResources();
197         // Ask the WebViewFactory to do any initialization that must run in the zygote process,
198         // for memory sharing purposes.
199         WebViewFactory.prepareWebViewInZygote();
200         try {
201             if (classPreload != null)
202                 classPreload.join();
203         } catch(InterruptedException ex) {
204         }
205         Log.d(TAG, "end preload");
206     }
207
208     private static void preloadSharedLibraries() {
209         Log.i(TAG, "Preloading shared libraries...");
210         System.loadLibrary("android");
211         System.loadLibrary("compiler_rt");
212         System.loadLibrary("jnigraphics");
213     }
214
215     private static void preloadOpenGL() {
216         if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
217             EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
218         }
219     }
220
221     private static void preloadTextResources() {
222         Hyphenator.init();
223     }
224
225     /**
226      * Performs Zygote process initialization. Loads and initializes
227      * commonly used classes.
228      *
229      * Most classes only cause a few hundred bytes to be allocated, but
230      * a few will allocate a dozen Kbytes (in one case, 500+K).
231      */
232     private static void preloadClasses() {
233         final VMRuntime runtime = VMRuntime.getRuntime();
234
235         InputStream is;
236         try {
237             is = new FileInputStream(PRELOADED_CLASSES);
238         } catch (FileNotFoundException e) {
239             Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
240             return;
241         }
242
243         Log.i(TAG, "Preloading classes...");
244         long startTime = SystemClock.uptimeMillis();
245
246         // Drop root perms while running static initializers.
247         final int reuid = Os.getuid();
248         final int regid = Os.getgid();
249
250         // We need to drop root perms only if we're already root. In the case of "wrapped"
251         // processes (see WrapperInit), this function is called from an unprivileged uid
252         // and gid.
253         boolean droppedPriviliges = false;
254         if (reuid == ROOT_UID && regid == ROOT_GID) {
255             try {
256                 Os.setregid(ROOT_GID, UNPRIVILEGED_GID);
257                 Os.setreuid(ROOT_UID, UNPRIVILEGED_UID);
258             } catch (ErrnoException ex) {
259                 throw new RuntimeException("Failed to drop root", ex);
260             }
261
262             droppedPriviliges = true;
263         }
264
265         // Alter the target heap utilization.  With explicit GCs this
266         // is not likely to have any effect.
267         float defaultUtilization = runtime.getTargetHeapUtilization();
268         runtime.setTargetHeapUtilization(0.8f);
269
270         try {
271             BufferedReader br
272                 = new BufferedReader(new InputStreamReader(is), 256);
273
274             int count = 0;
275             String line;
276             while ((line = br.readLine()) != null) {
277                 // Skip comments and blank lines.
278                 line = line.trim();
279                 if (line.startsWith("#") || line.equals("")) {
280                     continue;
281                 }
282
283                 try {
284                     if (false) {
285                         Log.v(TAG, "Preloading " + line + "...");
286                     }
287                     // Load and explicitly initialize the given class. Use
288                     // Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups
289                     // (to derive the caller's class-loader). Use true to force initialization, and
290                     // null for the boot classpath class-loader (could as well cache the
291                     // class-loader of this class in a variable).
292                     Class.forName(line, true, null);
293                     count++;
294                 } catch (ClassNotFoundException e) {
295                     Log.w(TAG, "Class not found for preloading: " + line);
296                 } catch (UnsatisfiedLinkError e) {
297                     Log.w(TAG, "Problem preloading " + line + ": " + e);
298                 } catch (Throwable t) {
299                     Log.e(TAG, "Error preloading " + line + ".", t);
300                     if (t instanceof Error) {
301                         throw (Error) t;
302                     }
303                     if (t instanceof RuntimeException) {
304                         throw (RuntimeException) t;
305                     }
306                     throw new RuntimeException(t);
307                 }
308             }
309
310             Log.i(TAG, "...preloaded " + count + " classes in "
311                     + (SystemClock.uptimeMillis()-startTime) + "ms.");
312         } catch (IOException e) {
313             Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
314         } finally {
315             IoUtils.closeQuietly(is);
316             // Restore default.
317             runtime.setTargetHeapUtilization(defaultUtilization);
318
319             // Fill in dex caches with classes, fields, and methods brought in by preloading.
320             runtime.preloadDexCaches();
321
322             // Bring back root. We'll need it later if we're in the zygote.
323             if (droppedPriviliges) {
324                 try {
325                     Os.setreuid(ROOT_UID, ROOT_UID);
326                     Os.setregid(ROOT_GID, ROOT_GID);
327                 } catch (ErrnoException ex) {
328                     throw new RuntimeException("Failed to restore root", ex);
329                 }
330             }
331         }
332     }
333
334     /**
335      * Load in commonly used resources, so they can be shared across
336      * processes.
337      *
338      * These tend to be a few Kbytes, but are frequently in the 20-40K
339      * range, and occasionally even larger.
340      */
341     private static void preloadResources() {
342         final VMRuntime runtime = VMRuntime.getRuntime();
343
344         try {
345             mResources = Resources.getSystem();
346             mResources.startPreloading();
347             if (PRELOAD_RESOURCES) {
348                 Log.i(TAG, "Preloading resources...");
349
350                 long startTime = SystemClock.uptimeMillis();
351                 TypedArray ar = mResources.obtainTypedArray(
352                         com.android.internal.R.array.preloaded_drawables);
353                 int N = preloadDrawables(runtime, ar);
354                 ar.recycle();
355                 Log.i(TAG, "...preloaded " + N + " resources in "
356                         + (SystemClock.uptimeMillis()-startTime) + "ms.");
357
358                 startTime = SystemClock.uptimeMillis();
359                 ar = mResources.obtainTypedArray(
360                         com.android.internal.R.array.preloaded_color_state_lists);
361                 N = preloadColorStateLists(runtime, ar);
362                 ar.recycle();
363                 Log.i(TAG, "...preloaded " + N + " resources in "
364                         + (SystemClock.uptimeMillis()-startTime) + "ms.");
365             }
366             mResources.finishPreloading();
367         } catch (RuntimeException e) {
368             Log.w(TAG, "Failure preloading resources", e);
369         }
370     }
371
372     private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
373         int N = ar.length();
374         for (int i=0; i<N; i++) {
375             int id = ar.getResourceId(i, 0);
376             if (false) {
377                 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
378             }
379             if (id != 0) {
380                 if (mResources.getColorStateList(id, null) == null) {
381                     throw new IllegalArgumentException(
382                             "Unable to find preloaded color resource #0x"
383                             + Integer.toHexString(id)
384                             + " (" + ar.getString(i) + ")");
385                 }
386             }
387         }
388         return N;
389     }
390
391
392     private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
393         int N = ar.length();
394         for (int i=0; i<N; i++) {
395             int id = ar.getResourceId(i, 0);
396             if (false) {
397                 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
398             }
399             if (id != 0) {
400                 if (mResources.getDrawable(id, null) == null) {
401                     throw new IllegalArgumentException(
402                             "Unable to find preloaded drawable resource #0x"
403                             + Integer.toHexString(id)
404                             + " (" + ar.getString(i) + ")");
405                 }
406             }
407         }
408         return N;
409     }
410
411     /**
412      * Runs several special GCs to try to clean up a few generations of
413      * softly- and final-reachable objects, along with any other garbage.
414      * This is only useful just before a fork().
415      */
416     /*package*/ static void gcAndFinalize() {
417         final VMRuntime runtime = VMRuntime.getRuntime();
418
419         /* runFinalizationSync() lets finalizers be called in Zygote,
420          * which doesn't have a HeapWorker thread.
421          */
422         System.gc();
423         runtime.runFinalizationSync();
424         System.gc();
425     }
426
427     /**
428      * Finish remaining work for the newly forked system server process.
429      */
430     private static void handleSystemServerProcess(
431             ZygoteConnection.Arguments parsedArgs)
432             throws ZygoteInit.MethodAndArgsCaller {
433
434         closeServerSocket();
435
436         // set umask to 0077 so new files and directories will default to owner-only permissions.
437         Os.umask(S_IRWXG | S_IRWXO);
438
439         if (parsedArgs.niceName != null) {
440             Process.setArgV0(parsedArgs.niceName);
441         }
442
443         final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
444         if (systemServerClasspath != null) {
445             performSystemServerDexOpt(systemServerClasspath);
446         }
447
448         if (parsedArgs.invokeWith != null) {
449             String[] args = parsedArgs.remainingArgs;
450             // If we have a non-null system server class path, we'll have to duplicate the
451             // existing arguments and append the classpath to it. ART will handle the classpath
452             // correctly when we exec a new process.
453             if (systemServerClasspath != null) {
454                 String[] amendedArgs = new String[args.length + 2];
455                 amendedArgs[0] = "-cp";
456                 amendedArgs[1] = systemServerClasspath;
457                 System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
458             }
459
460             WrapperInit.execApplication(parsedArgs.invokeWith,
461                     parsedArgs.niceName, parsedArgs.targetSdkVersion,
462                     VMRuntime.getCurrentInstructionSet(), null, args);
463         } else {
464             ClassLoader cl = null;
465             if (systemServerClasspath != null) {
466                 cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
467                 Thread.currentThread().setContextClassLoader(cl);
468             }
469
470             /*
471              * Pass the remaining arguments to SystemServer.
472              */
473             RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
474         }
475
476         /* should never reach here */
477     }
478
479     /**
480      * Performs dex-opt on the elements of {@code classPath}, if needed. We
481      * choose the instruction set of the current runtime.
482      */
483     private static void performSystemServerDexOpt(String classPath) {
484         final String[] classPathElements = classPath.split(":");
485         final InstallerConnection installer = new InstallerConnection();
486         installer.waitForConnection();
487         final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
488
489         try {
490             for (String classPathElement : classPathElements) {
491                 final int dexoptNeeded = DexFile.getDexOptNeeded(
492                         classPathElement, "*", instructionSet, false /* defer */);
493                 if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
494                     installer.dexopt(classPathElement, Process.SYSTEM_UID, false,
495                             instructionSet, dexoptNeeded, false /* boot complete */);
496                 }
497             }
498         } catch (IOException ioe) {
499             throw new RuntimeException("Error starting system_server", ioe);
500         } finally {
501             installer.disconnect();
502         }
503     }
504
505     /**
506      * Prepare the arguments and fork for the system server process.
507      */
508     private static boolean startSystemServer(String abiList, String socketName)
509             throws MethodAndArgsCaller, RuntimeException {
510         long capabilities = posixCapabilitiesAsBits(
511             OsConstants.CAP_BLOCK_SUSPEND,
512             OsConstants.CAP_KILL,
513             OsConstants.CAP_NET_ADMIN,
514             OsConstants.CAP_NET_BIND_SERVICE,
515             OsConstants.CAP_NET_BROADCAST,
516             OsConstants.CAP_NET_RAW,
517             OsConstants.CAP_SYS_MODULE,
518             OsConstants.CAP_SYS_NICE,
519             OsConstants.CAP_SYS_RESOURCE,
520             OsConstants.CAP_SYS_TIME,
521             OsConstants.CAP_SYS_TTY_CONFIG
522         );
523         /* Hardcoded command line to start the system server */
524         String args[] = {
525             "--setuid=1000",
526             "--setgid=1000",
527             "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007",
528             "--capabilities=" + capabilities + "," + capabilities,
529             "--nice-name=system_server",
530             "--runtime-args",
531             "com.android.server.SystemServer",
532         };
533         ZygoteConnection.Arguments parsedArgs = null;
534
535         int pid;
536
537         try {
538             parsedArgs = new ZygoteConnection.Arguments(args);
539             ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
540             ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
541
542             /* Request to fork the system server process */
543             pid = Zygote.forkSystemServer(
544                     parsedArgs.uid, parsedArgs.gid,
545                     parsedArgs.gids,
546                     parsedArgs.debugFlags,
547                     null,
548                     parsedArgs.permittedCapabilities,
549                     parsedArgs.effectiveCapabilities);
550         } catch (IllegalArgumentException ex) {
551             throw new RuntimeException(ex);
552         }
553
554         /* For child process */
555         if (pid == 0) {
556             if (hasSecondZygote(abiList)) {
557                 waitForSecondaryZygote(socketName);
558             }
559
560             handleSystemServerProcess(parsedArgs);
561         }
562
563         return true;
564     }
565
566     /**
567      * Gets the bit array representation of the provided list of POSIX capabilities.
568      */
569     private static long posixCapabilitiesAsBits(int... capabilities) {
570         long result = 0;
571         for (int capability : capabilities) {
572             if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
573                 throw new IllegalArgumentException(String.valueOf(capability));
574             }
575             result |= (1L << capability);
576         }
577         return result;
578     }
579
580     public static void main(String argv[]) {
581         try {
582             RuntimeInit.enableDdms();
583             // Start profiling the zygote initialization.
584             SamplingProfilerIntegration.start();
585
586             boolean startSystemServer = false;
587             String socketName = "zygote";
588             String abiList = null;
589             for (int i = 1; i < argv.length; i++) {
590                 if ("start-system-server".equals(argv[i])) {
591                     startSystemServer = true;
592                 } else if (argv[i].startsWith(ABI_LIST_ARG)) {
593                     abiList = argv[i].substring(ABI_LIST_ARG.length());
594                 } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
595                     socketName = argv[i].substring(SOCKET_NAME_ARG.length());
596                 } else {
597                     throw new RuntimeException("Unknown command line argument: " + argv[i]);
598                 }
599             }
600
601             if (abiList == null) {
602                 throw new RuntimeException("No ABI list supplied.");
603             }
604
605             registerZygoteSocket(socketName);
606             EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
607                 SystemClock.uptimeMillis());
608             preload();
609             EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
610                 SystemClock.uptimeMillis());
611
612             // Finish profiling the zygote initialization.
613             SamplingProfilerIntegration.writeZygoteSnapshot();
614
615             // Do an initial gc to clean up after startup
616             gcAndFinalize();
617
618             // Disable tracing so that forked processes do not inherit stale tracing tags from
619             // Zygote.
620             Trace.setTracingEnabled(false);
621
622             if (startSystemServer) {
623                 startSystemServer(abiList, socketName);
624             }
625
626             Log.i(TAG, "Accepting command socket connections");
627             runSelectLoop(abiList);
628
629             closeServerSocket();
630         } catch (MethodAndArgsCaller caller) {
631             caller.run();
632         } catch (RuntimeException ex) {
633             Log.e(TAG, "Zygote died with exception", ex);
634             closeServerSocket();
635             throw ex;
636         }
637     }
638
639     /**
640      * Return {@code true} if this device configuration has another zygote.
641      *
642      * We determine this by comparing the device ABI list with this zygotes
643      * list. If this zygote supports all ABIs this device supports, there won't
644      * be another zygote.
645      */
646     private static boolean hasSecondZygote(String abiList) {
647         return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList);
648     }
649
650     private static void waitForSecondaryZygote(String socketName) {
651         String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ?
652                 Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
653         while (true) {
654             try {
655                 final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName);
656                 zs.close();
657                 break;
658             } catch (IOException ioe) {
659                 Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
660             }
661
662             try {
663                 Thread.sleep(1000);
664             } catch (InterruptedException ie) {
665             }
666         }
667     }
668
669     /**
670      * Runs the zygote process's select loop. Accepts new connections as
671      * they happen, and reads commands from connections one spawn-request's
672      * worth at a time.
673      *
674      * @throws MethodAndArgsCaller in a child process when a main() should
675      * be executed.
676      */
677     private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
678         ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
679         ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
680
681         fds.add(sServerSocket.getFileDescriptor());
682         peers.add(null);
683
684         while (true) {
685             StructPollfd[] pollFds = new StructPollfd[fds.size()];
686             for (int i = 0; i < pollFds.length; ++i) {
687                 pollFds[i] = new StructPollfd();
688                 pollFds[i].fd = fds.get(i);
689                 pollFds[i].events = (short) POLLIN;
690             }
691             try {
692                 Os.poll(pollFds, -1);
693             } catch (ErrnoException ex) {
694                 throw new RuntimeException("poll failed", ex);
695             }
696             for (int i = pollFds.length - 1; i >= 0; --i) {
697                 if ((pollFds[i].revents & POLLIN) == 0) {
698                     continue;
699                 }
700                 if (i == 0) {
701                     ZygoteConnection newPeer = acceptCommandPeer(abiList);
702                     peers.add(newPeer);
703                     fds.add(newPeer.getFileDesciptor());
704                 } else {
705                     boolean done = peers.get(i).runOnce();
706                     if (done) {
707                         peers.remove(i);
708                         fds.remove(i);
709                     }
710                 }
711             }
712         }
713     }
714
715     /**
716      * Class not instantiable.
717      */
718     private ZygoteInit() {
719     }
720
721     /**
722      * Helper exception class which holds a method and arguments and
723      * can call them. This is used as part of a trampoline to get rid of
724      * the initial process setup stack frames.
725      */
726     public static class MethodAndArgsCaller extends Exception
727             implements Runnable {
728         /** method to call */
729         private final Method mMethod;
730
731         /** argument array */
732         private final String[] mArgs;
733
734         public MethodAndArgsCaller(Method method, String[] args) {
735             mMethod = method;
736             mArgs = args;
737         }
738
739         public void run() {
740             try {
741                 mMethod.invoke(null, new Object[] { mArgs });
742             } catch (IllegalAccessException ex) {
743                 throw new RuntimeException(ex);
744             } catch (InvocationTargetException ex) {
745                 Throwable cause = ex.getCause();
746                 if (cause instanceof RuntimeException) {
747                     throw (RuntimeException) cause;
748                 } else if (cause instanceof Error) {
749                     throw (Error) cause;
750                 }
751                 throw new RuntimeException(ex);
752             }
753         }
754     }
755 }