• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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