Home | History | Annotate | Download | only in os
      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 }
    827