• 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     /** The default value used for the USAP_REFILL_DELAY_MS device property */
70     private static final String USAP_POOL_REFILL_DELAY_MS_DEFAULT = "3000";
71 
72     /** The "not a timestamp" value for the refill delay timestamp mechanism. */
73     private static final int INVALID_TIMESTAMP = -1;
74 
75     /**
76      * Indicates if this Zygote server can support a unspecialized app process pool.  Currently this
77      * should only be true for the primary and secondary Zygotes, and not the App Zygotes or the
78      * WebView Zygote.
79      *
80      * TODO (chriswailes): Make this an explicit argument to the constructor
81      */
82 
83     private final boolean mUsapPoolSupported;
84 
85     /**
86      * If the unspecialized app process pool should be created and used to start applications.
87      *
88      * Setting this value to false will disable the creation, maintenance, and use of the USAP
89      * pool.  When the USAP pool is disabled the application lifecycle will be identical to
90      * previous versions of Android.
91      */
92     private boolean mUsapPoolEnabled = false;
93 
94     /**
95      * Listening socket that accepts new server connections.
96      */
97     private LocalServerSocket mZygoteSocket;
98 
99     /**
100      * The name of the unspecialized app process pool socket to use if the USAP pool is enabled.
101      */
102     private final LocalServerSocket mUsapPoolSocket;
103 
104     /**
105      * File descriptor used for communication between the signal handler and the ZygoteServer poll
106      * loop.
107      * */
108     private final FileDescriptor mUsapPoolEventFD;
109 
110     /**
111      * Whether or not mZygoteSocket's underlying FD should be closed directly.
112      * If mZygoteSocket is created with an existing FD, closing the socket does
113      * not close the FD and it must be closed explicitly. If the socket is created
114      * with a name instead, then closing the socket will close the underlying FD
115      * and it should not be double-closed.
116      */
117     private boolean mCloseSocketFd;
118 
119     /**
120      * Set by the child process, immediately after a call to {@code Zygote.forkAndSpecialize}.
121      */
122     private boolean mIsForkChild;
123 
124     /**
125      * The runtime-adjustable maximum USAP pool size.
126      */
127     private int mUsapPoolSizeMax = 0;
128 
129     /**
130      * The runtime-adjustable minimum USAP pool size.
131      */
132     private int mUsapPoolSizeMin = 0;
133 
134     /**
135      * The runtime-adjustable value used to determine when to re-fill the USAP pool.  The pool will
136      * be re-filled when (mUsapPoolMax - gUsapPoolCount) >= sUsapPoolRefillThreshold.
137      */
138     private int mUsapPoolRefillThreshold = 0;
139 
140     /**
141      * Number of milliseconds to delay before refilling the pool if it hasn't reached its
142      * minimum value.
143      */
144     private int mUsapPoolRefillDelayMs = -1;
145 
146     /**
147      * If and when we should refill the USAP pool.
148      */
149     private UsapPoolRefillAction mUsapPoolRefillAction;
150     private long mUsapPoolRefillTriggerTimestamp;
151 
152     private enum UsapPoolRefillAction {
153         DELAYED,
154         IMMEDIATE,
155         NONE
156     }
157 
ZygoteServer()158     ZygoteServer() {
159         mUsapPoolEventFD = null;
160         mZygoteSocket = null;
161         mUsapPoolSocket = null;
162 
163         mUsapPoolSupported = false;
164     }
165 
166     /**
167      * Initialize the Zygote server with the Zygote server socket, USAP pool server socket, and USAP
168      * pool event FD.
169      *
170      * @param isPrimaryZygote  If this is the primary Zygote or not.
171      */
ZygoteServer(boolean isPrimaryZygote)172     ZygoteServer(boolean isPrimaryZygote) {
173         mUsapPoolEventFD = Zygote.getUsapPoolEventFD();
174 
175         if (isPrimaryZygote) {
176             mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
177             mUsapPoolSocket =
178                     Zygote.createManagedSocketFromInitSocket(
179                             Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
180         } else {
181             mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);
182             mUsapPoolSocket =
183                     Zygote.createManagedSocketFromInitSocket(
184                             Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
185         }
186 
187         mUsapPoolSupported = true;
188         fetchUsapPoolPolicyProps();
189     }
190 
setForkChild()191     void setForkChild() {
192         mIsForkChild = true;
193     }
194 
isUsapPoolEnabled()195     public boolean isUsapPoolEnabled() {
196         return mUsapPoolEnabled;
197     }
198 
199     /**
200      * Registers a server socket for zygote command connections. This opens the server socket
201      * at the specified name in the abstract socket namespace.
202      */
registerServerSocketAtAbstractName(String socketName)203     void registerServerSocketAtAbstractName(String socketName) {
204         if (mZygoteSocket == null) {
205             try {
206                 mZygoteSocket = new LocalServerSocket(socketName);
207                 mCloseSocketFd = false;
208             } catch (IOException ex) {
209                 throw new RuntimeException(
210                         "Error binding to abstract socket '" + socketName + "'", ex);
211             }
212         }
213     }
214 
215     /**
216      * Waits for and accepts a single command connection. Throws
217      * RuntimeException on failure.
218      */
acceptCommandPeer(String abiList)219     private ZygoteConnection acceptCommandPeer(String abiList) {
220         try {
221             return createNewConnection(mZygoteSocket.accept(), abiList);
222         } catch (IOException ex) {
223             throw new RuntimeException(
224                     "IOException during accept()", ex);
225         }
226     }
227 
createNewConnection(LocalSocket socket, String abiList)228     protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
229             throws IOException {
230         return new ZygoteConnection(socket, abiList);
231     }
232 
233     /**
234      * Close and clean up zygote sockets. Called on shutdown and on the
235      * child's exit path.
236      */
closeServerSocket()237     void closeServerSocket() {
238         try {
239             if (mZygoteSocket != null) {
240                 FileDescriptor fd = mZygoteSocket.getFileDescriptor();
241                 mZygoteSocket.close();
242                 if (fd != null && mCloseSocketFd) {
243                     Os.close(fd);
244                 }
245             }
246         } catch (IOException ex) {
247             Log.e(TAG, "Zygote:  error closing sockets", ex);
248         } catch (ErrnoException ex) {
249             Log.e(TAG, "Zygote:  error closing descriptor", ex);
250         }
251 
252         mZygoteSocket = null;
253     }
254 
255     /**
256      * Return the server socket's underlying file descriptor, so that
257      * ZygoteConnection can pass it to the native code for proper
258      * closure after a child process is forked off.
259      */
260 
getZygoteSocketFileDescriptor()261     FileDescriptor getZygoteSocketFileDescriptor() {
262         return mZygoteSocket.getFileDescriptor();
263     }
264 
fetchUsapPoolPolicyProps()265     private void fetchUsapPoolPolicyProps() {
266         if (mUsapPoolSupported) {
267             final String usapPoolSizeMaxPropString = Zygote.getConfigurationProperty(
268                     ZygoteConfig.USAP_POOL_SIZE_MAX, USAP_POOL_SIZE_MAX_DEFAULT);
269 
270             if (!usapPoolSizeMaxPropString.isEmpty()) {
271                 mUsapPoolSizeMax = Integer.min(Integer.parseInt(
272                         usapPoolSizeMaxPropString), USAP_POOL_SIZE_MAX_LIMIT);
273             }
274 
275             final String usapPoolSizeMinPropString = Zygote.getConfigurationProperty(
276                     ZygoteConfig.USAP_POOL_SIZE_MIN, USAP_POOL_SIZE_MIN_DEFAULT);
277 
278             if (!usapPoolSizeMinPropString.isEmpty()) {
279                 mUsapPoolSizeMin = Integer.max(
280                         Integer.parseInt(usapPoolSizeMinPropString), USAP_POOL_SIZE_MIN_LIMIT);
281             }
282 
283             final String usapPoolRefillThresholdPropString = Zygote.getConfigurationProperty(
284                     ZygoteConfig.USAP_POOL_REFILL_THRESHOLD,
285                     Integer.toString(mUsapPoolSizeMax / 2));
286 
287             if (!usapPoolRefillThresholdPropString.isEmpty()) {
288                 mUsapPoolRefillThreshold = Integer.min(
289                         Integer.parseInt(usapPoolRefillThresholdPropString),
290                         mUsapPoolSizeMax);
291             }
292 
293             final String usapPoolRefillDelayMsPropString = Zygote.getConfigurationProperty(
294                     ZygoteConfig.USAP_POOL_REFILL_DELAY_MS, USAP_POOL_REFILL_DELAY_MS_DEFAULT);
295 
296             if (!usapPoolRefillDelayMsPropString.isEmpty()) {
297                 mUsapPoolRefillDelayMs = Integer.parseInt(usapPoolRefillDelayMsPropString);
298             }
299 
300             // Validity check
301             if (mUsapPoolSizeMin >= mUsapPoolSizeMax) {
302                 Log.w(TAG, "The max size of the USAP pool must be greater than the minimum size."
303                         + "  Restoring default values.");
304 
305                 mUsapPoolSizeMax = Integer.parseInt(USAP_POOL_SIZE_MAX_DEFAULT);
306                 mUsapPoolSizeMin = Integer.parseInt(USAP_POOL_SIZE_MIN_DEFAULT);
307                 mUsapPoolRefillThreshold = mUsapPoolSizeMax / 2;
308             }
309         }
310     }
311 
312     private boolean mIsFirstPropertyCheck = true;
313     private long mLastPropCheckTimestamp = 0;
314 
fetchUsapPoolPolicyPropsWithMinInterval()315     private void fetchUsapPoolPolicyPropsWithMinInterval() {
316         final long currentTimestamp = SystemClock.elapsedRealtime();
317 
318         if (mIsFirstPropertyCheck
319                 || (currentTimestamp - mLastPropCheckTimestamp >= Zygote.PROPERTY_CHECK_INTERVAL)) {
320             mIsFirstPropertyCheck = false;
321             mLastPropCheckTimestamp = currentTimestamp;
322             fetchUsapPoolPolicyProps();
323         }
324     }
325 
fetchUsapPoolPolicyPropsIfUnfetched()326     private void fetchUsapPoolPolicyPropsIfUnfetched() {
327         if (mIsFirstPropertyCheck) {
328             mIsFirstPropertyCheck = false;
329             fetchUsapPoolPolicyProps();
330         }
331     }
332 
333     /**
334      * Refill the USAP Pool to the appropriate level, determined by whether this is a priority
335      * refill event or not.
336      *
337      * @param sessionSocketRawFDs  Anonymous session sockets that are currently open
338      * @return In the Zygote process this function will always return null; in unspecialized app
339      *         processes this function will return a Runnable object representing the new
340      *         application that is passed up from childMain (the usap's main wait loop).
341      */
342 
fillUsapPool(int[] sessionSocketRawFDs, boolean isPriorityRefill)343     Runnable fillUsapPool(int[] sessionSocketRawFDs, boolean isPriorityRefill) {
344         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillUsapPool");
345 
346         // Ensure that the pool properties have been fetched.
347         fetchUsapPoolPolicyPropsIfUnfetched();
348 
349         int usapPoolCount = Zygote.getUsapPoolCount();
350         int numUsapsToSpawn;
351 
352         if (isPriorityRefill) {
353             // Refill to min
354             numUsapsToSpawn = mUsapPoolSizeMin - usapPoolCount;
355 
356             Log.i("zygote",
357                     "Priority USAP Pool refill. New USAPs: " + numUsapsToSpawn);
358         } else {
359             // Refill up to max
360             numUsapsToSpawn = mUsapPoolSizeMax - usapPoolCount;
361 
362             Log.i("zygote",
363                     "Delayed USAP Pool refill. New USAPs: " + numUsapsToSpawn);
364         }
365 
366         // Disable some VM functionality and reset some system values
367         // before forking.
368         ZygoteHooks.preFork();
369 
370         while (--numUsapsToSpawn >= 0) {
371             Runnable caller =
372                     Zygote.forkUsap(mUsapPoolSocket, sessionSocketRawFDs, isPriorityRefill);
373 
374             if (caller != null) {
375                 return caller;
376             }
377         }
378 
379         // Re-enable runtime services for the Zygote.  Services for unspecialized app process
380         // are re-enabled in specializeAppProcess.
381         ZygoteHooks.postForkCommon();
382 
383         resetUsapRefillState();
384 
385         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
386 
387         return null;
388     }
389 
390     /**
391      * Empty or fill the USAP pool as dictated by the current and new USAP pool statuses.
392      */
setUsapPoolStatus(boolean newStatus, LocalSocket sessionSocket)393     Runnable setUsapPoolStatus(boolean newStatus, LocalSocket sessionSocket) {
394         if (!mUsapPoolSupported) {
395             Log.w(TAG,
396                     "Attempting to enable a USAP pool for a Zygote that doesn't support it.");
397             return null;
398         } else if (mUsapPoolEnabled == newStatus) {
399             return null;
400         }
401 
402         Log.i(TAG, "USAP Pool status change: " + (newStatus ? "ENABLED" : "DISABLED"));
403 
404         mUsapPoolEnabled = newStatus;
405 
406         if (newStatus) {
407             return fillUsapPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() }, false);
408         } else {
409             Zygote.emptyUsapPool();
410             return null;
411         }
412     }
413 
resetUsapRefillState()414     private void resetUsapRefillState() {
415         mUsapPoolRefillAction = UsapPoolRefillAction.NONE;
416         mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
417     }
418 
419     /**
420      * Runs the zygote process's select loop. Accepts new connections as
421      * they happen, and reads commands from connections one spawn-request's
422      * worth at a time.
423      * @param abiList list of ABIs supported by this zygote.
424      */
runSelectLoop(String abiList)425     Runnable runSelectLoop(String abiList) {
426         ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
427         ArrayList<ZygoteConnection> peers = new ArrayList<>();
428 
429         socketFDs.add(mZygoteSocket.getFileDescriptor());
430         peers.add(null);
431 
432         mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
433 
434         while (true) {
435             fetchUsapPoolPolicyPropsWithMinInterval();
436             mUsapPoolRefillAction = UsapPoolRefillAction.NONE;
437 
438             int[] usapPipeFDs = null;
439             StructPollfd[] pollFDs;
440 
441             // Allocate enough space for the poll structs, taking into account
442             // the state of the USAP pool for this Zygote (could be a
443             // regular Zygote, a WebView Zygote, or an AppZygote).
444             if (mUsapPoolEnabled) {
445                 usapPipeFDs = Zygote.getUsapPipeFDs();
446                 pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length];
447             } else {
448                 pollFDs = new StructPollfd[socketFDs.size()];
449             }
450 
451             /*
452              * For reasons of correctness the USAP pool pipe and event FDs
453              * must be processed before the session and server sockets.  This
454              * is to ensure that the USAP pool accounting information is
455              * accurate when handling other requests like API deny list
456              * exemptions.
457              */
458 
459             int pollIndex = 0;
460             for (FileDescriptor socketFD : socketFDs) {
461                 pollFDs[pollIndex] = new StructPollfd();
462                 pollFDs[pollIndex].fd = socketFD;
463                 pollFDs[pollIndex].events = (short) POLLIN;
464                 ++pollIndex;
465             }
466 
467             final int usapPoolEventFDIndex = pollIndex;
468 
469             if (mUsapPoolEnabled) {
470                 pollFDs[pollIndex] = new StructPollfd();
471                 pollFDs[pollIndex].fd = mUsapPoolEventFD;
472                 pollFDs[pollIndex].events = (short) POLLIN;
473                 ++pollIndex;
474 
475                 // The usapPipeFDs array will always be filled in if the USAP Pool is enabled.
476                 assert usapPipeFDs != null;
477                 for (int usapPipeFD : usapPipeFDs) {
478                     FileDescriptor managedFd = new FileDescriptor();
479                     managedFd.setInt$(usapPipeFD);
480 
481                     pollFDs[pollIndex] = new StructPollfd();
482                     pollFDs[pollIndex].fd = managedFd;
483                     pollFDs[pollIndex].events = (short) POLLIN;
484                     ++pollIndex;
485                 }
486             }
487 
488             int pollTimeoutMs;
489 
490             if (mUsapPoolRefillTriggerTimestamp == INVALID_TIMESTAMP) {
491                 pollTimeoutMs = -1;
492             } else {
493                 long elapsedTimeMs = System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp;
494 
495                 if (elapsedTimeMs >= mUsapPoolRefillDelayMs) {
496                     // The refill delay has elapsed during the period between poll invocations.
497                     // We will now check for any currently ready file descriptors before refilling
498                     // the USAP pool.
499                     pollTimeoutMs = 0;
500                     mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
501                     mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
502 
503                 } else if (elapsedTimeMs <= 0) {
504                     // This can occur if the clock used by currentTimeMillis is reset, which is
505                     // possible because it is not guaranteed to be monotonic.  Because we can't tell
506                     // how far back the clock was set the best way to recover is to simply re-start
507                     // the respawn delay countdown.
508                     pollTimeoutMs = mUsapPoolRefillDelayMs;
509 
510                 } else {
511                     pollTimeoutMs = (int) (mUsapPoolRefillDelayMs - elapsedTimeMs);
512                 }
513             }
514 
515             int pollReturnValue;
516             try {
517                 pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
518             } catch (ErrnoException ex) {
519                 throw new RuntimeException("poll failed", ex);
520             }
521 
522             if (pollReturnValue == 0) {
523                 // The poll returned zero results either when the timeout value has been exceeded
524                 // or when a non-blocking poll is issued and no FDs are ready.  In either case it
525                 // is time to refill the pool.  This will result in a duplicate assignment when
526                 // the non-blocking poll returns zero results, but it avoids an additional
527                 // conditional in the else branch.
528                 mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
529                 mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
530 
531             } else {
532                 boolean usapPoolFDRead = false;
533 
534                 while (--pollIndex >= 0) {
535                     if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
536                         continue;
537                     }
538 
539                     if (pollIndex == 0) {
540                         // Zygote server socket
541                         ZygoteConnection newPeer = acceptCommandPeer(abiList);
542                         peers.add(newPeer);
543                         socketFDs.add(newPeer.getFileDescriptor());
544                     } else if (pollIndex < usapPoolEventFDIndex) {
545                         // Session socket accepted from the Zygote server socket
546 
547                         try {
548                             ZygoteConnection connection = peers.get(pollIndex);
549                             boolean multipleForksOK = !isUsapPoolEnabled()
550                                     && ZygoteHooks.isIndefiniteThreadSuspensionSafe();
551                             final Runnable command =
552                                     connection.processCommand(this, multipleForksOK);
553 
554                             // TODO (chriswailes): Is this extra check necessary?
555                             if (mIsForkChild) {
556                                 // We're in the child. We should always have a command to run at
557                                 // this stage if processCommand hasn't called "exec".
558                                 if (command == null) {
559                                     throw new IllegalStateException("command == null");
560                                 }
561 
562                                 return command;
563                             } else {
564                                 // We're in the server - we should never have any commands to run.
565                                 if (command != null) {
566                                     throw new IllegalStateException("command != null");
567                                 }
568 
569                                 // We don't know whether the remote side of the socket was closed or
570                                 // not until we attempt to read from it from processCommand. This
571                                 // shows up as a regular POLLIN event in our regular processing
572                                 // loop.
573                                 if (connection.isClosedByPeer()) {
574                                     connection.closeSocket();
575                                     peers.remove(pollIndex);
576                                     socketFDs.remove(pollIndex);
577                                 }
578                             }
579                         } catch (Exception e) {
580                             if (!mIsForkChild) {
581                                 // We're in the server so any exception here is one that has taken
582                                 // place pre-fork while processing commands or reading / writing
583                                 // from the control socket. Make a loud noise about any such
584                                 // exceptions so that we know exactly what failed and why.
585 
586                                 Slog.e(TAG, "Exception executing zygote command: ", e);
587 
588                                 // Make sure the socket is closed so that the other end knows
589                                 // immediately that something has gone wrong and doesn't time out
590                                 // waiting for a response.
591                                 ZygoteConnection conn = peers.remove(pollIndex);
592                                 conn.closeSocket();
593 
594                                 socketFDs.remove(pollIndex);
595                             } else {
596                                 // We're in the child so any exception caught here has happened post
597                                 // fork and before we execute ActivityThread.main (or any other
598                                 // main() method). Log the details of the exception and bring down
599                                 // the process.
600                                 Log.e(TAG, "Caught post-fork exception in child process.", e);
601                                 throw e;
602                             }
603                         } finally {
604                             // Reset the child flag, in the event that the child process is a child-
605                             // zygote. The flag will not be consulted this loop pass after the
606                             // Runnable is returned.
607                             mIsForkChild = false;
608                         }
609 
610                     } else {
611                         // Either the USAP pool event FD or a USAP reporting pipe.
612 
613                         // If this is the event FD the payload will be the number of USAPs removed.
614                         // If this is a reporting pipe FD the payload will be the PID of the USAP
615                         // that was just specialized.  The `continue` statements below ensure that
616                         // the messagePayload will always be valid if we complete the try block
617                         // without an exception.
618                         long messagePayload;
619 
620                         try {
621                             byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES];
622                             int readBytes =
623                                     Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);
624 
625                             if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
626                                 DataInputStream inputStream =
627                                         new DataInputStream(new ByteArrayInputStream(buffer));
628 
629                                 messagePayload = inputStream.readLong();
630                             } else {
631                                 Log.e(TAG, "Incomplete read from USAP management FD of size "
632                                         + readBytes);
633                                 continue;
634                             }
635                         } catch (Exception ex) {
636                             if (pollIndex == usapPoolEventFDIndex) {
637                                 Log.e(TAG, "Failed to read from USAP pool event FD: "
638                                         + ex.getMessage());
639                             } else {
640                                 Log.e(TAG, "Failed to read from USAP reporting pipe: "
641                                         + ex.getMessage());
642                             }
643 
644                             continue;
645                         }
646 
647                         if (pollIndex > usapPoolEventFDIndex) {
648                             Zygote.removeUsapTableEntry((int) messagePayload);
649                         }
650 
651                         usapPoolFDRead = true;
652                     }
653                 }
654 
655                 if (usapPoolFDRead) {
656                     int usapPoolCount = Zygote.getUsapPoolCount();
657 
658                     if (usapPoolCount < mUsapPoolSizeMin) {
659                         // Immediate refill
660                         mUsapPoolRefillAction = UsapPoolRefillAction.IMMEDIATE;
661                     } else if (mUsapPoolSizeMax - usapPoolCount >= mUsapPoolRefillThreshold) {
662                         // Delayed refill
663                         mUsapPoolRefillTriggerTimestamp = System.currentTimeMillis();
664                     }
665                 }
666             }
667 
668             if (mUsapPoolRefillAction != UsapPoolRefillAction.NONE) {
669                 int[] sessionSocketRawFDs =
670                         socketFDs.subList(1, socketFDs.size())
671                                 .stream()
672                                 .mapToInt(FileDescriptor::getInt$)
673                                 .toArray();
674 
675                 final boolean isPriorityRefill =
676                         mUsapPoolRefillAction == UsapPoolRefillAction.IMMEDIATE;
677 
678                 final Runnable command =
679                         fillUsapPool(sessionSocketRawFDs, isPriorityRefill);
680 
681                 if (command != null) {
682                     return command;
683                 } else if (isPriorityRefill) {
684                     // Schedule a delayed refill to finish refilling the pool.
685                     mUsapPoolRefillTriggerTimestamp = System.currentTimeMillis();
686                 }
687             }
688         }
689     }
690 }
691