ZygoteConnection.java revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
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
17package com.android.internal.os;
18
19import android.net.Credentials;
20import android.net.LocalSocket;
21import android.os.Process;
22import android.os.SystemProperties;
23import android.util.Log;
24
25import dalvik.system.PathClassLoader;
26import dalvik.system.Zygote;
27
28import java.io.BufferedReader;
29import java.io.DataOutputStream;
30import java.io.FileDescriptor;
31import java.io.FileOutputStream;
32import java.io.IOException;
33import java.io.InputStreamReader;
34import java.io.PrintStream;
35import java.util.ArrayList;
36
37/**
38 * A connection that can make spawn requests.
39 */
40class 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        /** from --enable-debugger, --enable-checkjni, --enable-assert */
299        int debugFlags;
300
301        /** from --classpath */
302        String classpath;
303
304        /** from --runtime-init */
305        boolean runtimeInit;
306
307        /** from --capabilities */
308        boolean capabilitiesSpecified;
309        long permittedCapabilities;
310        long effectiveCapabilities;
311
312        /** from all --rlimit=r,c,m */
313        ArrayList<int[]> rlimits;
314
315        /**
316         * Any args after and including the first non-option arg
317         * (or after a '--')
318         */
319        String remainingArgs[];
320
321        /**
322         * Constructs instance and parses args
323         * @param args zygote command-line args
324         * @throws IllegalArgumentException
325         */
326        Arguments(String args[]) throws IllegalArgumentException {
327            parseArgs(args);
328        }
329
330        /**
331         * Parses the commandline arguments intended for the Zygote spawner
332         * (such as "--setuid=" and "--setgid=") and creates an array
333         * containing the remaining args.
334         *
335         * Per security review bug #1112214, duplicate args are disallowed in
336         * critical cases to make injection harder.
337         */
338        private void parseArgs(String args[])
339                throws IllegalArgumentException {
340            int curArg = 0;
341
342            for ( /* curArg */ ; curArg < args.length; curArg++) {
343                String arg = args[curArg];
344
345                if (arg.equals("--")) {
346                    curArg++;
347                    break;
348                } else if (arg.startsWith("--setuid=")) {
349                    if (uidSpecified) {
350                        throw new IllegalArgumentException(
351                                "Duplicate arg specified");
352                    }
353                    uidSpecified = true;
354                    uid = Integer.parseInt(
355                            arg.substring(arg.indexOf('=') + 1));
356                } else if (arg.startsWith("--setgid=")) {
357                    if (gidSpecified) {
358                        throw new IllegalArgumentException(
359                                "Duplicate arg specified");
360                    }
361                    gidSpecified = true;
362                    gid = Integer.parseInt(
363                            arg.substring(arg.indexOf('=') + 1));
364                } else if (arg.equals("--enable-debugger")) {
365                    debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
366                } else if (arg.equals("--enable-checkjni")) {
367                    debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
368                } else if (arg.equals("--enable-assert")) {
369                    debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
370                } else if (arg.equals("--peer-wait")) {
371                    peerWait = true;
372                } else if (arg.equals("--runtime-init")) {
373                    runtimeInit = true;
374                } else if (arg.startsWith("--capabilities=")) {
375                    if (capabilitiesSpecified) {
376                        throw new IllegalArgumentException(
377                                "Duplicate arg specified");
378                    }
379                    capabilitiesSpecified = true;
380                    String capString = arg.substring(arg.indexOf('=')+1);
381
382                    String[] capStrings = capString.split(",", 2);
383
384                    if (capStrings.length == 1) {
385                        effectiveCapabilities = Long.decode(capStrings[0]);
386                        permittedCapabilities = effectiveCapabilities;
387                    } else {
388                        permittedCapabilities = Long.decode(capStrings[0]);
389                        effectiveCapabilities = Long.decode(capStrings[1]);
390                    }
391                } else if (arg.startsWith("--rlimit=")) {
392                    // Duplicate --rlimit arguments are specifically allowed.
393                    String[] limitStrings
394                            = arg.substring(arg.indexOf('=')+1).split(",");
395
396                    if (limitStrings.length != 3) {
397                        throw new IllegalArgumentException(
398                                "--rlimit= should have 3 comma-delimited ints");
399                    }
400                    int[] rlimitTuple = new int[limitStrings.length];
401
402                    for(int i=0; i < limitStrings.length; i++) {
403                        rlimitTuple[i] = Integer.parseInt(limitStrings[i]);
404                    }
405
406                    if (rlimits == null) {
407                        rlimits = new ArrayList();
408                    }
409
410                    rlimits.add(rlimitTuple);
411                } else if (arg.equals("-classpath")) {
412                    if (classpath != null) {
413                        throw new IllegalArgumentException(
414                                "Duplicate arg specified");
415                    }
416                    try {
417                        classpath = args[++curArg];
418                    } catch (IndexOutOfBoundsException ex) {
419                        throw new IllegalArgumentException(
420                                "-classpath requires argument");
421                    }
422                } else if (arg.startsWith("--setgroups=")) {
423                    if (gids != null) {
424                        throw new IllegalArgumentException(
425                                "Duplicate arg specified");
426                    }
427
428                    String[] params
429                            = arg.substring(arg.indexOf('=') + 1).split(",");
430
431                    gids = new int[params.length];
432
433                    for (int i = params.length - 1; i >= 0 ; i--) {
434                        gids[i] = Integer.parseInt(params[i]);
435                    }
436                } else {
437                    break;
438                }
439            }
440
441            if (runtimeInit && classpath != null) {
442                throw new IllegalArgumentException(
443                        "--runtime-init and -classpath are incompatible");
444            }
445
446            remainingArgs = new String[args.length - curArg];
447
448            System.arraycopy(args, curArg, remainingArgs, 0,
449                    remainingArgs.length);
450        }
451    }
452
453    /**
454     * Reads an argument list from the command socket/
455     * @return Argument list or null if EOF is reached
456     * @throws IOException passed straight through
457     */
458    private String[] readArgumentList()
459            throws IOException {
460
461        /**
462         * See android.os.Process.zygoteSendArgsAndGetPid()
463         * Presently the wire format to the zygote process is:
464         * a) a count of arguments (argc, in essence)
465         * b) a number of newline-separated argument strings equal to count
466         *
467         * After the zygote process reads these it will write the pid of
468         * the child or -1 on failure.
469         */
470
471        int argc;
472
473        try {
474            String s = mSocketReader.readLine();
475
476            if (s == null) {
477                // EOF reached.
478                return null;
479            }
480            argc = Integer.parseInt(s);
481        } catch (NumberFormatException ex) {
482            Log.e(TAG, "invalid Zygote wire format: non-int at argc");
483            throw new IOException("invalid wire format");
484        }
485
486        // See bug 1092107: large argc can be used for a DOS attack
487        if (argc > MAX_ZYGOTE_ARGC) {
488            throw new IOException("max arg count exceeded");
489        }
490
491        String[] result = new String[argc];
492        for (int i = 0; i < argc; i++) {
493            result[i] = mSocketReader.readLine();
494            if (result[i] == null) {
495                // We got an unexpected EOF.
496                throw new IOException("truncated request");
497            }
498        }
499
500        return result;
501    }
502
503    /**
504     * Applies zygote security policy per bugs #875058 and #1082165.
505     * Based on the credentials of the process issuing a zygote command:
506     * <ol>
507     * <li> uid 0 (root) may specify any uid, gid, and setgroups() list
508     * <li> uid 1000 (Process.SYSTEM_UID) may specify any uid &gt; 1000 in normal
509     * operation. It may also specify any gid and setgroups() list it chooses.
510     * In factory test mode, it may specify any UID.
511     * <li> Any other uid may not specify any uid, gid, or setgroups list. The
512     * uid and gid will be inherited from the requesting process.
513     * </ul>
514     *
515     * @param args non-null; zygote spawner arguments
516     * @param peer non-null; peer credentials
517     * @throws ZygoteSecurityException
518     */
519    private static void applyUidSecurityPolicy(Arguments args, Credentials peer)
520            throws ZygoteSecurityException {
521
522        int peerUid = peer.getUid();
523
524        if (peerUid == 0) {
525            // Root can do what it wants
526        } else if (peerUid == Process.SYSTEM_UID ) {
527            // System UID is restricted, except in factory test mode
528            String factoryTest = SystemProperties.get("ro.factorytest");
529            boolean uidRestricted;
530
531            /* In normal operation, SYSTEM_UID can only specify a restricted
532             * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
533             */
534            uidRestricted
535                 = !(factoryTest.equals("1") || factoryTest.equals("2"));
536
537            if (uidRestricted
538                    && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) {
539                throw new ZygoteSecurityException(
540                        "System UID may not launch process with UID < "
541                                + Process.SYSTEM_UID);
542            }
543        } else {
544            // Everything else
545            if (args.uidSpecified || args.gidSpecified
546                || args.gids != null) {
547                throw new ZygoteSecurityException(
548                        "App UIDs may not specify uid's or gid's");
549            }
550        }
551
552        // If not otherwise specified, uid and gid are inherited from peer
553        if (!args.uidSpecified) {
554            args.uid = peer.getUid();
555            args.uidSpecified = true;
556        }
557        if (!args.gidSpecified) {
558            args.gid = peer.getGid();
559            args.gidSpecified = true;
560        }
561    }
562
563
564    /**
565     * Applies debugger security policy.
566     * If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
567     * the debugger state is specified via the "--enable-debugger" flag
568     * in the spawn request.
569     *
570     * @param args non-null; zygote spawner args
571     */
572    private static void applyDebuggerSecurityPolicy(Arguments args) {
573        if ("1".equals(SystemProperties.get("ro.debuggable"))) {
574            args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
575        }
576    }
577
578    /**
579     * Applies zygote security policy per bug #1042973. Based on the credentials
580     * of the process issuing a zygote command:
581     * <ol>
582     * <li> peers of  uid 0 (root) and uid 1000 (Process.SYSTEM_UID)
583     * may specify any rlimits.
584     * <li> All other uids may not specify rlimits.
585     * </ul>
586     * @param args non-null; zygote spawner arguments
587     * @param peer non-null; peer credentials
588     * @throws ZygoteSecurityException
589     */
590    private static void applyRlimitSecurityPolicy(
591            Arguments args, Credentials peer)
592            throws ZygoteSecurityException {
593
594        int peerUid = peer.getUid();
595
596        if (!(peerUid == 0 || peerUid == Process.SYSTEM_UID)) {
597            // All peers with UID other than root or SYSTEM_UID
598            if (args.rlimits != null) {
599                throw new ZygoteSecurityException(
600                        "This UID may not specify rlimits.");
601            }
602        }
603    }
604
605    /**
606     * Applies zygote security policy per bug #1042973. A root peer may
607     * spawn an instance with any capabilities. All other uids may spawn
608     * instances with any of the capabilities in the peer's permitted set
609     * but no more.
610     *
611     * @param args non-null; zygote spawner arguments
612     * @param peer non-null; peer credentials
613     * @throws ZygoteSecurityException
614     */
615    private static void applyCapabilitiesSecurityPolicy(
616            Arguments args, Credentials peer)
617            throws ZygoteSecurityException {
618
619        if (args.permittedCapabilities == 0
620                && args.effectiveCapabilities == 0) {
621            // nothing to check
622            return;
623        }
624
625        if (peer.getUid() == 0) {
626            // root may specify anything
627            return;
628        }
629
630        long permittedCaps;
631
632        try {
633            permittedCaps = ZygoteInit.capgetPermitted(peer.getPid());
634        } catch (IOException ex) {
635            throw new ZygoteSecurityException(
636                    "Error retrieving peer's capabilities.");
637        }
638
639        /*
640         * Ensure that the client did not specify an effective set larger
641         * than the permitted set. The kernel will enforce this too, but we
642         * do it here to make the following check easier.
643         */
644        if (((~args.permittedCapabilities) & args.effectiveCapabilities) != 0) {
645            throw new ZygoteSecurityException(
646                    "Effective capabilities cannot be superset of "
647                            + " permitted capabilities" );
648        }
649
650        /*
651         * Ensure that the new permitted (and thus the new effective) set is
652         * a subset of the peer process's permitted set
653         */
654
655        if (((~permittedCaps) & args.permittedCapabilities) != 0) {
656            throw new ZygoteSecurityException(
657                    "Peer specified unpermitted capabilities" );
658        }
659    }
660
661    /**
662     * Handles post-fork setup of child proc, closing sockets as appropriate,
663     * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
664     * if successful or returning if failed.
665     *
666     * @param parsedArgs non-null; zygote args
667     * @param descriptors null-ok; new file descriptors for stdio if available.
668     * @param newStderr null-ok; stream to use for stderr until stdio
669     * is reopened.
670     *
671     * @throws ZygoteInit.MethodAndArgsCaller on success to
672     * trampoline to code that invokes static main.
673     */
674    private void handleChildProc(Arguments parsedArgs,
675            FileDescriptor[] descriptors, PrintStream newStderr)
676            throws ZygoteInit.MethodAndArgsCaller {
677
678        /*
679         * First, set the capabilities if necessary
680         */
681
682        if (parsedArgs.uid != 0) {
683            try {
684                ZygoteInit.setCapabilities(parsedArgs.permittedCapabilities,
685                        parsedArgs.effectiveCapabilities);
686            } catch (IOException ex) {
687                Log.e(TAG, "Error setting capabilities", ex);
688            }
689        }
690
691        /*
692         * Close the socket, unless we're in "peer wait" mode, in which
693         * case it's used to track the liveness of this process.
694         */
695
696        if (parsedArgs.peerWait) {
697            try {
698                ZygoteInit.setCloseOnExec(mSocket.getFileDescriptor(), true);
699                sPeerWaitSocket = mSocket;
700            } catch (IOException ex) {
701                Log.e(TAG, "Zygote Child: error setting peer wait "
702                        + "socket to be close-on-exec", ex);
703            }
704        } else {
705            closeSocket();
706            ZygoteInit.closeServerSocket();
707        }
708
709        if (descriptors != null) {
710            try {
711                ZygoteInit.reopenStdio(descriptors[0],
712                        descriptors[1], descriptors[2]);
713
714                for (FileDescriptor fd: descriptors) {
715                    ZygoteInit.closeDescriptor(fd);
716                }
717                newStderr = System.err;
718            } catch (IOException ex) {
719                Log.e(TAG, "Error reopening stdio", ex);
720            }
721        }
722
723        if (parsedArgs.runtimeInit) {
724            RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
725        } else {
726            ClassLoader cloader;
727
728            if (parsedArgs.classpath != null) {
729                cloader
730                    = new PathClassLoader(parsedArgs.classpath,
731                    ClassLoader.getSystemClassLoader());
732            } else {
733                cloader = ClassLoader.getSystemClassLoader();
734            }
735
736            String className;
737            try {
738                className = parsedArgs.remainingArgs[0];
739            } catch (ArrayIndexOutOfBoundsException ex) {
740                logAndPrintError (newStderr,
741                        "Missing required class name argument", null);
742                return;
743            }
744            String[] mainArgs
745                    = new String[parsedArgs.remainingArgs.length - 1];
746
747            System.arraycopy(parsedArgs.remainingArgs, 1,
748                    mainArgs, 0, mainArgs.length);
749
750            try {
751                ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
752            } catch (RuntimeException ex) {
753                logAndPrintError (newStderr, "Error starting. ", ex);
754            }
755        }
756    }
757
758    /**
759     * Handles post-fork cleanup of parent proc
760     *
761     * @param pid != 0; pid of child if &gt; 0 or indication of failed fork
762     * if &lt; 0;
763     * @param descriptors null-ok; file descriptors for child's new stdio if
764     * specified.
765     * @param parsedArgs non-null; zygote args
766     * @return true for "exit command loop" and false for "continue command
767     * loop"
768     */
769    private boolean handleParentProc(int pid,
770            FileDescriptor[] descriptors, Arguments parsedArgs) {
771
772        if(pid > 0) {
773            // Try to move the new child into the peer's process group.
774            try {
775                ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid()));
776            } catch (IOException ex) {
777                // This exception is expected in the case where
778                // the peer is not in our session
779                // TODO get rid of this log message in the case where
780                // getsid(0) != getsid(peer.getPid())
781                Log.i(TAG, "Zygote: setpgid failed. This is "
782                    + "normal if peer is not in our session");
783            }
784        }
785
786        try {
787            if (descriptors != null) {
788                for (FileDescriptor fd: descriptors) {
789                    ZygoteInit.closeDescriptor(fd);
790                }
791            }
792        } catch (IOException ex) {
793            Log.e(TAG, "Error closing passed descriptors in "
794                    + "parent process", ex);
795        }
796
797        try {
798            mSocketOutStream.writeInt(pid);
799        } catch (IOException ex) {
800            Log.e(TAG, "Error reading from command socket", ex);
801            return true;
802        }
803
804        /*
805         * If the peer wants to use the socket to wait on the
806         * newly spawned process, then we're all done.
807         */
808        if (parsedArgs.peerWait) {
809            try {
810                mSocket.close();
811            } catch (IOException ex) {
812                Log.e(TAG, "Zygote: error closing sockets", ex);
813            }
814            return true;
815        }
816        return false;
817    }
818
819    /**
820     * Logs an error message and prints it to the specified stream, if
821     * provided
822     *
823     * @param newStderr null-ok; a standard error stream
824     * @param message non-null; error message
825     * @param ex null-ok an exception
826     */
827    private static void logAndPrintError (PrintStream newStderr,
828            String message, Throwable ex) {
829        Log.e(TAG, message, ex);
830        if (newStderr != null) {
831            newStderr.println(message + (ex == null ? "" : ex));
832        }
833    }
834}
835