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 java.io.IOException;
29 import java.io.FileDescriptor;
30 import java.util.ArrayList;
31 
32 /**
33  * Server socket class for zygote processes.
34  *
35  * Provides functions to wait for commands on a UNIX domain socket, and fork
36  * off child processes that inherit the initial state of the VM.%
37  *
38  * Please see {@link ZygoteConnection.Arguments} for documentation on the
39  * client protocol.
40  */
41 class ZygoteServer {
42     public static final String TAG = "ZygoteServer";
43 
44     private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
45 
46     private LocalServerSocket mServerSocket;
47 
ZygoteServer()48     ZygoteServer() {
49     }
50 
51     /**
52      * Registers a server socket for zygote command connections
53      *
54      * @throws RuntimeException when open fails
55      */
registerServerSocket(String socketName)56     void registerServerSocket(String socketName) {
57         if (mServerSocket == null) {
58             int fileDesc;
59             final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
60             try {
61                 String env = System.getenv(fullSocketName);
62                 fileDesc = Integer.parseInt(env);
63             } catch (RuntimeException ex) {
64                 throw new RuntimeException(fullSocketName + " unset or invalid", ex);
65             }
66 
67             try {
68                 FileDescriptor fd = new FileDescriptor();
69                 fd.setInt$(fileDesc);
70                 mServerSocket = new LocalServerSocket(fd);
71             } catch (IOException ex) {
72                 throw new RuntimeException(
73                         "Error binding to local socket '" + fileDesc + "'", ex);
74             }
75         }
76     }
77 
78     /**
79      * Waits for and accepts a single command connection. Throws
80      * RuntimeException on failure.
81      */
acceptCommandPeer(String abiList)82     private ZygoteConnection acceptCommandPeer(String abiList) {
83         try {
84             return createNewConnection(mServerSocket.accept(), abiList);
85         } catch (IOException ex) {
86             throw new RuntimeException(
87                     "IOException during accept()", ex);
88         }
89     }
90 
createNewConnection(LocalSocket socket, String abiList)91     protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
92             throws IOException {
93         return new ZygoteConnection(socket, abiList);
94     }
95 
96     /**
97      * Close and clean up zygote sockets. Called on shutdown and on the
98      * child's exit path.
99      */
closeServerSocket()100     void closeServerSocket() {
101         try {
102             if (mServerSocket != null) {
103                 FileDescriptor fd = mServerSocket.getFileDescriptor();
104                 mServerSocket.close();
105                 if (fd != null) {
106                     Os.close(fd);
107                 }
108             }
109         } catch (IOException ex) {
110             Log.e(TAG, "Zygote:  error closing sockets", ex);
111         } catch (ErrnoException ex) {
112             Log.e(TAG, "Zygote:  error closing descriptor", ex);
113         }
114 
115         mServerSocket = null;
116     }
117 
118     /**
119      * Return the server socket's underlying file descriptor, so that
120      * ZygoteConnection can pass it to the native code for proper
121      * closure after a child process is forked off.
122      */
123 
getServerSocketFileDescriptor()124     FileDescriptor getServerSocketFileDescriptor() {
125         return mServerSocket.getFileDescriptor();
126     }
127 
128     /**
129      * Runs the zygote process's select loop. Accepts new connections as
130      * they happen, and reads commands from connections one spawn-request's
131      * worth at a time.
132      *
133      * @throws Zygote.MethodAndArgsCaller in a child process when a main()
134      * should be executed.
135      */
runSelectLoop(String abiList)136     void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
137         ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
138         ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
139 
140         fds.add(mServerSocket.getFileDescriptor());
141         peers.add(null);
142 
143         while (true) {
144             StructPollfd[] pollFds = new StructPollfd[fds.size()];
145             for (int i = 0; i < pollFds.length; ++i) {
146                 pollFds[i] = new StructPollfd();
147                 pollFds[i].fd = fds.get(i);
148                 pollFds[i].events = (short) POLLIN;
149             }
150             try {
151                 Os.poll(pollFds, -1);
152             } catch (ErrnoException ex) {
153                 throw new RuntimeException("poll failed", ex);
154             }
155             for (int i = pollFds.length - 1; i >= 0; --i) {
156                 if ((pollFds[i].revents & POLLIN) == 0) {
157                     continue;
158                 }
159                 if (i == 0) {
160                     ZygoteConnection newPeer = acceptCommandPeer(abiList);
161                     peers.add(newPeer);
162                     fds.add(newPeer.getFileDesciptor());
163                 } else {
164                     boolean done = peers.get(i).runOnce(this);
165                     if (done) {
166                         peers.remove(i);
167                         fds.remove(i);
168                     }
169                 }
170             }
171         }
172     }
173 }
174