| /* |
| * 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 {@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_"; |
| |
| private LocalServerSocket mServerSocket; |
| |
| /** |
| * 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 |
| * |
| * @throws RuntimeException when open fails |
| */ |
| void registerServerSocket(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); |
| } catch (IOException ex) { |
| throw new RuntimeException( |
| "Error binding to local socket '" + fileDesc + "'", ex); |
| } |
| } |
| } |
| |
| /** |
| * 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. |
| */ |
| void closeServerSocket() { |
| try { |
| if (mServerSocket != null) { |
| FileDescriptor fd = mServerSocket.getFileDescriptor(); |
| mServerSocket.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); |
| } |
| |
| 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. |
| */ |
| |
| 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. |
| */ |
| 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; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |