ZygoteInitpublic class ZygoteInit extends Object Startup class for the zygote process.
Pre-initializes some classes, and then waits for commands on a UNIX domain
socket. Based on these commands, forks off child processes that inherit
the initial state of the VM.
Please see {@link ZygoteConnection.Arguments} for documentation on the
client protocol. |
Fields Summary |
---|
private static final String | TAG | private static final String | PROPERTY_DISABLE_OPENGL_PRELOADING | private static final String | ANDROID_SOCKET_PREFIX | private static final int | LOG_BOOT_PROGRESS_PRELOAD_START | private static final int | LOG_BOOT_PROGRESS_PRELOAD_END | private static final int | PRELOAD_GC_THRESHOLDwhen preloading, GC after allocating this many bytes | private static final String | ABI_LIST_ARG | private static final String | SOCKET_NAME_ARG | private static android.net.LocalServerSocket | sServerSocket | private static android.content.res.Resources | mResourcesUsed to pre-load resources. We hold a global reference on it so it
never gets destroyed. | static final int | GC_LOOP_COUNTThe number of times that the main Zygote loop
should run before calling gc() again. | private static final String | PRELOADED_CLASSESThe path of a file that contains classes to preload. | private static final boolean | PRELOAD_RESOURCESControls whether we should preload resources during zygote init. | private static final int | UNPRIVILEGED_UID | private static final int | UNPRIVILEGED_GID | private static final int | ROOT_UID | private static final int | ROOT_GID |
Constructors Summary |
---|
private ZygoteInit()Class not instantiable.
|
Methods Summary |
---|
private static ZygoteConnection | acceptCommandPeer(java.lang.String abiList)Waits for and accepts a single command connection. Throws
RuntimeException on failure.
try {
return new ZygoteConnection(sServerSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
| static void | closeServerSocket()Close and clean up zygote sockets. Called on shutdown and on the
child's exit path.
try {
if (sServerSocket != null) {
FileDescriptor fd = sServerSocket.getFileDescriptor();
sServerSocket.close();
if (fd != null) {
Os.close(fd);
}
}
} catch (IOException ex) {
Log.e(TAG, "Zygote: error closing sockets", ex);
} catch (ErrnoException ex) {
Log.e(TAG, "Zygote: error closing descriptor", ex);
}
sServerSocket = null;
| static native java.io.FileDescriptor | createFileDescriptor(int fd)Creates a file descriptor from an int fd.
| static void | gc()Runs several special GCs to try to clean up a few generations of
softly- and final-reachable objects, along with any other garbage.
This is only useful just before a fork().
final VMRuntime runtime = VMRuntime.getRuntime();
/* runFinalizationSync() lets finalizers be called in Zygote,
* which doesn't have a HeapWorker thread.
*/
System.gc();
runtime.runFinalizationSync();
System.gc();
runtime.runFinalizationSync();
System.gc();
runtime.runFinalizationSync();
| static java.io.FileDescriptor | getServerSocketFileDescriptor()Return the server socket's underlying file descriptor, so that
ZygoteConnection can pass it to the native code for proper
closure after a child process is forked off.
return sServerSocket.getFileDescriptor();
| static native int | getpgid(int pid)Invokes the linux syscall "getpgid"
| private static void | handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs)Finish remaining work for the newly forked system server process.
closeServerSocket();
// set umask to 0077 so new files and directories will default to owner-only permissions.
Os.umask(S_IRWXG | S_IRWXO);
if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
}
final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
if (systemServerClasspath != null) {
performSystemServerDexOpt(systemServerClasspath);
}
if (parsedArgs.invokeWith != null) {
String[] args = parsedArgs.remainingArgs;
// If we have a non-null system server class path, we'll have to duplicate the
// existing arguments and append the classpath to it. ART will handle the classpath
// correctly when we exec a new process.
if (systemServerClasspath != null) {
String[] amendedArgs = new String[args.length + 2];
amendedArgs[0] = "-cp";
amendedArgs[1] = systemServerClasspath;
System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
}
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
null, args);
} else {
ClassLoader cl = null;
if (systemServerClasspath != null) {
cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
Thread.currentThread().setContextClassLoader(cl);
}
/*
* Pass the remaining arguments to SystemServer.
*/
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}
/* should never reach here */
| private static boolean | hasSecondZygote(java.lang.String abiList)Return {@code true} if this device configuration has another zygote.
We determine this by comparing the device ABI list with this zygotes
list. If this zygote supports all ABIs this device supports, there won't
be another zygote.
return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList);
| static void | invokeStaticMain(java.lang.ClassLoader loader, java.lang.String className, java.lang.String[] argv)Invokes a static "main(argv[]) method on class "className".
Converts various failing exceptions into RuntimeExceptions, with
the assumption that they will then cause the VM instance to exit.
Class<?> cl;
try {
cl = loader.loadClass(className);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
/*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
throw new ZygoteInit.MethodAndArgsCaller(m, argv);
| public static void | main(java.lang.String[] argv)
try {
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
socketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
if (abiList == null) {
throw new RuntimeException("No ABI list supplied.");
}
registerZygoteSocket(socketName);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preload();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
// Finish profiling the zygote initialization.
SamplingProfilerIntegration.writeZygoteSnapshot();
// Do an initial gc to clean up after startup
gc();
// Disable tracing so that forked processes do not inherit stale tracing tags from
// Zygote.
Trace.setTracingEnabled(false);
if (startSystemServer) {
startSystemServer(abiList, socketName);
}
Log.i(TAG, "Accepting command socket connections");
runSelectLoop(abiList);
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
| private static void | performSystemServerDexOpt(java.lang.String classPath)Performs dex-opt on the elements of {@code classPath}, if needed. We
choose the instruction set of the current runtime.
final String[] classPathElements = classPath.split(":");
final InstallerConnection installer = new InstallerConnection();
final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
try {
for (String classPathElement : classPathElements) {
final byte dexopt = DexFile.isDexOptNeededInternal(classPathElement, "*", instructionSet,
false /* defer */);
if (dexopt == DexFile.DEXOPT_NEEDED) {
installer.dexopt(classPathElement, Process.SYSTEM_UID, false, instructionSet);
} else if (dexopt == DexFile.PATCHOAT_NEEDED) {
installer.patchoat(classPathElement, Process.SYSTEM_UID, false, instructionSet);
}
}
} catch (IOException ioe) {
throw new RuntimeException("Error starting system_server", ioe);
} finally {
installer.disconnect();
}
| private static long | posixCapabilitiesAsBits(int capabilities)Gets the bit array representation of the provided list of POSIX capabilities.
long result = 0;
for (int capability : capabilities) {
if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
throw new IllegalArgumentException(String.valueOf(capability));
}
result |= (1L << capability);
}
return result;
| static void | preload()
Log.d(TAG, "begin preload");
preloadClasses();
preloadResources();
preloadOpenGL();
preloadSharedLibraries();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
WebViewFactory.prepareWebViewInZygote();
Log.d(TAG, "end preload");
| private static void | preloadClasses()Performs Zygote process initialization. Loads and initializes
commonly used classes.
Most classes only cause a few hundred bytes to be allocated, but
a few will allocate a dozen Kbytes (in one case, 500+K).
final VMRuntime runtime = VMRuntime.getRuntime();
InputStream is;
try {
is = new FileInputStream(PRELOADED_CLASSES);
} catch (FileNotFoundException e) {
Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
return;
}
Log.i(TAG, "Preloading classes...");
long startTime = SystemClock.uptimeMillis();
// Drop root perms while running static initializers.
setEffectiveGroup(UNPRIVILEGED_GID);
setEffectiveUser(UNPRIVILEGED_UID);
// Alter the target heap utilization. With explicit GCs this
// is not likely to have any effect.
float defaultUtilization = runtime.getTargetHeapUtilization();
runtime.setTargetHeapUtilization(0.8f);
// Start with a clean slate.
System.gc();
runtime.runFinalizationSync();
Debug.startAllocCounting();
try {
BufferedReader br
= new BufferedReader(new InputStreamReader(is), 256);
int count = 0;
String line;
while ((line = br.readLine()) != null) {
// Skip comments and blank lines.
line = line.trim();
if (line.startsWith("#") || line.equals("")) {
continue;
}
try {
if (false) {
Log.v(TAG, "Preloading " + line + "...");
}
Class.forName(line);
if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
if (false) {
Log.v(TAG,
" GC at " + Debug.getGlobalAllocSize());
}
System.gc();
runtime.runFinalizationSync();
Debug.resetGlobalAllocSize();
}
count++;
} catch (ClassNotFoundException e) {
Log.w(TAG, "Class not found for preloading: " + line);
} catch (UnsatisfiedLinkError e) {
Log.w(TAG, "Problem preloading " + line + ": " + e);
} catch (Throwable t) {
Log.e(TAG, "Error preloading " + line + ".", t);
if (t instanceof Error) {
throw (Error) t;
}
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
}
throw new RuntimeException(t);
}
}
Log.i(TAG, "...preloaded " + count + " classes in "
+ (SystemClock.uptimeMillis()-startTime) + "ms.");
} catch (IOException e) {
Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
} finally {
IoUtils.closeQuietly(is);
// Restore default.
runtime.setTargetHeapUtilization(defaultUtilization);
// Fill in dex caches with classes, fields, and methods brought in by preloading.
runtime.preloadDexCaches();
Debug.stopAllocCounting();
// Bring back root. We'll need it later.
setEffectiveUser(ROOT_UID);
setEffectiveGroup(ROOT_GID);
}
| private static int | preloadColorStateLists(dalvik.system.VMRuntime runtime, android.content.res.TypedArray ar)
int N = ar.length();
for (int i=0; i<N; i++) {
if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
if (false) {
Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
}
System.gc();
runtime.runFinalizationSync();
Debug.resetGlobalAllocSize();
}
int id = ar.getResourceId(i, 0);
if (false) {
Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
}
if (id != 0) {
if (mResources.getColorStateList(id) == null) {
throw new IllegalArgumentException(
"Unable to find preloaded color resource #0x"
+ Integer.toHexString(id)
+ " (" + ar.getString(i) + ")");
}
}
}
return N;
| private static int | preloadDrawables(dalvik.system.VMRuntime runtime, android.content.res.TypedArray ar)
int N = ar.length();
for (int i=0; i<N; i++) {
if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
if (false) {
Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
}
System.gc();
runtime.runFinalizationSync();
Debug.resetGlobalAllocSize();
}
int id = ar.getResourceId(i, 0);
if (false) {
Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
}
if (id != 0) {
if (mResources.getDrawable(id, null) == null) {
throw new IllegalArgumentException(
"Unable to find preloaded drawable resource #0x"
+ Integer.toHexString(id)
+ " (" + ar.getString(i) + ")");
}
}
}
return N;
| private static void | preloadOpenGL()
if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
}
| private static void | preloadResources()Load in commonly used resources, so they can be shared across
processes.
These tend to be a few Kbytes, but are frequently in the 20-40K
range, and occasionally even larger.
final VMRuntime runtime = VMRuntime.getRuntime();
Debug.startAllocCounting();
try {
System.gc();
runtime.runFinalizationSync();
mResources = Resources.getSystem();
mResources.startPreloading();
if (PRELOAD_RESOURCES) {
Log.i(TAG, "Preloading resources...");
long startTime = SystemClock.uptimeMillis();
TypedArray ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_drawables);
int N = preloadDrawables(runtime, ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
+ (SystemClock.uptimeMillis()-startTime) + "ms.");
startTime = SystemClock.uptimeMillis();
ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_color_state_lists);
N = preloadColorStateLists(runtime, ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
+ (SystemClock.uptimeMillis()-startTime) + "ms.");
}
mResources.finishPreloading();
} catch (RuntimeException e) {
Log.w(TAG, "Failure preloading resources", e);
} finally {
Debug.stopAllocCounting();
}
| private static void | preloadSharedLibraries()
Log.i(TAG, "Preloading shared libraries...");
System.loadLibrary("android");
System.loadLibrary("compiler_rt");
System.loadLibrary("jnigraphics");
| private static void | registerZygoteSocket(java.lang.String socketName)Registers a server socket for zygote command connections
if (sServerSocket == null) {
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException(fullSocketName + " unset or invalid", ex);
}
try {
sServerSocket = new LocalServerSocket(
createFileDescriptor(fileDesc));
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
| static native void | reopenStdio(java.io.FileDescriptor in, java.io.FileDescriptor out, java.io.FileDescriptor err)Invokes the syscall dup2() to copy the specified descriptors into
stdin, stdout, and stderr. The existing stdio descriptors will be
closed and errors during close will be ignored. The specified
descriptors will also remain open at their original descriptor numbers,
so the caller may want to close the original descriptors.
| private static void | runSelectLoop(java.lang.String abiList)Runs the zygote process's select loop. Accepts new connections as
they happen, and reads commands from connections one spawn-request's
worth at a time.
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
FileDescriptor[] fdArray = new FileDescriptor[4];
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
int loopCount = GC_LOOP_COUNT;
while (true) {
int index;
/*
* Call gc() before we block in select().
* It's work that has to be done anyway, and it's better
* to avoid making every child do it. It will also
* madvise() any free memory as a side-effect.
*
* Don't call it every time, because walking the entire
* heap is a lot of overhead to free a few hundred bytes.
*/
if (loopCount <= 0) {
gc();
loopCount = GC_LOOP_COUNT;
} else {
loopCount--;
}
try {
fdArray = fds.toArray(fdArray);
index = selectReadable(fdArray);
} catch (IOException ex) {
throw new RuntimeException("Error in select()", ex);
}
if (index < 0) {
throw new RuntimeException("Error in select()");
} else if (index == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDescriptor());
} else {
boolean done;
done = peers.get(index).runOnce();
if (done) {
peers.remove(index);
fds.remove(index);
}
}
}
| static native int | selectReadable(java.io.FileDescriptor[] fds)Invokes select() on the provider array of file descriptors (selecting
for readability only). Array elements of null are ignored.
| static native void | setCloseOnExec(java.io.FileDescriptor fd, boolean flag)Toggles the close-on-exec flag for the specified file descriptor.
| private static void | setEffectiveGroup(int gid)Sets effective group ID.
int errno = setregid(ROOT_GID, gid);
if (errno != 0) {
Log.e(TAG, "setregid() failed. errno: " + errno);
}
| private static void | setEffectiveUser(int uid)Sets effective user ID.
int errno = setreuid(ROOT_UID, uid);
if (errno != 0) {
Log.e(TAG, "setreuid() failed. errno: " + errno);
}
| static native int | setpgid(int pid, int pgid)Invokes the linux syscall "setpgid"
| static native int | setregid(int rgid, int egid)The Linux syscall "setregid()"
| static native int | setreuid(int ruid, int euid)The Linux syscall "setreuid()"
| private static boolean | startSystemServer(java.lang.String abiList, java.lang.String socketName)Prepare the arguments and fork for the system server process.
long capabilities = posixCapabilitiesAsBits(
OsConstants.CAP_BLOCK_SUSPEND,
OsConstants.CAP_KILL,
OsConstants.CAP_NET_ADMIN,
OsConstants.CAP_NET_BIND_SERVICE,
OsConstants.CAP_NET_BROADCAST,
OsConstants.CAP_NET_RAW,
OsConstants.CAP_SYS_MODULE,
OsConstants.CAP_SYS_NICE,
OsConstants.CAP_SYS_RESOURCE,
OsConstants.CAP_SYS_TIME,
OsConstants.CAP_SYS_TTY_CONFIG
);
/* Hardcoded command line to start the system server */
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007",
"--capabilities=" + capabilities + "," + capabilities,
"--runtime-init",
"--nice-name=system_server",
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.debugFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
/* For child process */
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
handleSystemServerProcess(parsedArgs);
}
return true;
| private static void | waitForSecondaryZygote(java.lang.String socketName)
String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ?
Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
while (true) {
try {
final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName);
zs.close();
break;
} catch (IOException ioe) {
Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
}
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
}
|
|