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.POLLIN; 20 21 import android.net.LocalServerSocket; 22 import android.net.LocalSocket; 23 import android.system.Os; 24 import android.system.ErrnoException; 25 import android.system.StructPollfd; 26 import android.util.Log; 27 28 import android.util.Slog; 29 import java.io.IOException; 30 import java.io.FileDescriptor; 31 import java.util.ArrayList; 32 33 /** 34 * Server socket class for zygote processes. 35 * 36 * Provides functions to wait for commands on a UNIX domain socket, and fork 37 * off child processes that inherit the initial state of the VM.% 38 * 39 * Please see {@link ZygoteConnection.Arguments} for documentation on the 40 * client protocol. 41 */ 42 class ZygoteServer { 43 public static final String TAG = "ZygoteServer"; 44 45 private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_"; 46 47 private LocalServerSocket mServerSocket; 48 49 /** 50 * Set by the child process, immediately after a call to {@code Zygote.forkAndSpecialize}. 51 */ 52 private boolean mIsForkChild; 53 ZygoteServer()54 ZygoteServer() { 55 } 56 setForkChild()57 void setForkChild() { 58 mIsForkChild = true; 59 } 60 61 /** 62 * Registers a server socket for zygote command connections 63 * 64 * @throws RuntimeException when open fails 65 */ registerServerSocket(String socketName)66 void registerServerSocket(String socketName) { 67 if (mServerSocket == null) { 68 int fileDesc; 69 final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName; 70 try { 71 String env = System.getenv(fullSocketName); 72 fileDesc = Integer.parseInt(env); 73 } catch (RuntimeException ex) { 74 throw new RuntimeException(fullSocketName + " unset or invalid", ex); 75 } 76 77 try { 78 FileDescriptor fd = new FileDescriptor(); 79 fd.setInt$(fileDesc); 80 mServerSocket = new LocalServerSocket(fd); 81 } catch (IOException ex) { 82 throw new RuntimeException( 83 "Error binding to local socket '" + fileDesc + "'", ex); 84 } 85 } 86 } 87 88 /** 89 * Waits for and accepts a single command connection. Throws 90 * RuntimeException on failure. 91 */ acceptCommandPeer(String abiList)92 private ZygoteConnection acceptCommandPeer(String abiList) { 93 try { 94 return createNewConnection(mServerSocket.accept(), abiList); 95 } catch (IOException ex) { 96 throw new RuntimeException( 97 "IOException during accept()", ex); 98 } 99 } 100 createNewConnection(LocalSocket socket, String abiList)101 protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList) 102 throws IOException { 103 return new ZygoteConnection(socket, abiList); 104 } 105 106 /** 107 * Close and clean up zygote sockets. Called on shutdown and on the 108 * child's exit path. 109 */ closeServerSocket()110 void closeServerSocket() { 111 try { 112 if (mServerSocket != null) { 113 FileDescriptor fd = mServerSocket.getFileDescriptor(); 114 mServerSocket.close(); 115 if (fd != null) { 116 Os.close(fd); 117 } 118 } 119 } catch (IOException ex) { 120 Log.e(TAG, "Zygote: error closing sockets", ex); 121 } catch (ErrnoException ex) { 122 Log.e(TAG, "Zygote: error closing descriptor", ex); 123 } 124 125 mServerSocket = null; 126 } 127 128 /** 129 * Return the server socket's underlying file descriptor, so that 130 * ZygoteConnection can pass it to the native code for proper 131 * closure after a child process is forked off. 132 */ 133 getServerSocketFileDescriptor()134 FileDescriptor getServerSocketFileDescriptor() { 135 return mServerSocket.getFileDescriptor(); 136 } 137 138 /** 139 * Runs the zygote process's select loop. Accepts new connections as 140 * they happen, and reads commands from connections one spawn-request's 141 * worth at a time. 142 */ runSelectLoop(String abiList)143 Runnable runSelectLoop(String abiList) { 144 ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>(); 145 ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>(); 146 147 fds.add(mServerSocket.getFileDescriptor()); 148 peers.add(null); 149 150 while (true) { 151 StructPollfd[] pollFds = new StructPollfd[fds.size()]; 152 for (int i = 0; i < pollFds.length; ++i) { 153 pollFds[i] = new StructPollfd(); 154 pollFds[i].fd = fds.get(i); 155 pollFds[i].events = (short) POLLIN; 156 } 157 try { 158 Os.poll(pollFds, -1); 159 } catch (ErrnoException ex) { 160 throw new RuntimeException("poll failed", ex); 161 } 162 for (int i = pollFds.length - 1; i >= 0; --i) { 163 if ((pollFds[i].revents & POLLIN) == 0) { 164 continue; 165 } 166 167 if (i == 0) { 168 ZygoteConnection newPeer = acceptCommandPeer(abiList); 169 peers.add(newPeer); 170 fds.add(newPeer.getFileDesciptor()); 171 } else { 172 try { 173 ZygoteConnection connection = peers.get(i); 174 final Runnable command = connection.processOneCommand(this); 175 176 if (mIsForkChild) { 177 // We're in the child. We should always have a command to run at this 178 // stage if processOneCommand hasn't called "exec". 179 if (command == null) { 180 throw new IllegalStateException("command == null"); 181 } 182 183 return command; 184 } else { 185 // We're in the server - we should never have any commands to run. 186 if (command != null) { 187 throw new IllegalStateException("command != null"); 188 } 189 190 // We don't know whether the remote side of the socket was closed or 191 // not until we attempt to read from it from processOneCommand. This shows up as 192 // a regular POLLIN event in our regular processing loop. 193 if (connection.isClosedByPeer()) { 194 connection.closeSocket(); 195 peers.remove(i); 196 fds.remove(i); 197 } 198 } 199 } catch (Exception e) { 200 if (!mIsForkChild) { 201 // We're in the server so any exception here is one that has taken place 202 // pre-fork while processing commands or reading / writing from the 203 // control socket. Make a loud noise about any such exceptions so that 204 // we know exactly what failed and why. 205 206 Slog.e(TAG, "Exception executing zygote command: ", e); 207 208 // Make sure the socket is closed so that the other end knows immediately 209 // that something has gone wrong and doesn't time out waiting for a 210 // response. 211 ZygoteConnection conn = peers.remove(i); 212 conn.closeSocket(); 213 214 fds.remove(i); 215 } else { 216 // We're in the child so any exception caught here has happened post 217 // fork and before we execute ActivityThread.main (or any other main() 218 // method). Log the details of the exception and bring down the process. 219 Log.e(TAG, "Caught post-fork exception in child process.", e); 220 throw e; 221 } 222 } 223 } 224 } 225 } 226 } 227 } 228