• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
ZygoteConnection(LocalSocket socket)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      */
getFileDesciptor()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      */
run()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      */
runOnce()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      */
closeSocket()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          */
Arguments(String args[])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          */
parseArgs(String args[])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      */
readArgumentList()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      */
applyUidSecurityPolicy(Arguments args, Credentials peer)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      */
applyDebuggerSecurityPolicy(Arguments args)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      */
applyRlimitSecurityPolicy( Arguments args, Credentials peer)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      */
applyCapabilitiesSecurityPolicy( Arguments args, Credentials peer)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      */
handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, PrintStream newStderr)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      */
handleParentProc(int pid, FileDescriptor[] descriptors, Arguments parsedArgs)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      */
logAndPrintError(PrintStream newStderr, String message, Throwable ex)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