2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.internal.os;
19 import static android.system.OsConstants.POLLIN;
20 import static android.system.OsConstants.S_IRWXG;
21 import static android.system.OsConstants.S_IRWXO;
23 import android.content.res.Resources;
24 import android.content.res.TypedArray;
25 import android.net.LocalServerSocket;
26 import android.opengl.EGL14;
27 import android.os.Process;
28 import android.os.SystemClock;
29 import android.os.SystemProperties;
30 import android.os.Trace;
31 import android.system.ErrnoException;
32 import android.system.Os;
33 import android.system.OsConstants;
34 import android.system.StructPollfd;
35 import android.text.Hyphenator;
36 import android.util.EventLog;
37 import android.util.Log;
38 import android.webkit.WebViewFactory;
40 import dalvik.system.DexFile;
41 import dalvik.system.PathClassLoader;
42 import dalvik.system.VMRuntime;
44 import libcore.io.IoUtils;
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.util.ArrayList;
58 * Startup class for the zygote process.
60 * Pre-initializes some classes, and then waits for commands on a UNIX domain
61 * socket. Based on these commands, forks off child processes that inherit
62 * the initial state of the VM.
64 * Please see {@link ZygoteConnection.Arguments} for documentation on the
69 public class ZygoteInit {
70 private static final String TAG = "Zygote";
72 private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
74 private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
76 private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
77 private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
79 /** when preloading, GC after allocating this many bytes */
80 private static final int PRELOAD_GC_THRESHOLD = 50000;
82 private static final String ABI_LIST_ARG = "--abi-list=";
84 private static final String SOCKET_NAME_ARG = "--socket-name=";
86 private static LocalServerSocket sServerSocket;
89 * Used to pre-load resources. We hold a global reference on it so it
90 * never gets destroyed.
92 private static Resources mResources;
95 * The path of a file that contains classes to preload.
97 private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
99 /** Controls whether we should preload resources during zygote init. */
100 private static final boolean PRELOAD_RESOURCES = true;
103 * Registers a server socket for zygote command connections
105 * @throws RuntimeException when open fails
107 private static void registerZygoteSocket(String socketName) {
108 if (sServerSocket == null) {
110 final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
112 String env = System.getenv(fullSocketName);
113 fileDesc = Integer.parseInt(env);
114 } catch (RuntimeException ex) {
115 throw new RuntimeException(fullSocketName + " unset or invalid", ex);
119 FileDescriptor fd = new FileDescriptor();
120 fd.setInt$(fileDesc);
121 sServerSocket = new LocalServerSocket(fd);
122 } catch (IOException ex) {
123 throw new RuntimeException(
124 "Error binding to local socket '" + fileDesc + "'", ex);
130 * Waits for and accepts a single command connection. Throws
131 * RuntimeException on failure.
133 private static ZygoteConnection acceptCommandPeer(String abiList) {
135 return new ZygoteConnection(sServerSocket.accept(), abiList);
136 } catch (IOException ex) {
137 throw new RuntimeException(
138 "IOException during accept()", ex);
143 * Close and clean up zygote sockets. Called on shutdown and on the
146 static void closeServerSocket() {
148 if (sServerSocket != null) {
149 FileDescriptor fd = sServerSocket.getFileDescriptor();
150 sServerSocket.close();
155 } catch (IOException ex) {
156 Log.e(TAG, "Zygote: error closing sockets", ex);
157 } catch (ErrnoException ex) {
158 Log.e(TAG, "Zygote: error closing descriptor", ex);
161 sServerSocket = null;
165 * Return the server socket's underlying file descriptor, so that
166 * ZygoteConnection can pass it to the native code for proper
167 * closure after a child process is forked off.
170 static FileDescriptor getServerSocketFileDescriptor() {
171 return sServerSocket.getFileDescriptor();
174 private static final int UNPRIVILEGED_UID = 9999;
175 private static final int UNPRIVILEGED_GID = 9999;
177 private static final int ROOT_UID = 0;
178 private static final int ROOT_GID = 0;
180 static void preload() {
181 Log.d(TAG, "begin preload");
182 Thread classPreload = null;
184 classPreload = new Thread() {
189 classPreload.start();
195 preloadSharedLibraries();
196 preloadTextResources();
197 // Ask the WebViewFactory to do any initialization that must run in the zygote process,
198 // for memory sharing purposes.
199 WebViewFactory.prepareWebViewInZygote();
201 if (classPreload != null)
203 } catch(InterruptedException ex) {
205 Log.d(TAG, "end preload");
208 private static void preloadSharedLibraries() {
209 Log.i(TAG, "Preloading shared libraries...");
210 System.loadLibrary("android");
211 System.loadLibrary("compiler_rt");
212 System.loadLibrary("jnigraphics");
215 private static void preloadOpenGL() {
216 if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
217 EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
221 private static void preloadTextResources() {
226 * Performs Zygote process initialization. Loads and initializes
227 * commonly used classes.
229 * Most classes only cause a few hundred bytes to be allocated, but
230 * a few will allocate a dozen Kbytes (in one case, 500+K).
232 private static void preloadClasses() {
233 final VMRuntime runtime = VMRuntime.getRuntime();
237 is = new FileInputStream(PRELOADED_CLASSES);
238 } catch (FileNotFoundException e) {
239 Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
243 Log.i(TAG, "Preloading classes...");
244 long startTime = SystemClock.uptimeMillis();
246 // Drop root perms while running static initializers.
247 final int reuid = Os.getuid();
248 final int regid = Os.getgid();
250 // We need to drop root perms only if we're already root. In the case of "wrapped"
251 // processes (see WrapperInit), this function is called from an unprivileged uid
253 boolean droppedPriviliges = false;
254 if (reuid == ROOT_UID && regid == ROOT_GID) {
256 Os.setregid(ROOT_GID, UNPRIVILEGED_GID);
257 Os.setreuid(ROOT_UID, UNPRIVILEGED_UID);
258 } catch (ErrnoException ex) {
259 throw new RuntimeException("Failed to drop root", ex);
262 droppedPriviliges = true;
265 // Alter the target heap utilization. With explicit GCs this
266 // is not likely to have any effect.
267 float defaultUtilization = runtime.getTargetHeapUtilization();
268 runtime.setTargetHeapUtilization(0.8f);
272 = new BufferedReader(new InputStreamReader(is), 256);
276 while ((line = br.readLine()) != null) {
277 // Skip comments and blank lines.
279 if (line.startsWith("#") || line.equals("")) {
285 Log.v(TAG, "Preloading " + line + "...");
287 // Load and explicitly initialize the given class. Use
288 // Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups
289 // (to derive the caller's class-loader). Use true to force initialization, and
290 // null for the boot classpath class-loader (could as well cache the
291 // class-loader of this class in a variable).
292 Class.forName(line, true, null);
294 } catch (ClassNotFoundException e) {
295 Log.w(TAG, "Class not found for preloading: " + line);
296 } catch (UnsatisfiedLinkError e) {
297 Log.w(TAG, "Problem preloading " + line + ": " + e);
298 } catch (Throwable t) {
299 Log.e(TAG, "Error preloading " + line + ".", t);
300 if (t instanceof Error) {
303 if (t instanceof RuntimeException) {
304 throw (RuntimeException) t;
306 throw new RuntimeException(t);
310 Log.i(TAG, "...preloaded " + count + " classes in "
311 + (SystemClock.uptimeMillis()-startTime) + "ms.");
312 } catch (IOException e) {
313 Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
315 IoUtils.closeQuietly(is);
317 runtime.setTargetHeapUtilization(defaultUtilization);
319 // Fill in dex caches with classes, fields, and methods brought in by preloading.
320 runtime.preloadDexCaches();
322 // Bring back root. We'll need it later if we're in the zygote.
323 if (droppedPriviliges) {
325 Os.setreuid(ROOT_UID, ROOT_UID);
326 Os.setregid(ROOT_GID, ROOT_GID);
327 } catch (ErrnoException ex) {
328 throw new RuntimeException("Failed to restore root", ex);
335 * Load in commonly used resources, so they can be shared across
338 * These tend to be a few Kbytes, but are frequently in the 20-40K
339 * range, and occasionally even larger.
341 private static void preloadResources() {
342 final VMRuntime runtime = VMRuntime.getRuntime();
345 mResources = Resources.getSystem();
346 mResources.startPreloading();
347 if (PRELOAD_RESOURCES) {
348 Log.i(TAG, "Preloading resources...");
350 long startTime = SystemClock.uptimeMillis();
351 TypedArray ar = mResources.obtainTypedArray(
352 com.android.internal.R.array.preloaded_drawables);
353 int N = preloadDrawables(runtime, ar);
355 Log.i(TAG, "...preloaded " + N + " resources in "
356 + (SystemClock.uptimeMillis()-startTime) + "ms.");
358 startTime = SystemClock.uptimeMillis();
359 ar = mResources.obtainTypedArray(
360 com.android.internal.R.array.preloaded_color_state_lists);
361 N = preloadColorStateLists(runtime, ar);
363 Log.i(TAG, "...preloaded " + N + " resources in "
364 + (SystemClock.uptimeMillis()-startTime) + "ms.");
366 mResources.finishPreloading();
367 } catch (RuntimeException e) {
368 Log.w(TAG, "Failure preloading resources", e);
372 private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
374 for (int i=0; i<N; i++) {
375 int id = ar.getResourceId(i, 0);
377 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
380 if (mResources.getColorStateList(id, null) == null) {
381 throw new IllegalArgumentException(
382 "Unable to find preloaded color resource #0x"
383 + Integer.toHexString(id)
384 + " (" + ar.getString(i) + ")");
392 private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
394 for (int i=0; i<N; i++) {
395 int id = ar.getResourceId(i, 0);
397 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
400 if (mResources.getDrawable(id, null) == null) {
401 throw new IllegalArgumentException(
402 "Unable to find preloaded drawable resource #0x"
403 + Integer.toHexString(id)
404 + " (" + ar.getString(i) + ")");
412 * Runs several special GCs to try to clean up a few generations of
413 * softly- and final-reachable objects, along with any other garbage.
414 * This is only useful just before a fork().
416 /*package*/ static void gcAndFinalize() {
417 final VMRuntime runtime = VMRuntime.getRuntime();
419 /* runFinalizationSync() lets finalizers be called in Zygote,
420 * which doesn't have a HeapWorker thread.
423 runtime.runFinalizationSync();
428 * Finish remaining work for the newly forked system server process.
430 private static void handleSystemServerProcess(
431 ZygoteConnection.Arguments parsedArgs)
432 throws ZygoteInit.MethodAndArgsCaller {
436 // set umask to 0077 so new files and directories will default to owner-only permissions.
437 Os.umask(S_IRWXG | S_IRWXO);
439 if (parsedArgs.niceName != null) {
440 Process.setArgV0(parsedArgs.niceName);
443 final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
444 if (systemServerClasspath != null) {
445 performSystemServerDexOpt(systemServerClasspath);
448 if (parsedArgs.invokeWith != null) {
449 String[] args = parsedArgs.remainingArgs;
450 // If we have a non-null system server class path, we'll have to duplicate the
451 // existing arguments and append the classpath to it. ART will handle the classpath
452 // correctly when we exec a new process.
453 if (systemServerClasspath != null) {
454 String[] amendedArgs = new String[args.length + 2];
455 amendedArgs[0] = "-cp";
456 amendedArgs[1] = systemServerClasspath;
457 System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
460 WrapperInit.execApplication(parsedArgs.invokeWith,
461 parsedArgs.niceName, parsedArgs.targetSdkVersion,
462 VMRuntime.getCurrentInstructionSet(), null, args);
464 ClassLoader cl = null;
465 if (systemServerClasspath != null) {
466 cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
467 Thread.currentThread().setContextClassLoader(cl);
471 * Pass the remaining arguments to SystemServer.
473 RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
476 /* should never reach here */
480 * Performs dex-opt on the elements of {@code classPath}, if needed. We
481 * choose the instruction set of the current runtime.
483 private static void performSystemServerDexOpt(String classPath) {
484 final String[] classPathElements = classPath.split(":");
485 final InstallerConnection installer = new InstallerConnection();
486 installer.waitForConnection();
487 final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
490 for (String classPathElement : classPathElements) {
491 final int dexoptNeeded = DexFile.getDexOptNeeded(
492 classPathElement, "*", instructionSet, false /* defer */);
493 if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
494 installer.dexopt(classPathElement, Process.SYSTEM_UID, false,
495 instructionSet, dexoptNeeded, false /* boot complete */);
498 } catch (IOException ioe) {
499 throw new RuntimeException("Error starting system_server", ioe);
501 installer.disconnect();
506 * Prepare the arguments and fork for the system server process.
508 private static boolean startSystemServer(String abiList, String socketName)
509 throws MethodAndArgsCaller, RuntimeException {
510 long capabilities = posixCapabilitiesAsBits(
511 OsConstants.CAP_BLOCK_SUSPEND,
512 OsConstants.CAP_KILL,
513 OsConstants.CAP_NET_ADMIN,
514 OsConstants.CAP_NET_BIND_SERVICE,
515 OsConstants.CAP_NET_BROADCAST,
516 OsConstants.CAP_NET_RAW,
517 OsConstants.CAP_SYS_MODULE,
518 OsConstants.CAP_SYS_NICE,
519 OsConstants.CAP_SYS_RESOURCE,
520 OsConstants.CAP_SYS_TIME,
521 OsConstants.CAP_SYS_TTY_CONFIG
523 /* Hardcoded command line to start the system server */
527 "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007",
528 "--capabilities=" + capabilities + "," + capabilities,
529 "--nice-name=system_server",
531 "com.android.server.SystemServer",
533 ZygoteConnection.Arguments parsedArgs = null;
538 parsedArgs = new ZygoteConnection.Arguments(args);
539 ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
540 ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
542 /* Request to fork the system server process */
543 pid = Zygote.forkSystemServer(
544 parsedArgs.uid, parsedArgs.gid,
546 parsedArgs.debugFlags,
548 parsedArgs.permittedCapabilities,
549 parsedArgs.effectiveCapabilities);
550 } catch (IllegalArgumentException ex) {
551 throw new RuntimeException(ex);
554 /* For child process */
556 if (hasSecondZygote(abiList)) {
557 waitForSecondaryZygote(socketName);
560 handleSystemServerProcess(parsedArgs);
567 * Gets the bit array representation of the provided list of POSIX capabilities.
569 private static long posixCapabilitiesAsBits(int... capabilities) {
571 for (int capability : capabilities) {
572 if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
573 throw new IllegalArgumentException(String.valueOf(capability));
575 result |= (1L << capability);
580 public static void main(String argv[]) {
582 RuntimeInit.enableDdms();
583 // Start profiling the zygote initialization.
584 SamplingProfilerIntegration.start();
586 boolean startSystemServer = false;
587 String socketName = "zygote";
588 String abiList = null;
589 for (int i = 1; i < argv.length; i++) {
590 if ("start-system-server".equals(argv[i])) {
591 startSystemServer = true;
592 } else if (argv[i].startsWith(ABI_LIST_ARG)) {
593 abiList = argv[i].substring(ABI_LIST_ARG.length());
594 } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
595 socketName = argv[i].substring(SOCKET_NAME_ARG.length());
597 throw new RuntimeException("Unknown command line argument: " + argv[i]);
601 if (abiList == null) {
602 throw new RuntimeException("No ABI list supplied.");
605 registerZygoteSocket(socketName);
606 EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
607 SystemClock.uptimeMillis());
609 EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
610 SystemClock.uptimeMillis());
612 // Finish profiling the zygote initialization.
613 SamplingProfilerIntegration.writeZygoteSnapshot();
615 // Do an initial gc to clean up after startup
618 // Disable tracing so that forked processes do not inherit stale tracing tags from
620 Trace.setTracingEnabled(false);
622 if (startSystemServer) {
623 startSystemServer(abiList, socketName);
626 Log.i(TAG, "Accepting command socket connections");
627 runSelectLoop(abiList);
630 } catch (MethodAndArgsCaller caller) {
632 } catch (RuntimeException ex) {
633 Log.e(TAG, "Zygote died with exception", ex);
640 * Return {@code true} if this device configuration has another zygote.
642 * We determine this by comparing the device ABI list with this zygotes
643 * list. If this zygote supports all ABIs this device supports, there won't
646 private static boolean hasSecondZygote(String abiList) {
647 return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList);
650 private static void waitForSecondaryZygote(String socketName) {
651 String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ?
652 Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
655 final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName);
658 } catch (IOException ioe) {
659 Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
664 } catch (InterruptedException ie) {
670 * Runs the zygote process's select loop. Accepts new connections as
671 * they happen, and reads commands from connections one spawn-request's
674 * @throws MethodAndArgsCaller in a child process when a main() should
677 private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
678 ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
679 ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
681 fds.add(sServerSocket.getFileDescriptor());
685 StructPollfd[] pollFds = new StructPollfd[fds.size()];
686 for (int i = 0; i < pollFds.length; ++i) {
687 pollFds[i] = new StructPollfd();
688 pollFds[i].fd = fds.get(i);
689 pollFds[i].events = (short) POLLIN;
692 Os.poll(pollFds, -1);
693 } catch (ErrnoException ex) {
694 throw new RuntimeException("poll failed", ex);
696 for (int i = pollFds.length - 1; i >= 0; --i) {
697 if ((pollFds[i].revents & POLLIN) == 0) {
701 ZygoteConnection newPeer = acceptCommandPeer(abiList);
703 fds.add(newPeer.getFileDesciptor());
705 boolean done = peers.get(i).runOnce();
716 * Class not instantiable.
718 private ZygoteInit() {
722 * Helper exception class which holds a method and arguments and
723 * can call them. This is used as part of a trampoline to get rid of
724 * the initial process setup stack frames.
726 public static class MethodAndArgsCaller extends Exception
727 implements Runnable {
728 /** method to call */
729 private final Method mMethod;
731 /** argument array */
732 private final String[] mArgs;
734 public MethodAndArgsCaller(Method method, String[] args) {
741 mMethod.invoke(null, new Object[] { mArgs });
742 } catch (IllegalAccessException ex) {
743 throw new RuntimeException(ex);
744 } catch (InvocationTargetException ex) {
745 Throwable cause = ex.getCause();
746 if (cause instanceof RuntimeException) {
747 throw (RuntimeException) cause;
748 } else if (cause instanceof Error) {
751 throw new RuntimeException(ex);