• 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.os.SystemClock;
24 import android.os.Trace;
25 import android.system.ErrnoException;
26 import android.system.Os;
27 import android.system.StructPollfd;
28 import android.util.Log;
29 import android.util.Slog;
30 
31 import dalvik.system.ZygoteHooks;
32 
33 import java.io.ByteArrayInputStream;
34 import java.io.DataInputStream;
35 import java.io.FileDescriptor;
36 import java.io.IOException;
37 import java.util.ArrayList;
38 
39 /**
40  * Server socket class for zygote processes.
41  *
42  * Provides functions to wait for commands on a UNIX domain socket, and fork
43  * off child processes that inherit the initial state of the VM.%
44  *
45  * Please see {@link ZygoteArguments} for documentation on the
46  * client protocol.
47  */
48 class ZygoteServer {
49     // TODO (chriswailes): Change this so it is set with Zygote or ZygoteSecondary as appropriate
50     public static final String TAG = "ZygoteServer";
51 
52     /**
53      * The maximim value that will be accepted from the USAP_POOL_SIZE_MAX device property.
54      * is a mirror of USAP_POOL_MAX_LIMIT found in com_android_internal_os_Zygote.cpp.
55      */
56     private static final int USAP_POOL_SIZE_MAX_LIMIT = 100;
57 
58     /**
59      * The minimum value that will be accepted from the USAP_POOL_SIZE_MIN device property.
60      */
61     private static final int USAP_POOL_SIZE_MIN_LIMIT = 1;
62 
63     /** The default value used for the USAP_POOL_SIZE_MAX device property */
64     private static final String USAP_POOL_SIZE_MAX_DEFAULT = "10";
65 
66     /** The default value used for the USAP_POOL_SIZE_MIN device property */
67     private static final String USAP_POOL_SIZE_MIN_DEFAULT = "1";
68 
69     /**
70      * Indicates if this Zygote server can support a unspecialized app process pool.  Currently this
71      * should only be true for the primary and secondary Zygotes, and not the App Zygotes or the
72      * WebView Zygote.
73      *
74      * TODO (chriswailes): Make this an explicit argument to the constructor
75      */
76 
77     private final boolean mUsapPoolSupported;
78 
79     /**
80      * If the unspecialized app process pool should be created and used to start applications.
81      *
82      * Setting this value to false will disable the creation, maintenance, and use of the USAP
83      * pool.  When the USAP pool is disabled the application lifecycle will be identical to
84      * previous versions of Android.
85      */
86     private boolean mUsapPoolEnabled = false;
87 
88     /**
89      * Listening socket that accepts new server connections.
90      */
91     private LocalServerSocket mZygoteSocket;
92 
93     /**
94      * The name of the unspecialized app process pool socket to use if the USAP pool is enabled.
95      */
96     private LocalServerSocket mUsapPoolSocket;
97 
98     /**
99      * File descriptor used for communication between the signal handler and the ZygoteServer poll
100      * loop.
101      * */
102     private FileDescriptor mUsapPoolEventFD;
103 
104     /**
105      * Whether or not mZygoteSocket's underlying FD should be closed directly.
106      * If mZygoteSocket is created with an existing FD, closing the socket does
107      * not close the FD and it must be closed explicitly. If the socket is created
108      * with a name instead, then closing the socket will close the underlying FD
109      * and it should not be double-closed.
110      */
111     private boolean mCloseSocketFd;
112 
113     /**
114      * Set by the child process, immediately after a call to {@code Zygote.forkAndSpecialize}.
115      */
116     private boolean mIsForkChild;
117 
118     /**
119      * The runtime-adjustable maximum USAP pool size.
120      */
121     private int mUsapPoolSizeMax = 0;
122 
123     /**
124      * The runtime-adjustable minimum USAP pool size.
125      */
126     private int mUsapPoolSizeMin = 0;
127 
128     /**
129      * The runtime-adjustable value used to determine when to re-fill the USAP pool.  The pool will
130      * be re-filled when (mUsapPoolMax - gUsapPoolCount) >= sUsapPoolRefillThreshold.
131      */
132     private int mUsapPoolRefillThreshold = 0;
133 
ZygoteServer()134     ZygoteServer() {
135         mUsapPoolEventFD = null;
136         mZygoteSocket = null;
137         mUsapPoolSocket = null;
138 
139         mUsapPoolSupported = false;
140     }
141 
142     /**
143      * Initialize the Zygote server with the Zygote server socket, USAP pool server socket, and USAP
144      * pool event FD.
145      *
146      * @param isPrimaryZygote  If this is the primary Zygote or not.
147      */
ZygoteServer(boolean isPrimaryZygote)148     ZygoteServer(boolean isPrimaryZygote) {
149         mUsapPoolEventFD = Zygote.getUsapPoolEventFD();
150 
151         if (isPrimaryZygote) {
152             mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
153             mUsapPoolSocket =
154                     Zygote.createManagedSocketFromInitSocket(
155                             Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
156         } else {
157             mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);
158             mUsapPoolSocket =
159                     Zygote.createManagedSocketFromInitSocket(
160                             Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
161         }
162 
163         fetchUsapPoolPolicyProps();
164 
165         mUsapPoolSupported = true;
166     }
167 
setForkChild()168     void setForkChild() {
169         mIsForkChild = true;
170     }
171 
isUsapPoolEnabled()172     public boolean isUsapPoolEnabled() {
173         return mUsapPoolEnabled;
174     }
175 
176     /**
177      * Registers a server socket for zygote command connections. This opens the server socket
178      * at the specified name in the abstract socket namespace.
179      */
registerServerSocketAtAbstractName(String socketName)180     void registerServerSocketAtAbstractName(String socketName) {
181         if (mZygoteSocket == null) {
182             try {
183                 mZygoteSocket = new LocalServerSocket(socketName);
184                 mCloseSocketFd = false;
185             } catch (IOException ex) {
186                 throw new RuntimeException(
187                         "Error binding to abstract socket '" + socketName + "'", ex);
188             }
189         }
190     }
191 
192     /**
193      * Waits for and accepts a single command connection. Throws
194      * RuntimeException on failure.
195      */
acceptCommandPeer(String abiList)196     private ZygoteConnection acceptCommandPeer(String abiList) {
197         try {
198             return createNewConnection(mZygoteSocket.accept(), abiList);
199         } catch (IOException ex) {
200             throw new RuntimeException(
201                     "IOException during accept()", ex);
202         }
203     }
204 
createNewConnection(LocalSocket socket, String abiList)205     protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
206             throws IOException {
207         return new ZygoteConnection(socket, abiList);
208     }
209 
210     /**
211      * Close and clean up zygote sockets. Called on shutdown and on the
212      * child's exit path.
213      */
closeServerSocket()214     void closeServerSocket() {
215         try {
216             if (mZygoteSocket != null) {
217                 FileDescriptor fd = mZygoteSocket.getFileDescriptor();
218                 mZygoteSocket.close();
219                 if (fd != null && mCloseSocketFd) {
220                     Os.close(fd);
221                 }
222             }
223         } catch (IOException ex) {
224             Log.e(TAG, "Zygote:  error closing sockets", ex);
225         } catch (ErrnoException ex) {
226             Log.e(TAG, "Zygote:  error closing descriptor", ex);
227         }
228 
229         mZygoteSocket = null;
230     }
231 
232     /**
233      * Return the server socket's underlying file descriptor, so that
234      * ZygoteConnection can pass it to the native code for proper
235      * closure after a child process is forked off.
236      */
237 
getZygoteSocketFileDescriptor()238     FileDescriptor getZygoteSocketFileDescriptor() {
239         return mZygoteSocket.getFileDescriptor();
240     }
241 
fetchUsapPoolPolicyProps()242     private void fetchUsapPoolPolicyProps() {
243         if (mUsapPoolSupported) {
244             final String usapPoolSizeMaxPropString = Zygote.getConfigurationProperty(
245                     ZygoteConfig.USAP_POOL_SIZE_MAX, USAP_POOL_SIZE_MAX_DEFAULT);
246 
247             if (!usapPoolSizeMaxPropString.isEmpty()) {
248                 mUsapPoolSizeMax = Integer.min(Integer.parseInt(
249                         usapPoolSizeMaxPropString), USAP_POOL_SIZE_MAX_LIMIT);
250             }
251 
252             final String usapPoolSizeMinPropString = Zygote.getConfigurationProperty(
253                     ZygoteConfig.USAP_POOL_SIZE_MIN, USAP_POOL_SIZE_MIN_DEFAULT);
254 
255             if (!usapPoolSizeMinPropString.isEmpty()) {
256                 mUsapPoolSizeMin = Integer.max(
257                         Integer.parseInt(usapPoolSizeMinPropString), USAP_POOL_SIZE_MIN_LIMIT);
258             }
259 
260             final String usapPoolRefillThresholdPropString = Zygote.getConfigurationProperty(
261                     ZygoteConfig.USAP_POOL_REFILL_THRESHOLD,
262                     Integer.toString(mUsapPoolSizeMax / 2));
263 
264             if (!usapPoolRefillThresholdPropString.isEmpty()) {
265                 mUsapPoolRefillThreshold = Integer.min(
266                         Integer.parseInt(usapPoolRefillThresholdPropString),
267                         mUsapPoolSizeMax);
268             }
269 
270             // Sanity check
271             if (mUsapPoolSizeMin >= mUsapPoolSizeMax) {
272                 Log.w(TAG, "The max size of the USAP pool must be greater than the minimum size."
273                         + "  Restoring default values.");
274 
275                 mUsapPoolSizeMax = Integer.parseInt(USAP_POOL_SIZE_MAX_DEFAULT);
276                 mUsapPoolSizeMin = Integer.parseInt(USAP_POOL_SIZE_MIN_DEFAULT);
277                 mUsapPoolRefillThreshold = mUsapPoolSizeMax / 2;
278             }
279         }
280     }
281 
282     private boolean mIsFirstPropertyCheck = true;
283     private long mLastPropCheckTimestamp = 0;
284 
fetchUsapPoolPolicyPropsWithMinInterval()285     private void fetchUsapPoolPolicyPropsWithMinInterval() {
286         final long currentTimestamp = SystemClock.elapsedRealtime();
287 
288         if (mIsFirstPropertyCheck
289                 || (currentTimestamp - mLastPropCheckTimestamp >= Zygote.PROPERTY_CHECK_INTERVAL)) {
290             mIsFirstPropertyCheck = false;
291             mLastPropCheckTimestamp = currentTimestamp;
292             fetchUsapPoolPolicyProps();
293         }
294     }
295 
296     /**
297      * Checks to see if the current policy says that pool should be refilled, and spawns new USAPs
298      * if necessary.
299      *
300      * @param sessionSocketRawFDs  Anonymous session sockets that are currently open
301      * @return In the Zygote process this function will always return null; in unspecialized app
302      *         processes this function will return a Runnable object representing the new
303      *         application that is passed up from usapMain.
304      */
305 
fillUsapPool(int[] sessionSocketRawFDs)306     Runnable fillUsapPool(int[] sessionSocketRawFDs) {
307         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillUsapPool");
308 
309         // Ensure that the pool properties have been fetched.
310         fetchUsapPoolPolicyPropsWithMinInterval();
311 
312         int usapPoolCount = Zygote.getUsapPoolCount();
313         int numUsapsToSpawn = mUsapPoolSizeMax - usapPoolCount;
314 
315         if (usapPoolCount < mUsapPoolSizeMin
316                 || numUsapsToSpawn >= mUsapPoolRefillThreshold) {
317 
318             // Disable some VM functionality and reset some system values
319             // before forking.
320             ZygoteHooks.preFork();
321             Zygote.resetNicePriority();
322 
323             while (usapPoolCount++ < mUsapPoolSizeMax) {
324                 Runnable caller = Zygote.forkUsap(mUsapPoolSocket, sessionSocketRawFDs);
325 
326                 if (caller != null) {
327                     return caller;
328                 }
329             }
330 
331             // Re-enable runtime services for the Zygote.  Services for unspecialized app process
332             // are re-enabled in specializeAppProcess.
333             ZygoteHooks.postForkCommon();
334 
335             Log.i("zygote",
336                     "Filled the USAP pool. New USAPs: " + numUsapsToSpawn);
337         }
338 
339         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
340 
341         return null;
342     }
343 
344     /**
345      * Empty or fill the USAP pool as dictated by the current and new USAP pool statuses.
346      */
setUsapPoolStatus(boolean newStatus, LocalSocket sessionSocket)347     Runnable setUsapPoolStatus(boolean newStatus, LocalSocket sessionSocket) {
348         if (!mUsapPoolSupported) {
349             Log.w(TAG,
350                     "Attempting to enable a USAP pool for a Zygote that doesn't support it.");
351             return null;
352         } else if (mUsapPoolEnabled == newStatus) {
353             return null;
354         }
355 
356         Log.i(TAG, "USAP Pool status change: " + (newStatus ? "ENABLED" : "DISABLED"));
357 
358         mUsapPoolEnabled = newStatus;
359 
360         if (newStatus) {
361             return fillUsapPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() });
362         } else {
363             Zygote.emptyUsapPool();
364             return null;
365         }
366     }
367 
368     /**
369      * Runs the zygote process's select loop. Accepts new connections as
370      * they happen, and reads commands from connections one spawn-request's
371      * worth at a time.
372      */
runSelectLoop(String abiList)373     Runnable runSelectLoop(String abiList) {
374         ArrayList<FileDescriptor> socketFDs = new ArrayList<FileDescriptor>();
375         ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
376 
377         socketFDs.add(mZygoteSocket.getFileDescriptor());
378         peers.add(null);
379 
380         while (true) {
381             fetchUsapPoolPolicyPropsWithMinInterval();
382 
383             int[] usapPipeFDs = null;
384             StructPollfd[] pollFDs = null;
385 
386             // Allocate enough space for the poll structs, taking into account
387             // the state of the USAP pool for this Zygote (could be a
388             // regular Zygote, a WebView Zygote, or an AppZygote).
389             if (mUsapPoolEnabled) {
390                 usapPipeFDs = Zygote.getUsapPipeFDs();
391                 pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length];
392             } else {
393                 pollFDs = new StructPollfd[socketFDs.size()];
394             }
395 
396             /*
397              * For reasons of correctness the USAP pool pipe and event FDs
398              * must be processed before the session and server sockets.  This
399              * is to ensure that the USAP pool accounting information is
400              * accurate when handling other requests like API blacklist
401              * exemptions.
402              */
403 
404             int pollIndex = 0;
405             for (FileDescriptor socketFD : socketFDs) {
406                 pollFDs[pollIndex] = new StructPollfd();
407                 pollFDs[pollIndex].fd = socketFD;
408                 pollFDs[pollIndex].events = (short) POLLIN;
409                 ++pollIndex;
410             }
411 
412             final int usapPoolEventFDIndex = pollIndex;
413 
414             if (mUsapPoolEnabled) {
415                 pollFDs[pollIndex] = new StructPollfd();
416                 pollFDs[pollIndex].fd = mUsapPoolEventFD;
417                 pollFDs[pollIndex].events = (short) POLLIN;
418                 ++pollIndex;
419 
420                 for (int usapPipeFD : usapPipeFDs) {
421                     FileDescriptor managedFd = new FileDescriptor();
422                     managedFd.setInt$(usapPipeFD);
423 
424                     pollFDs[pollIndex] = new StructPollfd();
425                     pollFDs[pollIndex].fd = managedFd;
426                     pollFDs[pollIndex].events = (short) POLLIN;
427                     ++pollIndex;
428                 }
429             }
430 
431             try {
432                 Os.poll(pollFDs, -1);
433             } catch (ErrnoException ex) {
434                 throw new RuntimeException("poll failed", ex);
435             }
436 
437             boolean usapPoolFDRead = false;
438 
439             while (--pollIndex >= 0) {
440                 if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
441                     continue;
442                 }
443 
444                 if (pollIndex == 0) {
445                     // Zygote server socket
446 
447                     ZygoteConnection newPeer = acceptCommandPeer(abiList);
448                     peers.add(newPeer);
449                     socketFDs.add(newPeer.getFileDescriptor());
450 
451                 } else if (pollIndex < usapPoolEventFDIndex) {
452                     // Session socket accepted from the Zygote server socket
453 
454                     try {
455                         ZygoteConnection connection = peers.get(pollIndex);
456                         final Runnable command = connection.processOneCommand(this);
457 
458                         // TODO (chriswailes): Is this extra check necessary?
459                         if (mIsForkChild) {
460                             // We're in the child. We should always have a command to run at this
461                             // stage if processOneCommand hasn't called "exec".
462                             if (command == null) {
463                                 throw new IllegalStateException("command == null");
464                             }
465 
466                             return command;
467                         } else {
468                             // We're in the server - we should never have any commands to run.
469                             if (command != null) {
470                                 throw new IllegalStateException("command != null");
471                             }
472 
473                             // We don't know whether the remote side of the socket was closed or
474                             // not until we attempt to read from it from processOneCommand. This
475                             // shows up as a regular POLLIN event in our regular processing loop.
476                             if (connection.isClosedByPeer()) {
477                                 connection.closeSocket();
478                                 peers.remove(pollIndex);
479                                 socketFDs.remove(pollIndex);
480                             }
481                         }
482                     } catch (Exception e) {
483                         if (!mIsForkChild) {
484                             // We're in the server so any exception here is one that has taken place
485                             // pre-fork while processing commands or reading / writing from the
486                             // control socket. Make a loud noise about any such exceptions so that
487                             // we know exactly what failed and why.
488 
489                             Slog.e(TAG, "Exception executing zygote command: ", e);
490 
491                             // Make sure the socket is closed so that the other end knows
492                             // immediately that something has gone wrong and doesn't time out
493                             // waiting for a response.
494                             ZygoteConnection conn = peers.remove(pollIndex);
495                             conn.closeSocket();
496 
497                             socketFDs.remove(pollIndex);
498                         } else {
499                             // We're in the child so any exception caught here has happened post
500                             // fork and before we execute ActivityThread.main (or any other main()
501                             // method). Log the details of the exception and bring down the process.
502                             Log.e(TAG, "Caught post-fork exception in child process.", e);
503                             throw e;
504                         }
505                     } finally {
506                         // Reset the child flag, in the event that the child process is a child-
507                         // zygote. The flag will not be consulted this loop pass after the Runnable
508                         // is returned.
509                         mIsForkChild = false;
510                     }
511                 } else {
512                     // Either the USAP pool event FD or a USAP reporting pipe.
513 
514                     // If this is the event FD the payload will be the number of USAPs removed.
515                     // If this is a reporting pipe FD the payload will be the PID of the USAP
516                     // that was just specialized.
517                     long messagePayload = -1;
518 
519                     try {
520                         byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES];
521                         int readBytes = Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);
522 
523                         if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
524                             DataInputStream inputStream =
525                                     new DataInputStream(new ByteArrayInputStream(buffer));
526 
527                             messagePayload = inputStream.readLong();
528                         } else {
529                             Log.e(TAG, "Incomplete read from USAP management FD of size "
530                                     + readBytes);
531                             continue;
532                         }
533                     } catch (Exception ex) {
534                         if (pollIndex == usapPoolEventFDIndex) {
535                             Log.e(TAG, "Failed to read from USAP pool event FD: "
536                                     + ex.getMessage());
537                         } else {
538                             Log.e(TAG, "Failed to read from USAP reporting pipe: "
539                                     + ex.getMessage());
540                         }
541 
542                         continue;
543                     }
544 
545                     if (pollIndex > usapPoolEventFDIndex) {
546                         Zygote.removeUsapTableEntry((int) messagePayload);
547                     }
548 
549                     usapPoolFDRead = true;
550                 }
551             }
552 
553             // Check to see if the USAP pool needs to be refilled.
554             if (usapPoolFDRead) {
555                 int[] sessionSocketRawFDs =
556                         socketFDs.subList(1, socketFDs.size())
557                                 .stream()
558                                 .mapToInt(fd -> fd.getInt$())
559                                 .toArray();
560 
561                 final Runnable command = fillUsapPool(sessionSocketRawFDs);
562 
563                 if (command != null) {
564                     return command;
565                 }
566             }
567         }
568     }
569 }
570