ZygoteServer.java revision ded209843616a98e6f97db0d1784f6d630cbd5e9
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
17package com.android.internal.os;
18
19import static android.system.OsConstants.POLLIN;
20
21import android.net.LocalServerSocket;
22import android.net.LocalSocket;
23import android.system.Os;
24import android.system.ErrnoException;
25import android.system.StructPollfd;
26import android.util.Log;
27
28import java.io.IOException;
29import java.io.FileDescriptor;
30import 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 */
41class 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
48    ZygoteServer() {
49    }
50
51    /**
52     * Registers a server socket for zygote command connections
53     *
54     * @throws RuntimeException when open fails
55     */
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     */
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
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     */
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
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     */
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