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