• 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 static android.system.OsConstants.S_IRWXG;
20 import static android.system.OsConstants.S_IRWXO;
21 
22 import android.content.res.Resources;
23 import android.content.res.TypedArray;
24 import android.net.LocalServerSocket;
25 import android.opengl.EGL14;
26 import android.os.Build;
27 import android.os.Debug;
28 import android.os.Process;
29 import android.os.SystemClock;
30 import android.os.SystemProperties;
31 import android.os.Trace;
32 import android.system.ErrnoException;
33 import android.system.Os;
34 import android.system.OsConstants;
35 import android.util.EventLog;
36 import android.util.Log;
37 import android.util.Slog;
38 import android.webkit.WebViewFactory;
39 
40 import dalvik.system.DexFile;
41 import dalvik.system.PathClassLoader;
42 import dalvik.system.VMRuntime;
43 
44 import libcore.io.IoUtils;
45 
46 import java.io.BufferedReader;
47 import java.io.FileDescriptor;
48 import java.io.IOException;
49 import java.io.InputStream;
50 import java.io.InputStreamReader;
51 import java.lang.reflect.InvocationTargetException;
52 import java.lang.reflect.Method;
53 import java.lang.reflect.Modifier;
54 import java.util.ArrayList;
55 
56 /**
57  * Startup class for the zygote process.
58  *
59  * Pre-initializes some classes, and then waits for commands on a UNIX domain
60  * socket. Based on these commands, forks off child processes that inherit
61  * the initial state of the VM.
62  *
63  * Please see {@link ZygoteConnection.Arguments} for documentation on the
64  * client protocol.
65  *
66  * @hide
67  */
68 public class ZygoteInit {
69     private static final String TAG = "Zygote";
70 
71     private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
72 
73     private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
74 
75     private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
76     private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
77 
78     /** when preloading, GC after allocating this many bytes */
79     private static final int PRELOAD_GC_THRESHOLD = 50000;
80 
81     private static final String ABI_LIST_ARG = "--abi-list=";
82 
83     private static final String SOCKET_NAME_ARG = "--socket-name=";
84 
85     private static LocalServerSocket sServerSocket;
86 
87     /**
88      * Used to pre-load resources.  We hold a global reference on it so it
89      * never gets destroyed.
90      */
91     private static Resources mResources;
92 
93     /**
94      * The number of times that the main Zygote loop
95      * should run before calling gc() again.
96      */
97     static final int GC_LOOP_COUNT = 10;
98 
99     /**
100      * The name of a resource file that contains classes to preload.
101      */
102     private static final String PRELOADED_CLASSES = "preloaded-classes";
103 
104     /** Controls whether we should preload resources during zygote init. */
105     private static final boolean PRELOAD_RESOURCES = true;
106 
107     /**
108      * Invokes a static "main(argv[]) method on class "className".
109      * Converts various failing exceptions into RuntimeExceptions, with
110      * the assumption that they will then cause the VM instance to exit.
111      *
112      * @param loader class loader to use
113      * @param className Fully-qualified class name
114      * @param argv Argument vector for main()
115      */
invokeStaticMain(ClassLoader loader, String className, String[] argv)116     static void invokeStaticMain(ClassLoader loader,
117             String className, String[] argv)
118             throws ZygoteInit.MethodAndArgsCaller {
119         Class<?> cl;
120 
121         try {
122             cl = loader.loadClass(className);
123         } catch (ClassNotFoundException ex) {
124             throw new RuntimeException(
125                     "Missing class when invoking static main " + className,
126                     ex);
127         }
128 
129         Method m;
130         try {
131             m = cl.getMethod("main", new Class[] { String[].class });
132         } catch (NoSuchMethodException ex) {
133             throw new RuntimeException(
134                     "Missing static main on " + className, ex);
135         } catch (SecurityException ex) {
136             throw new RuntimeException(
137                     "Problem getting static main on " + className, ex);
138         }
139 
140         int modifiers = m.getModifiers();
141         if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
142             throw new RuntimeException(
143                     "Main method is not public and static on " + className);
144         }
145 
146         /*
147          * This throw gets caught in ZygoteInit.main(), which responds
148          * by invoking the exception's run() method. This arrangement
149          * clears up all the stack frames that were required in setting
150          * up the process.
151          */
152         throw new ZygoteInit.MethodAndArgsCaller(m, argv);
153     }
154 
155     /**
156      * Registers a server socket for zygote command connections
157      *
158      * @throws RuntimeException when open fails
159      */
registerZygoteSocket(String socketName)160     private static void registerZygoteSocket(String socketName) {
161         if (sServerSocket == null) {
162             int fileDesc;
163             final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
164             try {
165                 String env = System.getenv(fullSocketName);
166                 fileDesc = Integer.parseInt(env);
167             } catch (RuntimeException ex) {
168                 throw new RuntimeException(fullSocketName + " unset or invalid", ex);
169             }
170 
171             try {
172                 sServerSocket = new LocalServerSocket(
173                         createFileDescriptor(fileDesc));
174             } catch (IOException ex) {
175                 throw new RuntimeException(
176                         "Error binding to local socket '" + fileDesc + "'", ex);
177             }
178         }
179     }
180 
181     /**
182      * Waits for and accepts a single command connection. Throws
183      * RuntimeException on failure.
184      */
acceptCommandPeer(String abiList)185     private static ZygoteConnection acceptCommandPeer(String abiList) {
186         try {
187             return new ZygoteConnection(sServerSocket.accept(), abiList);
188         } catch (IOException ex) {
189             throw new RuntimeException(
190                     "IOException during accept()", ex);
191         }
192     }
193 
194     /**
195      * Close and clean up zygote sockets. Called on shutdown and on the
196      * child's exit path.
197      */
closeServerSocket()198     static void closeServerSocket() {
199         try {
200             if (sServerSocket != null) {
201                 FileDescriptor fd = sServerSocket.getFileDescriptor();
202                 sServerSocket.close();
203                 if (fd != null) {
204                     Os.close(fd);
205                 }
206             }
207         } catch (IOException ex) {
208             Log.e(TAG, "Zygote:  error closing sockets", ex);
209         } catch (ErrnoException ex) {
210             Log.e(TAG, "Zygote:  error closing descriptor", ex);
211         }
212 
213         sServerSocket = null;
214     }
215 
216     /**
217      * Return the server socket's underlying file descriptor, so that
218      * ZygoteConnection can pass it to the native code for proper
219      * closure after a child process is forked off.
220      */
221 
getServerSocketFileDescriptor()222     static FileDescriptor getServerSocketFileDescriptor() {
223         return sServerSocket.getFileDescriptor();
224     }
225 
226     private static final int UNPRIVILEGED_UID = 9999;
227     private static final int UNPRIVILEGED_GID = 9999;
228 
229     private static final int ROOT_UID = 0;
230     private static final int ROOT_GID = 0;
231 
232     /**
233      * Sets effective user ID.
234      */
setEffectiveUser(int uid)235     private static void setEffectiveUser(int uid) {
236         int errno = setreuid(ROOT_UID, uid);
237         if (errno != 0) {
238             Log.e(TAG, "setreuid() failed. errno: " + errno);
239         }
240     }
241 
242     /**
243      * Sets effective group ID.
244      */
setEffectiveGroup(int gid)245     private static void setEffectiveGroup(int gid) {
246         int errno = setregid(ROOT_GID, gid);
247         if (errno != 0) {
248             Log.e(TAG, "setregid() failed. errno: " + errno);
249         }
250     }
251 
preload()252     static void preload() {
253         Log.d(TAG, "begin preload");
254         preloadClasses();
255         preloadResources();
256         preloadOpenGL();
257         preloadSharedLibraries();
258         // Ask the WebViewFactory to do any initialization that must run in the zygote process,
259         // for memory sharing purposes.
260         WebViewFactory.prepareWebViewInZygote();
261         Log.d(TAG, "end preload");
262     }
263 
preloadSharedLibraries()264     private static void preloadSharedLibraries() {
265         Log.i(TAG, "Preloading shared libraries...");
266         System.loadLibrary("android");
267         System.loadLibrary("compiler_rt");
268         System.loadLibrary("jnigraphics");
269     }
270 
preloadOpenGL()271     private static void preloadOpenGL() {
272         if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
273             EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
274         }
275     }
276 
277     /**
278      * Performs Zygote process initialization. Loads and initializes
279      * commonly used classes.
280      *
281      * Most classes only cause a few hundred bytes to be allocated, but
282      * a few will allocate a dozen Kbytes (in one case, 500+K).
283      */
preloadClasses()284     private static void preloadClasses() {
285         final VMRuntime runtime = VMRuntime.getRuntime();
286 
287         InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream(
288                 PRELOADED_CLASSES);
289         if (is == null) {
290             Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
291         } else {
292             Log.i(TAG, "Preloading classes...");
293             long startTime = SystemClock.uptimeMillis();
294 
295             // Drop root perms while running static initializers.
296             setEffectiveGroup(UNPRIVILEGED_GID);
297             setEffectiveUser(UNPRIVILEGED_UID);
298 
299             // Alter the target heap utilization.  With explicit GCs this
300             // is not likely to have any effect.
301             float defaultUtilization = runtime.getTargetHeapUtilization();
302             runtime.setTargetHeapUtilization(0.8f);
303 
304             // Start with a clean slate.
305             System.gc();
306             runtime.runFinalizationSync();
307             Debug.startAllocCounting();
308 
309             try {
310                 BufferedReader br
311                     = new BufferedReader(new InputStreamReader(is), 256);
312 
313                 int count = 0;
314                 String line;
315                 while ((line = br.readLine()) != null) {
316                     // Skip comments and blank lines.
317                     line = line.trim();
318                     if (line.startsWith("#") || line.equals("")) {
319                         continue;
320                     }
321 
322                     try {
323                         if (false) {
324                             Log.v(TAG, "Preloading " + line + "...");
325                         }
326                         Class.forName(line);
327                         if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
328                             if (false) {
329                                 Log.v(TAG,
330                                     " GC at " + Debug.getGlobalAllocSize());
331                             }
332                             System.gc();
333                             runtime.runFinalizationSync();
334                             Debug.resetGlobalAllocSize();
335                         }
336                         count++;
337                     } catch (ClassNotFoundException e) {
338                         Log.w(TAG, "Class not found for preloading: " + line);
339                     } catch (UnsatisfiedLinkError e) {
340                         Log.w(TAG, "Problem preloading " + line + ": " + e);
341                     } catch (Throwable t) {
342                         Log.e(TAG, "Error preloading " + line + ".", t);
343                         if (t instanceof Error) {
344                             throw (Error) t;
345                         }
346                         if (t instanceof RuntimeException) {
347                             throw (RuntimeException) t;
348                         }
349                         throw new RuntimeException(t);
350                     }
351                 }
352 
353                 Log.i(TAG, "...preloaded " + count + " classes in "
354                         + (SystemClock.uptimeMillis()-startTime) + "ms.");
355             } catch (IOException e) {
356                 Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
357             } finally {
358                 IoUtils.closeQuietly(is);
359                 // Restore default.
360                 runtime.setTargetHeapUtilization(defaultUtilization);
361 
362                 // Fill in dex caches with classes, fields, and methods brought in by preloading.
363                 runtime.preloadDexCaches();
364 
365                 Debug.stopAllocCounting();
366 
367                 // Bring back root. We'll need it later.
368                 setEffectiveUser(ROOT_UID);
369                 setEffectiveGroup(ROOT_GID);
370             }
371         }
372     }
373 
374     /**
375      * Load in commonly used resources, so they can be shared across
376      * processes.
377      *
378      * These tend to be a few Kbytes, but are frequently in the 20-40K
379      * range, and occasionally even larger.
380      */
preloadResources()381     private static void preloadResources() {
382         final VMRuntime runtime = VMRuntime.getRuntime();
383 
384         Debug.startAllocCounting();
385         try {
386             System.gc();
387             runtime.runFinalizationSync();
388             mResources = Resources.getSystem();
389             mResources.startPreloading();
390             if (PRELOAD_RESOURCES) {
391                 Log.i(TAG, "Preloading resources...");
392 
393                 long startTime = SystemClock.uptimeMillis();
394                 TypedArray ar = mResources.obtainTypedArray(
395                         com.android.internal.R.array.preloaded_drawables);
396                 int N = preloadDrawables(runtime, ar);
397                 ar.recycle();
398                 Log.i(TAG, "...preloaded " + N + " resources in "
399                         + (SystemClock.uptimeMillis()-startTime) + "ms.");
400 
401                 startTime = SystemClock.uptimeMillis();
402                 ar = mResources.obtainTypedArray(
403                         com.android.internal.R.array.preloaded_color_state_lists);
404                 N = preloadColorStateLists(runtime, ar);
405                 ar.recycle();
406                 Log.i(TAG, "...preloaded " + N + " resources in "
407                         + (SystemClock.uptimeMillis()-startTime) + "ms.");
408             }
409             mResources.finishPreloading();
410         } catch (RuntimeException e) {
411             Log.w(TAG, "Failure preloading resources", e);
412         } finally {
413             Debug.stopAllocCounting();
414         }
415     }
416 
preloadColorStateLists(VMRuntime runtime, TypedArray ar)417     private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
418         int N = ar.length();
419         for (int i=0; i<N; i++) {
420             if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
421                 if (false) {
422                     Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
423                 }
424                 System.gc();
425                 runtime.runFinalizationSync();
426                 Debug.resetGlobalAllocSize();
427             }
428             int id = ar.getResourceId(i, 0);
429             if (false) {
430                 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
431             }
432             if (id != 0) {
433                 if (mResources.getColorStateList(id) == null) {
434                     throw new IllegalArgumentException(
435                             "Unable to find preloaded color resource #0x"
436                             + Integer.toHexString(id)
437                             + " (" + ar.getString(i) + ")");
438                 }
439             }
440         }
441         return N;
442     }
443 
444 
preloadDrawables(VMRuntime runtime, TypedArray ar)445     private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
446         int N = ar.length();
447         for (int i=0; i<N; i++) {
448             if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
449                 if (false) {
450                     Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
451                 }
452                 System.gc();
453                 runtime.runFinalizationSync();
454                 Debug.resetGlobalAllocSize();
455             }
456             int id = ar.getResourceId(i, 0);
457             if (false) {
458                 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
459             }
460             if (id != 0) {
461                 if (mResources.getDrawable(id, null) == null) {
462                     throw new IllegalArgumentException(
463                             "Unable to find preloaded drawable resource #0x"
464                             + Integer.toHexString(id)
465                             + " (" + ar.getString(i) + ")");
466                 }
467             }
468         }
469         return N;
470     }
471 
472     /**
473      * Runs several special GCs to try to clean up a few generations of
474      * softly- and final-reachable objects, along with any other garbage.
475      * This is only useful just before a fork().
476      */
gc()477     /*package*/ static void gc() {
478         final VMRuntime runtime = VMRuntime.getRuntime();
479 
480         /* runFinalizationSync() lets finalizers be called in Zygote,
481          * which doesn't have a HeapWorker thread.
482          */
483         System.gc();
484         runtime.runFinalizationSync();
485         System.gc();
486         runtime.runFinalizationSync();
487         System.gc();
488         runtime.runFinalizationSync();
489     }
490 
491     /**
492      * Finish remaining work for the newly forked system server process.
493      */
handleSystemServerProcess( ZygoteConnection.Arguments parsedArgs)494     private static void handleSystemServerProcess(
495             ZygoteConnection.Arguments parsedArgs)
496             throws ZygoteInit.MethodAndArgsCaller {
497 
498         closeServerSocket();
499 
500         // set umask to 0077 so new files and directories will default to owner-only permissions.
501         Os.umask(S_IRWXG | S_IRWXO);
502 
503         if (parsedArgs.niceName != null) {
504             Process.setArgV0(parsedArgs.niceName);
505         }
506 
507         final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
508         if (systemServerClasspath != null) {
509             performSystemServerDexOpt(systemServerClasspath);
510         }
511 
512         if (parsedArgs.invokeWith != null) {
513             String[] args = parsedArgs.remainingArgs;
514             // If we have a non-null system server class path, we'll have to duplicate the
515             // existing arguments and append the classpath to it. ART will handle the classpath
516             // correctly when we exec a new process.
517             if (systemServerClasspath != null) {
518                 String[] amendedArgs = new String[args.length + 2];
519                 amendedArgs[0] = "-cp";
520                 amendedArgs[1] = systemServerClasspath;
521                 System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
522             }
523 
524             WrapperInit.execApplication(parsedArgs.invokeWith,
525                     parsedArgs.niceName, parsedArgs.targetSdkVersion,
526                     null, args);
527         } else {
528             ClassLoader cl = null;
529             if (systemServerClasspath != null) {
530                 cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
531                 Thread.currentThread().setContextClassLoader(cl);
532             }
533 
534             /*
535              * Pass the remaining arguments to SystemServer.
536              */
537             RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
538         }
539 
540         /* should never reach here */
541     }
542 
543     /**
544      * Performs dex-opt on the elements of {@code classPath}, if needed. We
545      * choose the instruction set of the current runtime.
546      */
performSystemServerDexOpt(String classPath)547     private static void performSystemServerDexOpt(String classPath) {
548         final String[] classPathElements = classPath.split(":");
549         final InstallerConnection installer = new InstallerConnection();
550         final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
551 
552         try {
553             for (String classPathElement : classPathElements) {
554                 final byte dexopt = DexFile.isDexOptNeededInternal(classPathElement, "*", instructionSet,
555                         false /* defer */);
556                 if (dexopt == DexFile.DEXOPT_NEEDED) {
557                     installer.dexopt(classPathElement, Process.SYSTEM_UID, false, instructionSet);
558                 } else if (dexopt == DexFile.PATCHOAT_NEEDED) {
559                     installer.patchoat(classPathElement, Process.SYSTEM_UID, false, instructionSet);
560                 }
561             }
562         } catch (IOException ioe) {
563             throw new RuntimeException("Error starting system_server", ioe);
564         } finally {
565             installer.disconnect();
566         }
567     }
568 
569     /**
570      * Prepare the arguments and fork for the system server process.
571      */
startSystemServer(String abiList, String socketName)572     private static boolean startSystemServer(String abiList, String socketName)
573             throws MethodAndArgsCaller, RuntimeException {
574         long capabilities = posixCapabilitiesAsBits(
575             OsConstants.CAP_BLOCK_SUSPEND,
576             OsConstants.CAP_KILL,
577             OsConstants.CAP_NET_ADMIN,
578             OsConstants.CAP_NET_BIND_SERVICE,
579             OsConstants.CAP_NET_BROADCAST,
580             OsConstants.CAP_NET_RAW,
581             OsConstants.CAP_SYS_MODULE,
582             OsConstants.CAP_SYS_NICE,
583             OsConstants.CAP_SYS_RESOURCE,
584             OsConstants.CAP_SYS_TIME,
585             OsConstants.CAP_SYS_TTY_CONFIG
586         );
587         /* Hardcoded command line to start the system server */
588         String args[] = {
589             "--setuid=1000",
590             "--setgid=1000",
591             "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007",
592             "--capabilities=" + capabilities + "," + capabilities,
593             "--runtime-init",
594             "--nice-name=system_server",
595             "com.android.server.SystemServer",
596         };
597         ZygoteConnection.Arguments parsedArgs = null;
598 
599         int pid;
600 
601         try {
602             parsedArgs = new ZygoteConnection.Arguments(args);
603             ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
604             ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
605 
606             /* Request to fork the system server process */
607             pid = Zygote.forkSystemServer(
608                     parsedArgs.uid, parsedArgs.gid,
609                     parsedArgs.gids,
610                     parsedArgs.debugFlags,
611                     null,
612                     parsedArgs.permittedCapabilities,
613                     parsedArgs.effectiveCapabilities);
614         } catch (IllegalArgumentException ex) {
615             throw new RuntimeException(ex);
616         }
617 
618         /* For child process */
619         if (pid == 0) {
620             if (hasSecondZygote(abiList)) {
621                 waitForSecondaryZygote(socketName);
622             }
623 
624             handleSystemServerProcess(parsedArgs);
625         }
626 
627         return true;
628     }
629 
630     /**
631      * Gets the bit array representation of the provided list of POSIX capabilities.
632      */
posixCapabilitiesAsBits(int... capabilities)633     private static long posixCapabilitiesAsBits(int... capabilities) {
634         long result = 0;
635         for (int capability : capabilities) {
636             if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
637                 throw new IllegalArgumentException(String.valueOf(capability));
638             }
639             result |= (1L << capability);
640         }
641         return result;
642     }
643 
main(String argv[])644     public static void main(String argv[]) {
645         try {
646             // Start profiling the zygote initialization.
647             SamplingProfilerIntegration.start();
648 
649             boolean startSystemServer = false;
650             String socketName = "zygote";
651             String abiList = null;
652             for (int i = 1; i < argv.length; i++) {
653                 if ("start-system-server".equals(argv[i])) {
654                     startSystemServer = true;
655                 } else if (argv[i].startsWith(ABI_LIST_ARG)) {
656                     abiList = argv[i].substring(ABI_LIST_ARG.length());
657                 } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
658                     socketName = argv[i].substring(SOCKET_NAME_ARG.length());
659                 } else {
660                     throw new RuntimeException("Unknown command line argument: " + argv[i]);
661                 }
662             }
663 
664             if (abiList == null) {
665                 throw new RuntimeException("No ABI list supplied.");
666             }
667 
668             registerZygoteSocket(socketName);
669             EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
670                 SystemClock.uptimeMillis());
671             preload();
672             EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
673                 SystemClock.uptimeMillis());
674 
675             // Finish profiling the zygote initialization.
676             SamplingProfilerIntegration.writeZygoteSnapshot();
677 
678             // Do an initial gc to clean up after startup
679             gc();
680 
681             // Disable tracing so that forked processes do not inherit stale tracing tags from
682             // Zygote.
683             Trace.setTracingEnabled(false);
684 
685             if (startSystemServer) {
686                 startSystemServer(abiList, socketName);
687             }
688 
689             Log.i(TAG, "Accepting command socket connections");
690             runSelectLoop(abiList);
691 
692             closeServerSocket();
693         } catch (MethodAndArgsCaller caller) {
694             caller.run();
695         } catch (RuntimeException ex) {
696             Log.e(TAG, "Zygote died with exception", ex);
697             closeServerSocket();
698             throw ex;
699         }
700     }
701 
702     /**
703      * Return {@code true} if this device configuration has another zygote.
704      *
705      * We determine this by comparing the device ABI list with this zygotes
706      * list. If this zygote supports all ABIs this device supports, there won't
707      * be another zygote.
708      */
hasSecondZygote(String abiList)709     private static boolean hasSecondZygote(String abiList) {
710         return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList);
711     }
712 
waitForSecondaryZygote(String socketName)713     private static void waitForSecondaryZygote(String socketName) {
714         String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ?
715                 Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
716         while (true) {
717             try {
718                 final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName);
719                 zs.close();
720                 break;
721             } catch (IOException ioe) {
722                 Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
723             }
724 
725             try {
726                 Thread.sleep(1000);
727             } catch (InterruptedException ie) {
728             }
729         }
730     }
731 
732     /**
733      * Runs the zygote process's select loop. Accepts new connections as
734      * they happen, and reads commands from connections one spawn-request's
735      * worth at a time.
736      *
737      * @throws MethodAndArgsCaller in a child process when a main() should
738      * be executed.
739      */
runSelectLoop(String abiList)740     private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
741         ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
742         ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
743         FileDescriptor[] fdArray = new FileDescriptor[4];
744 
745         fds.add(sServerSocket.getFileDescriptor());
746         peers.add(null);
747 
748         int loopCount = GC_LOOP_COUNT;
749         while (true) {
750             int index;
751 
752             /*
753              * Call gc() before we block in select().
754              * It's work that has to be done anyway, and it's better
755              * to avoid making every child do it.  It will also
756              * madvise() any free memory as a side-effect.
757              *
758              * Don't call it every time, because walking the entire
759              * heap is a lot of overhead to free a few hundred bytes.
760              */
761             if (loopCount <= 0) {
762                 gc();
763                 loopCount = GC_LOOP_COUNT;
764             } else {
765                 loopCount--;
766             }
767 
768 
769             try {
770                 fdArray = fds.toArray(fdArray);
771                 index = selectReadable(fdArray);
772             } catch (IOException ex) {
773                 throw new RuntimeException("Error in select()", ex);
774             }
775 
776             if (index < 0) {
777                 throw new RuntimeException("Error in select()");
778             } else if (index == 0) {
779                 ZygoteConnection newPeer = acceptCommandPeer(abiList);
780                 peers.add(newPeer);
781                 fds.add(newPeer.getFileDescriptor());
782             } else {
783                 boolean done;
784                 done = peers.get(index).runOnce();
785 
786                 if (done) {
787                     peers.remove(index);
788                     fds.remove(index);
789                 }
790             }
791         }
792     }
793 
794     /**
795      * The Linux syscall "setreuid()"
796      * @param ruid real uid
797      * @param euid effective uid
798      * @return 0 on success, non-zero errno on fail
799      */
setreuid(int ruid, int euid)800     static native int setreuid(int ruid, int euid);
801 
802     /**
803      * The Linux syscall "setregid()"
804      * @param rgid real gid
805      * @param egid effective gid
806      * @return 0 on success, non-zero errno on fail
807      */
setregid(int rgid, int egid)808     static native int setregid(int rgid, int egid);
809 
810     /**
811      * Invokes the linux syscall "setpgid"
812      *
813      * @param pid pid to change
814      * @param pgid new process group of pid
815      * @return 0 on success or non-zero errno on fail
816      */
setpgid(int pid, int pgid)817     static native int setpgid(int pid, int pgid);
818 
819     /**
820      * Invokes the linux syscall "getpgid"
821      *
822      * @param pid pid to query
823      * @return pgid of pid in question
824      * @throws IOException on error
825      */
getpgid(int pid)826     static native int getpgid(int pid) throws IOException;
827 
828     /**
829      * Invokes the syscall dup2() to copy the specified descriptors into
830      * stdin, stdout, and stderr. The existing stdio descriptors will be
831      * closed and errors during close will be ignored. The specified
832      * descriptors will also remain open at their original descriptor numbers,
833      * so the caller may want to close the original descriptors.
834      *
835      * @param in new stdin
836      * @param out new stdout
837      * @param err new stderr
838      * @throws IOException
839      */
reopenStdio(FileDescriptor in, FileDescriptor out, FileDescriptor err)840     static native void reopenStdio(FileDescriptor in,
841             FileDescriptor out, FileDescriptor err) throws IOException;
842 
843     /**
844      * Toggles the close-on-exec flag for the specified file descriptor.
845      *
846      * @param fd non-null; file descriptor
847      * @param flag desired close-on-exec flag state
848      * @throws IOException
849      */
setCloseOnExec(FileDescriptor fd, boolean flag)850     static native void setCloseOnExec(FileDescriptor fd, boolean flag)
851             throws IOException;
852 
853     /**
854      * Invokes select() on the provider array of file descriptors (selecting
855      * for readability only). Array elements of null are ignored.
856      *
857      * @param fds non-null; array of readable file descriptors
858      * @return index of descriptor that is now readable or -1 for empty array.
859      * @throws IOException if an error occurs
860      */
selectReadable(FileDescriptor[] fds)861     static native int selectReadable(FileDescriptor[] fds) throws IOException;
862 
863     /**
864      * Creates a file descriptor from an int fd.
865      *
866      * @param fd integer OS file descriptor
867      * @return non-null; FileDescriptor instance
868      * @throws IOException if fd is invalid
869      */
createFileDescriptor(int fd)870     static native FileDescriptor createFileDescriptor(int fd)
871             throws IOException;
872 
873     /**
874      * Class not instantiable.
875      */
ZygoteInit()876     private ZygoteInit() {
877     }
878 
879     /**
880      * Helper exception class which holds a method and arguments and
881      * can call them. This is used as part of a trampoline to get rid of
882      * the initial process setup stack frames.
883      */
884     public static class MethodAndArgsCaller extends Exception
885             implements Runnable {
886         /** method to call */
887         private final Method mMethod;
888 
889         /** argument array */
890         private final String[] mArgs;
891 
MethodAndArgsCaller(Method method, String[] args)892         public MethodAndArgsCaller(Method method, String[] args) {
893             mMethod = method;
894             mArgs = args;
895         }
896 
run()897         public void run() {
898             try {
899                 mMethod.invoke(null, new Object[] { mArgs });
900             } catch (IllegalAccessException ex) {
901                 throw new RuntimeException(ex);
902             } catch (InvocationTargetException ex) {
903                 Throwable cause = ex.getCause();
904                 if (cause instanceof RuntimeException) {
905                     throw (RuntimeException) cause;
906                 } else if (cause instanceof Error) {
907                     throw (Error) cause;
908                 }
909                 throw new RuntimeException(ex);
910             }
911         }
912     }
913 }
914