OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / java / com / android / internal / os / ZygoteConnection.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 android.net.Credentials;
20 import android.net.LocalSocket;
21 import android.os.Process;
22 import android.os.SystemProperties;
23 import android.util.Log;
24
25 import dalvik.system.PathClassLoader;
26 import dalvik.system.Zygote;
27
28 import java.io.BufferedReader;
29 import java.io.DataOutputStream;
30 import java.io.FileDescriptor;
31 import java.io.FileOutputStream;
32 import java.io.IOException;
33 import java.io.InputStreamReader;
34 import java.io.PrintStream;
35 import java.util.ArrayList;
36
37 /**
38  * A connection that can make spawn requests.
39  */
40 class ZygoteConnection {
41     private static final String TAG = "Zygote";
42
43     /** a prototype instance for a future List.toArray() */
44     private static final int[][] intArray2d = new int[0][0];
45
46     /**
47      * {@link android.net.LocalSocket#setSoTimeout} value for connections.
48      * Effectively, the amount of time a requestor has between the start of
49      * the request and the completed request. The select-loop mode Zygote
50      * doesn't have the logic to return to the select loop in the middle of
51      * a request, so we need to time out here to avoid being denial-of-serviced.
52      */
53     private static final int CONNECTION_TIMEOUT_MILLIS = 1000;
54
55     /** max number of arguments that a connection can specify */
56     private static final int MAX_ZYGOTE_ARGC=1024;
57
58     /**
59      * The command socket.
60      *
61      * mSocket is retained in the child process in "peer wait" mode, so
62      * that it closes when the child process terminates. In other cases,
63      * it is closed in the peer.
64      */
65     private final LocalSocket mSocket;
66     private final DataOutputStream mSocketOutStream;
67     private final BufferedReader mSocketReader;
68     private final Credentials peer;
69
70     /**
71      * A long-lived reference to the original command socket used to launch
72      * this peer. If "peer wait" mode is specified, the process that requested
73      * the new VM instance intends to track the lifetime of the spawned instance
74      * via the command socket. In this case, the command socket is closed
75      * in the Zygote and placed here in the spawned instance so that it will
76      * not be collected and finalized. This field remains null at all times
77      * in the original Zygote process, and in all spawned processes where
78      * "peer-wait" mode was not requested.
79      */
80     private static LocalSocket sPeerWaitSocket = null;
81
82     /**
83      * Constructs instance from connected socket.
84      *
85      * @param socket non-null; connected socket
86      * @throws IOException
87      */
88     ZygoteConnection(LocalSocket socket) throws IOException {
89         mSocket = socket;
90
91         mSocketOutStream
92                 = new DataOutputStream(socket.getOutputStream());
93
94         mSocketReader = new BufferedReader(
95                 new InputStreamReader(socket.getInputStream()), 256);
96
97         mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS);
98                 
99         try {
100             peer = mSocket.getPeerCredentials();
101         } catch (IOException ex) {
102             Log.e(TAG, "Cannot read peer credentials", ex);
103             throw ex;
104         }
105     }
106
107     /**
108      * Returns the file descriptor of the associated socket.
109      *
110      * @return null-ok; file descriptor
111      */
112     FileDescriptor getFileDesciptor() {
113         return mSocket.getFileDescriptor();
114     }
115
116     /**
117      * Reads start commands from an open command socket.
118      * Start commands are presently a pair of newline-delimited lines
119      * indicating a) class to invoke main() on b) nice name to set argv[0] to.
120      * Continues to read commands and forkAndSpecialize children until
121      * the socket is closed. This method is used in ZYGOTE_FORK_MODE
122      *
123      * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main()
124      * method in child process
125      */
126     void run() throws ZygoteInit.MethodAndArgsCaller {
127
128         int loopCount = ZygoteInit.GC_LOOP_COUNT;
129
130         while (true) {
131             /*
132              * Call gc() before we block in readArgumentList().
133              * It's work that has to be done anyway, and it's better
134              * to avoid making every child do it.  It will also
135              * madvise() any free memory as a side-effect.
136              *
137              * Don't call it every time, because walking the entire
138              * heap is a lot of overhead to free a few hundred bytes.
139              */
140             if (loopCount <= 0) {
141                 ZygoteInit.gc();
142                 loopCount = ZygoteInit.GC_LOOP_COUNT;
143             } else {
144                 loopCount--;
145             }
146
147             if (runOnce()) {
148                 break;
149             }
150         }
151     }
152
153     /**
154      * Reads one start command from the command socket. If successful,
155      * a child is forked and a {@link ZygoteInit.MethodAndArgsCaller}
156      * exception is thrown in that child while in the parent process,
157      * the method returns normally. On failure, the child is not
158      * spawned and messages are printed to the log and stderr. Returns
159      * a boolean status value indicating whether an end-of-file on the command
160      * socket has been encountered.
161      *
162      * @return false if command socket should continue to be read from, or
163      * true if an end-of-file has been encountered.
164      * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main()
165      * method in child process
166      */
167     boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
168
169         String args[];
170         Arguments parsedArgs = null;
171         FileDescriptor[] descriptors;
172
173         try {
174             args = readArgumentList();
175             descriptors = mSocket.getAncillaryFileDescriptors();
176         } catch (IOException ex) {
177             Log.w(TAG, "IOException on command socket " + ex.getMessage());
178             closeSocket();
179             return true;
180         }
181
182         if (args == null) {
183             // EOF reached.
184             closeSocket();
185             return true;
186         }
187
188         /** the stderr of the most recent request, if avail */
189         PrintStream newStderr = null;
190
191         if (descriptors != null && descriptors.length >= 3) {
192             newStderr = new PrintStream(
193                     new FileOutputStream(descriptors[2]));
194         }
195
196         int pid;
197
198         try {
199             parsedArgs = new Arguments(args);
200
201             applyUidSecurityPolicy(parsedArgs, peer);
202             applyDebuggerSecurityPolicy(parsedArgs);
203             applyRlimitSecurityPolicy(parsedArgs, peer);
204             applyCapabilitiesSecurityPolicy(parsedArgs, peer);
205
206             int[][] rlimits = null;
207
208             if (parsedArgs.rlimits != null) {
209                 rlimits = parsedArgs.rlimits.toArray(intArray2d);
210             }
211
212             pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
213                     parsedArgs.gids, parsedArgs.debugFlags, rlimits);
214         } catch (IllegalArgumentException ex) {
215             logAndPrintError (newStderr, "Invalid zygote arguments", ex);
216             pid = -1;
217         } catch (ZygoteSecurityException ex) {
218             logAndPrintError(newStderr,
219                     "Zygote security policy prevents request: ", ex);
220             pid = -1;
221         }
222
223         if (pid == 0) {
224             // in child
225             handleChildProc(parsedArgs, descriptors, newStderr);
226             // should never happen
227             return true;
228         } else { /* pid != 0 */
229             // in parent...pid of < 0 means failure
230             return handleParentProc(pid, descriptors, parsedArgs);
231         }
232     }
233
234     /**
235      * Closes socket associated with this connection.
236      */
237     void closeSocket() {
238         try {
239             mSocket.close();
240         } catch (IOException ex) {
241             Log.e(TAG, "Exception while closing command "
242                     + "socket in parent", ex);
243         }
244     }
245
246     /**
247      * Handles argument parsing for args related to the zygote spawner.<p>
248
249      * Current recognized args:
250      * <ul>
251      *   <li> --setuid=<i>uid of child process, defaults to 0</i>
252      *   <li> --setgid=<i>gid of child process, defaults to 0</i>
253      *   <li> --setgroups=<i>comma-separated list of supplimentary gid's</i>
254      *   <li> --capabilities=<i>a pair of comma-separated integer strings
255      * indicating Linux capabilities(2) set for child. The first string
256      * represents the <code>permitted</code> set, and the second the
257      * <code>effective</code> set. Precede each with 0 or
258      * 0x for octal or hexidecimal value. If unspecified, both default to 0.
259      * This parameter is only applied if the uid of the new process will
260      * be non-0. </i>
261      *   <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call.
262      *    <code>r</code> is the resource, <code>c</code> and <code>m</code>
263      *    are the settings for current and max value.</i>
264      *   <li> --peer-wait indicates that the command socket should
265      * be inherited by (and set to close-on-exec in) the spawned process
266      * and used to track the lifetime of that process. The spawning process
267      * then exits. Without this flag, it is retained by the spawning process
268      * (and closed in the child) in expectation of a new spawn request.
269      *   <li> --classpath=<i>colon-separated classpath</i> indicates
270      * that the specified class (which must b first non-flag argument) should
271      * be loaded from jar files in the specified classpath. Incompatible with
272      * --runtime-init
273      *   <li> --runtime-init indicates that the remaining arg list should
274      * be handed off to com.android.internal.os.RuntimeInit, rather than
275      * processed directly
276      * Android runtime startup (eg, Binder initialization) is also eschewed.
277      *   <li> If <code>--runtime-init</code> is present:
278      *      [--] &lt;args for RuntimeInit &gt;
279      *   <li> If <code>--runtime-init</code> is absent:
280      *      [--] &lt;classname&gt; [args...]
281      * </ul>
282      */
283     static class Arguments {
284         /** from --setuid */
285         int uid = 0;
286         boolean uidSpecified;
287
288         /** from --setgid */
289         int gid = 0;
290         boolean gidSpecified;
291
292         /** from --setgroups */
293         int[] gids;
294
295         /** from --peer-wait */
296         boolean peerWait;
297
298         /**
299          * From --enable-debugger, --enable-checkjni, --enable-assert, and
300          * --enable-safemode
301          */
302         int debugFlags;
303
304         /** from --classpath */
305         String classpath;
306
307         /** from --runtime-init */
308         boolean runtimeInit;
309
310         /** from --capabilities */
311         boolean capabilitiesSpecified;
312         long permittedCapabilities;
313         long effectiveCapabilities;
314
315         /** from all --rlimit=r,c,m */
316         ArrayList<int[]> rlimits;
317
318         /**
319          * Any args after and including the first non-option arg
320          * (or after a '--')
321          */
322         String remainingArgs[];
323
324         /**
325          * Constructs instance and parses args
326          * @param args zygote command-line args
327          * @throws IllegalArgumentException
328          */
329         Arguments(String args[]) throws IllegalArgumentException {
330             parseArgs(args);
331         }
332
333         /**
334          * Parses the commandline arguments intended for the Zygote spawner
335          * (such as "--setuid=" and "--setgid=") and creates an array
336          * containing the remaining args.
337          *
338          * Per security review bug #1112214, duplicate args are disallowed in
339          * critical cases to make injection harder.
340          */
341         private void parseArgs(String args[])
342                 throws IllegalArgumentException {
343             int curArg = 0;
344
345             for ( /* curArg */ ; curArg < args.length; curArg++) {
346                 String arg = args[curArg];
347
348                 if (arg.equals("--")) {
349                     curArg++;
350                     break;
351                 } else if (arg.startsWith("--setuid=")) {
352                     if (uidSpecified) {
353                         throw new IllegalArgumentException(
354                                 "Duplicate arg specified");
355                     }
356                     uidSpecified = true;
357                     uid = Integer.parseInt(
358                             arg.substring(arg.indexOf('=') + 1));
359                 } else if (arg.startsWith("--setgid=")) {
360                     if (gidSpecified) {
361                         throw new IllegalArgumentException(
362                                 "Duplicate arg specified");
363                     }
364                     gidSpecified = true;
365                     gid = Integer.parseInt(
366                             arg.substring(arg.indexOf('=') + 1));
367                 } else if (arg.equals("--enable-debugger")) {
368                     debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
369                 } else if (arg.equals("--enable-safemode")) {
370                     debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
371                 } else if (arg.equals("--enable-checkjni")) {
372                     debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
373                 } else if (arg.equals("--enable-assert")) {
374                     debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
375                 } else if (arg.equals("--peer-wait")) {
376                     peerWait = true;
377                 } else if (arg.equals("--runtime-init")) {
378                     runtimeInit = true;
379                 } else if (arg.startsWith("--capabilities=")) {
380                     if (capabilitiesSpecified) {
381                         throw new IllegalArgumentException(
382                                 "Duplicate arg specified");
383                     }
384                     capabilitiesSpecified = true;
385                     String capString = arg.substring(arg.indexOf('=')+1);
386
387                     String[] capStrings = capString.split(",", 2);
388
389                     if (capStrings.length == 1) {
390                         effectiveCapabilities = Long.decode(capStrings[0]);
391                         permittedCapabilities = effectiveCapabilities;
392                     } else {
393                         permittedCapabilities = Long.decode(capStrings[0]);
394                         effectiveCapabilities = Long.decode(capStrings[1]);
395                     }
396                 } else if (arg.startsWith("--rlimit=")) {
397                     // Duplicate --rlimit arguments are specifically allowed.
398                     String[] limitStrings
399                             = arg.substring(arg.indexOf('=')+1).split(",");
400
401                     if (limitStrings.length != 3) {
402                         throw new IllegalArgumentException(
403                                 "--rlimit= should have 3 comma-delimited ints");
404                     }
405                     int[] rlimitTuple = new int[limitStrings.length];
406
407                     for(int i=0; i < limitStrings.length; i++) {
408                         rlimitTuple[i] = Integer.parseInt(limitStrings[i]);
409                     }
410
411                     if (rlimits == null) {
412                         rlimits = new ArrayList();
413                     }
414
415                     rlimits.add(rlimitTuple);
416                 } else if (arg.equals("-classpath")) {
417                     if (classpath != null) {
418                         throw new IllegalArgumentException(
419                                 "Duplicate arg specified");
420                     }
421                     try {
422                         classpath = args[++curArg];
423                     } catch (IndexOutOfBoundsException ex) {
424                         throw new IllegalArgumentException(
425                                 "-classpath requires argument");
426                     }
427                 } else if (arg.startsWith("--setgroups=")) {
428                     if (gids != null) {
429                         throw new IllegalArgumentException(
430                                 "Duplicate arg specified");
431                     }
432
433                     String[] params
434                             = arg.substring(arg.indexOf('=') + 1).split(",");
435
436                     gids = new int[params.length];
437
438                     for (int i = params.length - 1; i >= 0 ; i--) {
439                         gids[i] = Integer.parseInt(params[i]);
440                     }
441                 } else {
442                     break;
443                 }
444             }
445
446             if (runtimeInit && classpath != null) {
447                 throw new IllegalArgumentException(
448                         "--runtime-init and -classpath are incompatible");
449             }
450
451             remainingArgs = new String[args.length - curArg];
452
453             System.arraycopy(args, curArg, remainingArgs, 0,
454                     remainingArgs.length);
455         }
456     }
457
458     /**
459      * Reads an argument list from the command socket/
460      * @return Argument list or null if EOF is reached
461      * @throws IOException passed straight through
462      */
463     private String[] readArgumentList()
464             throws IOException {
465
466         /**
467          * See android.os.Process.zygoteSendArgsAndGetPid()
468          * Presently the wire format to the zygote process is:
469          * a) a count of arguments (argc, in essence)
470          * b) a number of newline-separated argument strings equal to count
471          *
472          * After the zygote process reads these it will write the pid of
473          * the child or -1 on failure.
474          */
475
476         int argc;
477
478         try {
479             String s = mSocketReader.readLine();
480
481             if (s == null) {
482                 // EOF reached.
483                 return null;
484             }
485             argc = Integer.parseInt(s);
486         } catch (NumberFormatException ex) {
487             Log.e(TAG, "invalid Zygote wire format: non-int at argc");
488             throw new IOException("invalid wire format");
489         }
490
491         // See bug 1092107: large argc can be used for a DOS attack
492         if (argc > MAX_ZYGOTE_ARGC) {   
493             throw new IOException("max arg count exceeded");
494         }
495
496         String[] result = new String[argc];
497         for (int i = 0; i < argc; i++) {
498             result[i] = mSocketReader.readLine();
499             if (result[i] == null) {
500                 // We got an unexpected EOF.
501                 throw new IOException("truncated request");
502             }
503         }
504
505         return result;
506     }
507
508     /**
509      * Applies zygote security policy per bugs #875058 and #1082165. 
510      * Based on the credentials of the process issuing a zygote command:
511      * <ol>
512      * <li> uid 0 (root) may specify any uid, gid, and setgroups() list
513      * <li> uid 1000 (Process.SYSTEM_UID) may specify any uid &gt; 1000 in normal
514      * operation. It may also specify any gid and setgroups() list it chooses.
515      * In factory test mode, it may specify any UID.
516      * <li> Any other uid may not specify any uid, gid, or setgroups list. The
517      * uid and gid will be inherited from the requesting process.
518      * </ul>
519      *
520      * @param args non-null; zygote spawner arguments
521      * @param peer non-null; peer credentials
522      * @throws ZygoteSecurityException
523      */
524     private static void applyUidSecurityPolicy(Arguments args, Credentials peer)
525             throws ZygoteSecurityException {
526
527         int peerUid = peer.getUid();
528
529         if (peerUid == 0) {
530             // Root can do what it wants
531         } else if (peerUid == Process.SYSTEM_UID ) {
532             // System UID is restricted, except in factory test mode
533             String factoryTest = SystemProperties.get("ro.factorytest");
534             boolean uidRestricted;
535
536             /* In normal operation, SYSTEM_UID can only specify a restricted
537              * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
538              */
539             uidRestricted  
540                  = !(factoryTest.equals("1") || factoryTest.equals("2"));
541
542             if (uidRestricted
543                     && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) {
544                 throw new ZygoteSecurityException(
545                         "System UID may not launch process with UID < "
546                                 + Process.SYSTEM_UID);
547             }
548         } else {
549             // Everything else
550             if (args.uidSpecified || args.gidSpecified
551                 || args.gids != null) {
552                 throw new ZygoteSecurityException(
553                         "App UIDs may not specify uid's or gid's");
554             }
555         }
556
557         // If not otherwise specified, uid and gid are inherited from peer
558         if (!args.uidSpecified) {
559             args.uid = peer.getUid();
560             args.uidSpecified = true;
561         }
562         if (!args.gidSpecified) {
563             args.gid = peer.getGid();
564             args.gidSpecified = true;
565         }
566     }
567
568
569     /**
570      * Applies debugger security policy.
571      * If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
572      * the debugger state is specified via the "--enable-debugger" flag
573      * in the spawn request.
574      *
575      * @param args non-null; zygote spawner args
576      */
577     private static void applyDebuggerSecurityPolicy(Arguments args) {
578         if ("1".equals(SystemProperties.get("ro.debuggable"))) {
579             args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
580         }
581     }
582
583     /**
584      * Applies zygote security policy per bug #1042973. Based on the credentials
585      * of the process issuing a zygote command:
586      * <ol>
587      * <li> peers of  uid 0 (root) and uid 1000 (Process.SYSTEM_UID)
588      * may specify any rlimits.
589      * <li> All other uids may not specify rlimits.
590      * </ul>
591      * @param args non-null; zygote spawner arguments
592      * @param peer non-null; peer credentials
593      * @throws ZygoteSecurityException
594      */
595     private static void applyRlimitSecurityPolicy(
596             Arguments args, Credentials peer)
597             throws ZygoteSecurityException {
598
599         int peerUid = peer.getUid();
600
601         if (!(peerUid == 0 || peerUid == Process.SYSTEM_UID)) {
602             // All peers with UID other than root or SYSTEM_UID
603             if (args.rlimits != null) {
604                 throw new ZygoteSecurityException(
605                         "This UID may not specify rlimits.");
606             }
607         }
608     }
609
610     /**
611      * Applies zygote security policy per bug #1042973. A root peer may
612      * spawn an instance with any capabilities. All other uids may spawn
613      * instances with any of the capabilities in the peer's permitted set
614      * but no more.
615      *
616      * @param args non-null; zygote spawner arguments
617      * @param peer non-null; peer credentials
618      * @throws ZygoteSecurityException
619      */
620     private static void applyCapabilitiesSecurityPolicy(
621             Arguments args, Credentials peer)
622             throws ZygoteSecurityException {
623
624         if (args.permittedCapabilities == 0
625                 && args.effectiveCapabilities == 0) {
626             // nothing to check
627             return;
628         }
629
630         if (peer.getUid() == 0) {
631             // root may specify anything
632             return;
633         }
634
635         long permittedCaps;
636
637         try {
638             permittedCaps = ZygoteInit.capgetPermitted(peer.getPid());
639         } catch (IOException ex) {
640             throw new ZygoteSecurityException(
641                     "Error retrieving peer's capabilities.");
642         }
643
644         /*
645          * Ensure that the client did not specify an effective set larger
646          * than the permitted set. The kernel will enforce this too, but we
647          * do it here to make the following check easier.
648          */
649         if (((~args.permittedCapabilities) & args.effectiveCapabilities) != 0) {
650             throw new ZygoteSecurityException(
651                     "Effective capabilities cannot be superset of "
652                             + " permitted capabilities" );
653         }
654
655         /*
656          * Ensure that the new permitted (and thus the new effective) set is
657          * a subset of the peer process's permitted set
658          */
659
660         if (((~permittedCaps) & args.permittedCapabilities) != 0) {
661             throw new ZygoteSecurityException(
662                     "Peer specified unpermitted capabilities" );
663         }
664     }
665
666     /**
667      * Handles post-fork setup of child proc, closing sockets as appropriate,
668      * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
669      * if successful or returning if failed.
670      *
671      * @param parsedArgs non-null; zygote args
672      * @param descriptors null-ok; new file descriptors for stdio if available.
673      * @param newStderr null-ok; stream to use for stderr until stdio
674      * is reopened.
675      *
676      * @throws ZygoteInit.MethodAndArgsCaller on success to
677      * trampoline to code that invokes static main.
678      */
679     private void handleChildProc(Arguments parsedArgs,
680             FileDescriptor[] descriptors, PrintStream newStderr)
681             throws ZygoteInit.MethodAndArgsCaller {
682
683         /*
684          * Close the socket, unless we're in "peer wait" mode, in which
685          * case it's used to track the liveness of this process.
686          */
687
688         if (parsedArgs.peerWait) {
689             try {
690                 ZygoteInit.setCloseOnExec(mSocket.getFileDescriptor(), true);
691                 sPeerWaitSocket = mSocket;
692             } catch (IOException ex) {
693                 Log.e(TAG, "Zygote Child: error setting peer wait "
694                         + "socket to be close-on-exec", ex);
695             }
696         } else {
697             closeSocket();
698             ZygoteInit.closeServerSocket();
699         }
700
701         if (descriptors != null) {
702             try {
703                 ZygoteInit.reopenStdio(descriptors[0],
704                         descriptors[1], descriptors[2]);
705
706                 for (FileDescriptor fd: descriptors) {
707                     ZygoteInit.closeDescriptor(fd);
708                 }
709                 newStderr = System.err;
710             } catch (IOException ex) {
711                 Log.e(TAG, "Error reopening stdio", ex);
712             }
713         }
714
715         if (parsedArgs.runtimeInit) {
716             RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
717         } else {
718             ClassLoader cloader;
719
720             if (parsedArgs.classpath != null) {
721                 cloader
722                     = new PathClassLoader(parsedArgs.classpath,
723                     ClassLoader.getSystemClassLoader());
724             } else {
725                 cloader = ClassLoader.getSystemClassLoader();
726             }
727
728             String className;
729             try {
730                 className = parsedArgs.remainingArgs[0];
731             } catch (ArrayIndexOutOfBoundsException ex) {
732                 logAndPrintError (newStderr,
733                         "Missing required class name argument", null);
734                 return;
735             }
736             String[] mainArgs
737                     = new String[parsedArgs.remainingArgs.length - 1];
738
739             System.arraycopy(parsedArgs.remainingArgs, 1,
740                     mainArgs, 0, mainArgs.length);
741
742             try {
743                 ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
744             } catch (RuntimeException ex) {
745                 logAndPrintError (newStderr, "Error starting. ", ex);
746             }
747         }
748     }
749
750     /**
751      * Handles post-fork cleanup of parent proc
752      *
753      * @param pid != 0; pid of child if &gt; 0 or indication of failed fork
754      * if &lt; 0;
755      * @param descriptors null-ok; file descriptors for child's new stdio if
756      * specified.
757      * @param parsedArgs non-null; zygote args
758      * @return true for "exit command loop" and false for "continue command
759      * loop"
760      */
761     private boolean handleParentProc(int pid,
762             FileDescriptor[] descriptors, Arguments parsedArgs) {
763
764         if(pid > 0) {
765             // Try to move the new child into the peer's process group.
766             try {
767                 ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid()));
768             } catch (IOException ex) {
769                 // This exception is expected in the case where
770                 // the peer is not in our session
771                 // TODO get rid of this log message in the case where
772                 // getsid(0) != getsid(peer.getPid())
773                 Log.i(TAG, "Zygote: setpgid failed. This is "
774                     + "normal if peer is not in our session");
775             }
776         }
777
778         try {
779             if (descriptors != null) {
780                 for (FileDescriptor fd: descriptors) {
781                     ZygoteInit.closeDescriptor(fd);
782                 }
783             }
784         } catch (IOException ex) {
785             Log.e(TAG, "Error closing passed descriptors in "
786                     + "parent process", ex);
787         }
788
789         try {
790             mSocketOutStream.writeInt(pid);
791         } catch (IOException ex) {
792             Log.e(TAG, "Error reading from command socket", ex);
793             return true;
794         }
795
796         /*
797          * If the peer wants to use the socket to wait on the
798          * newly spawned process, then we're all done.
799          */
800         if (parsedArgs.peerWait) {
801             try {
802                 mSocket.close();
803             } catch (IOException ex) {
804                 Log.e(TAG, "Zygote: error closing sockets", ex);
805             }
806             return true;
807         }
808         return false;
809     }
810
811     /**
812      * Logs an error message and prints it to the specified stream, if
813      * provided
814      *
815      * @param newStderr null-ok; a standard error stream
816      * @param message non-null; error message
817      * @param ex null-ok an exception
818      */
819     private static void logAndPrintError (PrintStream newStderr,
820             String message, Throwable ex) {
821         Log.e(TAG, message, ex);
822         if (newStderr != null) {
823             newStderr.println(message + (ex == null ? "" : ex));
824         }
825     }
826 }