/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.os;

import static android.system.OsConstants.POLLIN;

import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.system.Os;
import android.system.ErrnoException;
import android.system.StructPollfd;
import android.util.Log;

import android.util.Slog;
import java.io.IOException;
import java.io.FileDescriptor;
import java.util.ArrayList;

Server socket class for zygote processes. Provides functions to wait for commands on a UNIX domain socket, and fork off child processes that inherit the initial state of the VM.% Please see Arguments for documentation on the client protocol.
/** * Server socket class for zygote processes. * * Provides functions to wait for commands on a UNIX domain socket, and fork * off child processes that inherit the initial state of the VM.% * * Please see {@link ZygoteConnection.Arguments} for documentation on the * client protocol. */
class ZygoteServer { public static final String TAG = "ZygoteServer"; private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
Listening socket that accepts new server connections.
/** * Listening socket that accepts new server connections. */
private LocalServerSocket mServerSocket;
Whether or not mServerSocket's underlying FD should be closed directly. If mServerSocket is created with an existing FD, closing the socket does not close the FD and it must be closed explicitly. If the socket is created with a name instead, then closing the socket will close the underlying FD and it should not be double-closed.
/** * Whether or not mServerSocket's underlying FD should be closed directly. * If mServerSocket is created with an existing FD, closing the socket does * not close the FD and it must be closed explicitly. If the socket is created * with a name instead, then closing the socket will close the underlying FD * and it should not be double-closed. */
private boolean mCloseSocketFd;
Set by the child process, immediately after a call to Zygote.forkAndSpecialize.
/** * Set by the child process, immediately after a call to {@code Zygote.forkAndSpecialize}. */
private boolean mIsForkChild; ZygoteServer() { } void setForkChild() { mIsForkChild = true; }
Registers a server socket for zygote command connections. This locates the server socket file descriptor through an ANDROID_SOCKET_ environment variable.
Throws:
  • RuntimeException – when open fails
/** * Registers a server socket for zygote command connections. This locates the server socket * file descriptor through an ANDROID_SOCKET_ environment variable. * * @throws RuntimeException when open fails */
void registerServerSocketFromEnv(String socketName) { if (mServerSocket == 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 { FileDescriptor fd = new FileDescriptor(); fd.setInt$(fileDesc); mServerSocket = new LocalServerSocket(fd); mCloseSocketFd = true; } catch (IOException ex) { throw new RuntimeException( "Error binding to local socket '" + fileDesc + "'", ex); } } }
Registers a server socket for zygote command connections. This opens the server socket at the specified name in the abstract socket namespace.
/** * Registers a server socket for zygote command connections. This opens the server socket * at the specified name in the abstract socket namespace. */
void registerServerSocketAtAbstractName(String socketName) { if (mServerSocket == null) { try { mServerSocket = new LocalServerSocket(socketName); mCloseSocketFd = false; } catch (IOException ex) { throw new RuntimeException( "Error binding to abstract socket '" + socketName + "'", ex); } } }
Waits for and accepts a single command connection. Throws RuntimeException on failure.
/** * Waits for and accepts a single command connection. Throws * RuntimeException on failure. */
private ZygoteConnection acceptCommandPeer(String abiList) { try { return createNewConnection(mServerSocket.accept(), abiList); } catch (IOException ex) { throw new RuntimeException( "IOException during accept()", ex); } } protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList) throws IOException { return new ZygoteConnection(socket, abiList); }
Close and clean up zygote sockets. Called on shutdown and on the child's exit path.
/** * Close and clean up zygote sockets. Called on shutdown and on the * child's exit path. */
void closeServerSocket() { try { if (mServerSocket != null) { FileDescriptor fd = mServerSocket.getFileDescriptor(); mServerSocket.close(); if (fd != null && mCloseSocketFd) { 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); } mServerSocket = null; }
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 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. */
FileDescriptor getServerSocketFileDescriptor() { return mServerSocket.getFileDescriptor(); }
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.
/** * 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. */
Runnable runSelectLoop(String abiList) { ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>(); ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>(); fds.add(mServerSocket.getFileDescriptor()); peers.add(null); while (true) { StructPollfd[] pollFds = new StructPollfd[fds.size()]; for (int i = 0; i < pollFds.length; ++i) { pollFds[i] = new StructPollfd(); pollFds[i].fd = fds.get(i); pollFds[i].events = (short) POLLIN; } try { Os.poll(pollFds, -1); } catch (ErrnoException ex) { throw new RuntimeException("poll failed", ex); } for (int i = pollFds.length - 1; i >= 0; --i) { if ((pollFds[i].revents & POLLIN) == 0) { continue; } if (i == 0) { ZygoteConnection newPeer = acceptCommandPeer(abiList); peers.add(newPeer); fds.add(newPeer.getFileDesciptor()); } else { try { ZygoteConnection connection = peers.get(i); final Runnable command = connection.processOneCommand(this); if (mIsForkChild) { // We're in the child. We should always have a command to run at this // stage if processOneCommand hasn't called "exec". if (command == null) { throw new IllegalStateException("command == null"); } return command; } else { // We're in the server - we should never have any commands to run. if (command != null) { throw new IllegalStateException("command != null"); } // We don't know whether the remote side of the socket was closed or // not until we attempt to read from it from processOneCommand. This shows up as // a regular POLLIN event in our regular processing loop. if (connection.isClosedByPeer()) { connection.closeSocket(); peers.remove(i); fds.remove(i); } } } catch (Exception e) { if (!mIsForkChild) { // We're in the server so any exception here is one that has taken place // pre-fork while processing commands or reading / writing from the // control socket. Make a loud noise about any such exceptions so that // we know exactly what failed and why. Slog.e(TAG, "Exception executing zygote command: ", e); // Make sure the socket is closed so that the other end knows immediately // that something has gone wrong and doesn't time out waiting for a // response. ZygoteConnection conn = peers.remove(i); conn.closeSocket(); fds.remove(i); } else { // We're in the child so any exception caught here has happened post // fork and before we execute ActivityThread.main (or any other main() // method). Log the details of the exception and bring down the process. Log.e(TAG, "Caught post-fork exception in child process.", e); throw e; } } finally { // Reset the child flag, in the event that the child process is a child- // zygote. The flag will not be consulted this loop pass after the Runnable // is returned. mIsForkChild = false; } } } } } }