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 static android.system.OsConstants.F_SETFD;
20import static android.system.OsConstants.O_CLOEXEC;
21import static android.system.OsConstants.STDERR_FILENO;
22import static android.system.OsConstants.STDIN_FILENO;
23import static android.system.OsConstants.STDOUT_FILENO;
24
25import android.net.Credentials;
26import android.net.LocalSocket;
27import android.os.Process;
28import android.os.SELinux;
29import android.os.SystemProperties;
30import android.os.Trace;
31import android.system.ErrnoException;
32import android.system.Os;
33import android.util.Log;
34import dalvik.system.VMRuntime;
35import java.io.BufferedReader;
36import java.io.DataInputStream;
37import java.io.DataOutputStream;
38import java.io.FileDescriptor;
39import java.io.FileInputStream;
40import java.io.FileOutputStream;
41import java.io.IOException;
42import java.io.InputStreamReader;
43import java.io.PrintStream;
44import java.nio.charset.StandardCharsets;
45import java.util.ArrayList;
46import libcore.io.IoUtils;
47
48/**
49 * A connection that can make spawn requests.
50 */
51class ZygoteConnection {
52    private static final String TAG = "Zygote";
53
54    /** a prototype instance for a future List.toArray() */
55    private static final int[][] intArray2d = new int[0][0];
56
57    /**
58     * {@link android.net.LocalSocket#setSoTimeout} value for connections.
59     * Effectively, the amount of time a requestor has between the start of
60     * the request and the completed request. The select-loop mode Zygote
61     * doesn't have the logic to return to the select loop in the middle of
62     * a request, so we need to time out here to avoid being denial-of-serviced.
63     */
64    private static final int CONNECTION_TIMEOUT_MILLIS = 1000;
65
66    /** max number of arguments that a connection can specify */
67    private static final int MAX_ZYGOTE_ARGC = 1024;
68
69    /**
70     * The command socket.
71     *
72     * mSocket is retained in the child process in "peer wait" mode, so
73     * that it closes when the child process terminates. In other cases,
74     * it is closed in the peer.
75     */
76    private final LocalSocket mSocket;
77    private final DataOutputStream mSocketOutStream;
78    private final BufferedReader mSocketReader;
79    private final Credentials peer;
80    private final String abiList;
81
82    /**
83     * Constructs instance from connected socket.
84     *
85     * @param socket non-null; connected socket
86     * @param abiList non-null; a list of ABIs this zygote supports.
87     * @throws IOException
88     */
89    ZygoteConnection(LocalSocket socket, String abiList) throws IOException {
90        mSocket = socket;
91        this.abiList = abiList;
92
93        mSocketOutStream
94                = new DataOutputStream(socket.getOutputStream());
95
96        mSocketReader = new BufferedReader(
97                new InputStreamReader(socket.getInputStream()), 256);
98
99        mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS);
100
101        try {
102            peer = mSocket.getPeerCredentials();
103        } catch (IOException ex) {
104            Log.e(TAG, "Cannot read peer credentials", ex);
105            throw ex;
106        }
107    }
108
109    /**
110     * Returns the file descriptor of the associated socket.
111     *
112     * @return null-ok; file descriptor
113     */
114    FileDescriptor getFileDesciptor() {
115        return mSocket.getFileDescriptor();
116    }
117
118    /**
119     * Reads one start command from the command socket. If successful,
120     * a child is forked and a {@link ZygoteInit.MethodAndArgsCaller}
121     * exception is thrown in that child while in the parent process,
122     * the method returns normally. On failure, the child is not
123     * spawned and messages are printed to the log and stderr. Returns
124     * a boolean status value indicating whether an end-of-file on the command
125     * socket has been encountered.
126     *
127     * @return false if command socket should continue to be read from, or
128     * true if an end-of-file has been encountered.
129     * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main()
130     * method in child process
131     */
132    boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
133
134        String args[];
135        Arguments parsedArgs = null;
136        FileDescriptor[] descriptors;
137
138        try {
139            args = readArgumentList();
140            descriptors = mSocket.getAncillaryFileDescriptors();
141        } catch (IOException ex) {
142            Log.w(TAG, "IOException on command socket " + ex.getMessage());
143            closeSocket();
144            return true;
145        }
146
147        if (args == null) {
148            // EOF reached.
149            closeSocket();
150            return true;
151        }
152
153        /** the stderr of the most recent request, if avail */
154        PrintStream newStderr = null;
155
156        if (descriptors != null && descriptors.length >= 3) {
157            newStderr = new PrintStream(
158                    new FileOutputStream(descriptors[2]));
159        }
160
161        int pid = -1;
162        FileDescriptor childPipeFd = null;
163        FileDescriptor serverPipeFd = null;
164
165        try {
166            parsedArgs = new Arguments(args);
167
168            if (parsedArgs.abiListQuery) {
169                return handleAbiListQuery();
170            }
171
172            if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
173                throw new ZygoteSecurityException("Client may not specify capabilities: " +
174                        "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
175                        ", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities));
176            }
177
178            applyUidSecurityPolicy(parsedArgs, peer);
179            applyInvokeWithSecurityPolicy(parsedArgs, peer);
180
181            applyDebuggerSystemProperty(parsedArgs);
182            applyInvokeWithSystemProperty(parsedArgs);
183
184            int[][] rlimits = null;
185
186            if (parsedArgs.rlimits != null) {
187                rlimits = parsedArgs.rlimits.toArray(intArray2d);
188            }
189
190            if (parsedArgs.invokeWith != null) {
191                FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
192                childPipeFd = pipeFds[1];
193                serverPipeFd = pipeFds[0];
194                Os.fcntlInt(childPipeFd, F_SETFD, 0);
195            }
196
197            /**
198             * In order to avoid leaking descriptors to the Zygote child,
199             * the native code must close the two Zygote socket descriptors
200             * in the child process before it switches from Zygote-root to
201             * the UID and privileges of the application being launched.
202             *
203             * In order to avoid "bad file descriptor" errors when the
204             * two LocalSocket objects are closed, the Posix file
205             * descriptors are released via a dup2() call which closes
206             * the socket and substitutes an open descriptor to /dev/null.
207             */
208
209            int [] fdsToClose = { -1, -1 };
210
211            FileDescriptor fd = mSocket.getFileDescriptor();
212
213            if (fd != null) {
214                fdsToClose[0] = fd.getInt$();
215            }
216
217            fd = ZygoteInit.getServerSocketFileDescriptor();
218
219            if (fd != null) {
220                fdsToClose[1] = fd.getInt$();
221            }
222
223            fd = null;
224
225            pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
226                    parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
227                    parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
228                    parsedArgs.appDataDir);
229        } catch (ErrnoException ex) {
230            logAndPrintError(newStderr, "Exception creating pipe", ex);
231        } catch (IllegalArgumentException ex) {
232            logAndPrintError(newStderr, "Invalid zygote arguments", ex);
233        } catch (ZygoteSecurityException ex) {
234            logAndPrintError(newStderr,
235                    "Zygote security policy prevents request: ", ex);
236        }
237
238        try {
239            if (pid == 0) {
240                // in child
241                IoUtils.closeQuietly(serverPipeFd);
242                serverPipeFd = null;
243                handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
244
245                // should never get here, the child is expected to either
246                // throw ZygoteInit.MethodAndArgsCaller or exec().
247                return true;
248            } else {
249                // in parent...pid of < 0 means failure
250                IoUtils.closeQuietly(childPipeFd);
251                childPipeFd = null;
252                return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
253            }
254        } finally {
255            IoUtils.closeQuietly(childPipeFd);
256            IoUtils.closeQuietly(serverPipeFd);
257        }
258    }
259
260    private boolean handleAbiListQuery() {
261        try {
262            final byte[] abiListBytes = abiList.getBytes(StandardCharsets.US_ASCII);
263            mSocketOutStream.writeInt(abiListBytes.length);
264            mSocketOutStream.write(abiListBytes);
265            return false;
266        } catch (IOException ioe) {
267            Log.e(TAG, "Error writing to command socket", ioe);
268            return true;
269        }
270    }
271
272    /**
273     * Closes socket associated with this connection.
274     */
275    void closeSocket() {
276        try {
277            mSocket.close();
278        } catch (IOException ex) {
279            Log.e(TAG, "Exception while closing command "
280                    + "socket in parent", ex);
281        }
282    }
283
284    /**
285     * Handles argument parsing for args related to the zygote spawner.
286     *
287     * Current recognized args:
288     * <ul>
289     *   <li> --setuid=<i>uid of child process, defaults to 0</i>
290     *   <li> --setgid=<i>gid of child process, defaults to 0</i>
291     *   <li> --setgroups=<i>comma-separated list of supplimentary gid's</i>
292     *   <li> --capabilities=<i>a pair of comma-separated integer strings
293     * indicating Linux capabilities(2) set for child. The first string
294     * represents the <code>permitted</code> set, and the second the
295     * <code>effective</code> set. Precede each with 0 or
296     * 0x for octal or hexidecimal value. If unspecified, both default to 0.
297     * This parameter is only applied if the uid of the new process will
298     * be non-0. </i>
299     *   <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call.
300     *    <code>r</code> is the resource, <code>c</code> and <code>m</code>
301     *    are the settings for current and max value.</i>
302     *   <li> --instruction-set=<i>instruction-set-string</i> which instruction set to use/emulate.
303     *   <li> --nice-name=<i>nice name to appear in ps</i>
304     *   <li> --runtime-args indicates that the remaining arg list should
305     * be handed off to com.android.internal.os.RuntimeInit, rather than
306     * processed directly.
307     * Android runtime startup (eg, Binder initialization) is also eschewed.
308     *   <li> [--] &lt;args for RuntimeInit &gt;
309     * </ul>
310     */
311    static class Arguments {
312        /** from --setuid */
313        int uid = 0;
314        boolean uidSpecified;
315
316        /** from --setgid */
317        int gid = 0;
318        boolean gidSpecified;
319
320        /** from --setgroups */
321        int[] gids;
322
323        /**
324         * From --enable-debugger, --enable-checkjni, --enable-assert,
325         * --enable-safemode, --generate-debug-info and --enable-jni-logging.
326         */
327        int debugFlags;
328
329        /** From --mount-external */
330        int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
331
332        /** from --target-sdk-version. */
333        int targetSdkVersion;
334        boolean targetSdkVersionSpecified;
335
336        /** from --nice-name */
337        String niceName;
338
339        /** from --capabilities */
340        boolean capabilitiesSpecified;
341        long permittedCapabilities;
342        long effectiveCapabilities;
343
344        /** from --seinfo */
345        boolean seInfoSpecified;
346        String seInfo;
347
348        /** from all --rlimit=r,c,m */
349        ArrayList<int[]> rlimits;
350
351        /** from --invoke-with */
352        String invokeWith;
353
354        /**
355         * Any args after and including the first non-option arg
356         * (or after a '--')
357         */
358        String remainingArgs[];
359
360        /**
361         * Whether the current arguments constitute an ABI list query.
362         */
363        boolean abiListQuery;
364
365        /**
366         * The instruction set to use, or null when not important.
367         */
368        String instructionSet;
369
370        /**
371         * The app data directory. May be null, e.g., for the system server. Note that this might
372         * not be reliable in the case of process-sharing apps.
373         */
374        String appDataDir;
375
376        /**
377         * Constructs instance and parses args
378         * @param args zygote command-line args
379         * @throws IllegalArgumentException
380         */
381        Arguments(String args[]) throws IllegalArgumentException {
382            parseArgs(args);
383        }
384
385        /**
386         * Parses the commandline arguments intended for the Zygote spawner
387         * (such as "--setuid=" and "--setgid=") and creates an array
388         * containing the remaining args.
389         *
390         * Per security review bug #1112214, duplicate args are disallowed in
391         * critical cases to make injection harder.
392         */
393        private void parseArgs(String args[])
394                throws IllegalArgumentException {
395            int curArg = 0;
396
397            boolean seenRuntimeArgs = false;
398
399            for ( /* curArg */ ; curArg < args.length; curArg++) {
400                String arg = args[curArg];
401
402                if (arg.equals("--")) {
403                    curArg++;
404                    break;
405                } else if (arg.startsWith("--setuid=")) {
406                    if (uidSpecified) {
407                        throw new IllegalArgumentException(
408                                "Duplicate arg specified");
409                    }
410                    uidSpecified = true;
411                    uid = Integer.parseInt(
412                            arg.substring(arg.indexOf('=') + 1));
413                } else if (arg.startsWith("--setgid=")) {
414                    if (gidSpecified) {
415                        throw new IllegalArgumentException(
416                                "Duplicate arg specified");
417                    }
418                    gidSpecified = true;
419                    gid = Integer.parseInt(
420                            arg.substring(arg.indexOf('=') + 1));
421                } else if (arg.startsWith("--target-sdk-version=")) {
422                    if (targetSdkVersionSpecified) {
423                        throw new IllegalArgumentException(
424                                "Duplicate target-sdk-version specified");
425                    }
426                    targetSdkVersionSpecified = true;
427                    targetSdkVersion = Integer.parseInt(
428                            arg.substring(arg.indexOf('=') + 1));
429                } else if (arg.equals("--enable-debugger")) {
430                    debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
431                } else if (arg.equals("--enable-safemode")) {
432                    debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
433                } else if (arg.equals("--enable-checkjni")) {
434                    debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
435                } else if (arg.equals("--generate-debug-info")) {
436                    debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
437                } else if (arg.equals("--always-jit")) {
438                    debugFlags |= Zygote.DEBUG_ALWAYS_JIT;
439                } else if (arg.equals("--native-debuggable")) {
440                    debugFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE;
441                } else if (arg.equals("--enable-jni-logging")) {
442                    debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
443                } else if (arg.equals("--enable-assert")) {
444                    debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
445                } else if (arg.equals("--runtime-args")) {
446                    seenRuntimeArgs = true;
447                } else if (arg.startsWith("--seinfo=")) {
448                    if (seInfoSpecified) {
449                        throw new IllegalArgumentException(
450                                "Duplicate arg specified");
451                    }
452                    seInfoSpecified = true;
453                    seInfo = arg.substring(arg.indexOf('=') + 1);
454                } else if (arg.startsWith("--capabilities=")) {
455                    if (capabilitiesSpecified) {
456                        throw new IllegalArgumentException(
457                                "Duplicate arg specified");
458                    }
459                    capabilitiesSpecified = true;
460                    String capString = arg.substring(arg.indexOf('=')+1);
461
462                    String[] capStrings = capString.split(",", 2);
463
464                    if (capStrings.length == 1) {
465                        effectiveCapabilities = Long.decode(capStrings[0]);
466                        permittedCapabilities = effectiveCapabilities;
467                    } else {
468                        permittedCapabilities = Long.decode(capStrings[0]);
469                        effectiveCapabilities = Long.decode(capStrings[1]);
470                    }
471                } else if (arg.startsWith("--rlimit=")) {
472                    // Duplicate --rlimit arguments are specifically allowed.
473                    String[] limitStrings
474                            = arg.substring(arg.indexOf('=')+1).split(",");
475
476                    if (limitStrings.length != 3) {
477                        throw new IllegalArgumentException(
478                                "--rlimit= should have 3 comma-delimited ints");
479                    }
480                    int[] rlimitTuple = new int[limitStrings.length];
481
482                    for(int i=0; i < limitStrings.length; i++) {
483                        rlimitTuple[i] = Integer.parseInt(limitStrings[i]);
484                    }
485
486                    if (rlimits == null) {
487                        rlimits = new ArrayList();
488                    }
489
490                    rlimits.add(rlimitTuple);
491                } else if (arg.startsWith("--setgroups=")) {
492                    if (gids != null) {
493                        throw new IllegalArgumentException(
494                                "Duplicate arg specified");
495                    }
496
497                    String[] params
498                            = arg.substring(arg.indexOf('=') + 1).split(",");
499
500                    gids = new int[params.length];
501
502                    for (int i = params.length - 1; i >= 0 ; i--) {
503                        gids[i] = Integer.parseInt(params[i]);
504                    }
505                } else if (arg.equals("--invoke-with")) {
506                    if (invokeWith != null) {
507                        throw new IllegalArgumentException(
508                                "Duplicate arg specified");
509                    }
510                    try {
511                        invokeWith = args[++curArg];
512                    } catch (IndexOutOfBoundsException ex) {
513                        throw new IllegalArgumentException(
514                                "--invoke-with requires argument");
515                    }
516                } else if (arg.startsWith("--nice-name=")) {
517                    if (niceName != null) {
518                        throw new IllegalArgumentException(
519                                "Duplicate arg specified");
520                    }
521                    niceName = arg.substring(arg.indexOf('=') + 1);
522                } else if (arg.equals("--mount-external-default")) {
523                    mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
524                } else if (arg.equals("--mount-external-read")) {
525                    mountExternal = Zygote.MOUNT_EXTERNAL_READ;
526                } else if (arg.equals("--mount-external-write")) {
527                    mountExternal = Zygote.MOUNT_EXTERNAL_WRITE;
528                } else if (arg.equals("--query-abi-list")) {
529                    abiListQuery = true;
530                } else if (arg.startsWith("--instruction-set=")) {
531                    instructionSet = arg.substring(arg.indexOf('=') + 1);
532                } else if (arg.startsWith("--app-data-dir=")) {
533                    appDataDir = arg.substring(arg.indexOf('=') + 1);
534                } else {
535                    break;
536                }
537            }
538
539            if (abiListQuery) {
540                if (args.length - curArg > 0) {
541                    throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
542                }
543            } else {
544                if (!seenRuntimeArgs) {
545                    throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
546                }
547
548                remainingArgs = new String[args.length - curArg];
549                System.arraycopy(args, curArg, remainingArgs, 0, remainingArgs.length);
550            }
551        }
552    }
553
554    /**
555     * Reads an argument list from the command socket/
556     * @return Argument list or null if EOF is reached
557     * @throws IOException passed straight through
558     */
559    private String[] readArgumentList()
560            throws IOException {
561
562        /**
563         * See android.os.Process.zygoteSendArgsAndGetPid()
564         * Presently the wire format to the zygote process is:
565         * a) a count of arguments (argc, in essence)
566         * b) a number of newline-separated argument strings equal to count
567         *
568         * After the zygote process reads these it will write the pid of
569         * the child or -1 on failure.
570         */
571
572        int argc;
573
574        try {
575            String s = mSocketReader.readLine();
576
577            if (s == null) {
578                // EOF reached.
579                return null;
580            }
581            argc = Integer.parseInt(s);
582        } catch (NumberFormatException ex) {
583            Log.e(TAG, "invalid Zygote wire format: non-int at argc");
584            throw new IOException("invalid wire format");
585        }
586
587        // See bug 1092107: large argc can be used for a DOS attack
588        if (argc > MAX_ZYGOTE_ARGC) {
589            throw new IOException("max arg count exceeded");
590        }
591
592        String[] result = new String[argc];
593        for (int i = 0; i < argc; i++) {
594            result[i] = mSocketReader.readLine();
595            if (result[i] == null) {
596                // We got an unexpected EOF.
597                throw new IOException("truncated request");
598            }
599        }
600
601        return result;
602    }
603
604    /**
605     * uid 1000 (Process.SYSTEM_UID) may specify any uid &gt; 1000 in normal
606     * operation. It may also specify any gid and setgroups() list it chooses.
607     * In factory test mode, it may specify any UID.
608     *
609     * @param args non-null; zygote spawner arguments
610     * @param peer non-null; peer credentials
611     * @throws ZygoteSecurityException
612     */
613    private static void applyUidSecurityPolicy(Arguments args, Credentials peer)
614            throws ZygoteSecurityException {
615
616        if (peer.getUid() == Process.SYSTEM_UID) {
617            String factoryTest = SystemProperties.get("ro.factorytest");
618            boolean uidRestricted;
619
620            /* In normal operation, SYSTEM_UID can only specify a restricted
621             * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
622             */
623            uidRestricted = !(factoryTest.equals("1") || factoryTest.equals("2"));
624
625            if (uidRestricted && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) {
626                throw new ZygoteSecurityException(
627                        "System UID may not launch process with UID < "
628                        + Process.SYSTEM_UID);
629            }
630        }
631
632        // If not otherwise specified, uid and gid are inherited from peer
633        if (!args.uidSpecified) {
634            args.uid = peer.getUid();
635            args.uidSpecified = true;
636        }
637        if (!args.gidSpecified) {
638            args.gid = peer.getGid();
639            args.gidSpecified = true;
640        }
641    }
642
643    /**
644     * Applies debugger system properties to the zygote arguments.
645     *
646     * If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
647     * the debugger state is specified via the "--enable-debugger" flag
648     * in the spawn request.
649     *
650     * @param args non-null; zygote spawner args
651     */
652    public static void applyDebuggerSystemProperty(Arguments args) {
653        if ("1".equals(SystemProperties.get("ro.debuggable"))) {
654            args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
655        }
656    }
657
658    /**
659     * Applies zygote security policy.
660     * Based on the credentials of the process issuing a zygote command:
661     * <ol>
662     * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a
663     * wrapper command.
664     * <li> Any other uid may not specify any invoke-with argument.
665     * </ul>
666     *
667     * @param args non-null; zygote spawner arguments
668     * @param peer non-null; peer credentials
669     * @throws ZygoteSecurityException
670     */
671    private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer)
672            throws ZygoteSecurityException {
673        int peerUid = peer.getUid();
674
675        if (args.invokeWith != null && peerUid != 0) {
676            throw new ZygoteSecurityException("Peer is not permitted to specify "
677                    + "an explicit invoke-with wrapper command");
678        }
679    }
680
681    /**
682     * Applies invoke-with system properties to the zygote arguments.
683     *
684     * @param args non-null; zygote args
685     */
686    public static void applyInvokeWithSystemProperty(Arguments args) {
687        if (args.invokeWith == null && args.niceName != null) {
688            String property = "wrap." + args.niceName;
689            if (property.length() > 31) {
690                // Properties with a trailing "." are illegal.
691                if (property.charAt(30) != '.') {
692                    property = property.substring(0, 31);
693                } else {
694                    property = property.substring(0, 30);
695                }
696            }
697            args.invokeWith = SystemProperties.get(property);
698            if (args.invokeWith != null && args.invokeWith.length() == 0) {
699                args.invokeWith = null;
700            }
701        }
702    }
703
704    /**
705     * Handles post-fork setup of child proc, closing sockets as appropriate,
706     * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
707     * if successful or returning if failed.
708     *
709     * @param parsedArgs non-null; zygote args
710     * @param descriptors null-ok; new file descriptors for stdio if available.
711     * @param pipeFd null-ok; pipe for communication back to Zygote.
712     * @param newStderr null-ok; stream to use for stderr until stdio
713     * is reopened.
714     *
715     * @throws ZygoteInit.MethodAndArgsCaller on success to
716     * trampoline to code that invokes static main.
717     */
718    private void handleChildProc(Arguments parsedArgs,
719            FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
720            throws ZygoteInit.MethodAndArgsCaller {
721        /**
722         * By the time we get here, the native code has closed the two actual Zygote
723         * socket connections, and substituted /dev/null in their place.  The LocalSocket
724         * objects still need to be closed properly.
725         */
726
727        closeSocket();
728        ZygoteInit.closeServerSocket();
729
730        if (descriptors != null) {
731            try {
732                Os.dup2(descriptors[0], STDIN_FILENO);
733                Os.dup2(descriptors[1], STDOUT_FILENO);
734                Os.dup2(descriptors[2], STDERR_FILENO);
735
736                for (FileDescriptor fd: descriptors) {
737                    IoUtils.closeQuietly(fd);
738                }
739                newStderr = System.err;
740            } catch (ErrnoException ex) {
741                Log.e(TAG, "Error reopening stdio", ex);
742            }
743        }
744
745        if (parsedArgs.niceName != null) {
746            Process.setArgV0(parsedArgs.niceName);
747        }
748
749        // End of the postFork event.
750        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
751        if (parsedArgs.invokeWith != null) {
752            WrapperInit.execApplication(parsedArgs.invokeWith,
753                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
754                    VMRuntime.getCurrentInstructionSet(),
755                    pipeFd, parsedArgs.remainingArgs);
756        } else {
757            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
758                    parsedArgs.remainingArgs, null /* classLoader */);
759        }
760    }
761
762    /**
763     * Handles post-fork cleanup of parent proc
764     *
765     * @param pid != 0; pid of child if &gt; 0 or indication of failed fork
766     * if &lt; 0;
767     * @param descriptors null-ok; file descriptors for child's new stdio if
768     * specified.
769     * @param pipeFd null-ok; pipe for communication with child.
770     * @param parsedArgs non-null; zygote args
771     * @return true for "exit command loop" and false for "continue command
772     * loop"
773     */
774    private boolean handleParentProc(int pid,
775            FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) {
776
777        if (pid > 0) {
778            setChildPgid(pid);
779        }
780
781        if (descriptors != null) {
782            for (FileDescriptor fd: descriptors) {
783                IoUtils.closeQuietly(fd);
784            }
785        }
786
787        boolean usingWrapper = false;
788        if (pipeFd != null && pid > 0) {
789            DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
790            int innerPid = -1;
791            try {
792                innerPid = is.readInt();
793            } catch (IOException ex) {
794                Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
795            } finally {
796                try {
797                    is.close();
798                } catch (IOException ex) {
799                }
800            }
801
802            // Ensure that the pid reported by the wrapped process is either the
803            // child process that we forked, or a descendant of it.
804            if (innerPid > 0) {
805                int parentPid = innerPid;
806                while (parentPid > 0 && parentPid != pid) {
807                    parentPid = Process.getParentPid(parentPid);
808                }
809                if (parentPid > 0) {
810                    Log.i(TAG, "Wrapped process has pid " + innerPid);
811                    pid = innerPid;
812                    usingWrapper = true;
813                } else {
814                    Log.w(TAG, "Wrapped process reported a pid that is not a child of "
815                            + "the process that we forked: childPid=" + pid
816                            + " innerPid=" + innerPid);
817                }
818            }
819        }
820
821        try {
822            mSocketOutStream.writeInt(pid);
823            mSocketOutStream.writeBoolean(usingWrapper);
824        } catch (IOException ex) {
825            Log.e(TAG, "Error writing to command socket", ex);
826            return true;
827        }
828
829        return false;
830    }
831
832    private void setChildPgid(int pid) {
833        // Try to move the new child into the peer's process group.
834        try {
835            Os.setpgid(pid, Os.getpgid(peer.getPid()));
836        } catch (ErrnoException ex) {
837            // This exception is expected in the case where
838            // the peer is not in our session
839            // TODO get rid of this log message in the case where
840            // getsid(0) != getsid(peer.getPid())
841            Log.i(TAG, "Zygote: setpgid failed. This is "
842                + "normal if peer is not in our session");
843        }
844    }
845
846    /**
847     * Logs an error message and prints it to the specified stream, if
848     * provided
849     *
850     * @param newStderr null-ok; a standard error stream
851     * @param message non-null; error message
852     * @param ex null-ok an exception
853     */
854    private static void logAndPrintError (PrintStream newStderr,
855            String message, Throwable ex) {
856        Log.e(TAG, message, ex);
857        if (newStderr != null) {
858            newStderr.println(message + (ex == null ? "" : ex));
859        }
860    }
861}
862