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             // Sanity 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 usapMain.
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     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      */
runSelectLoop(String abiList)424     Runnable runSelectLoop(String abiList) {
425         ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
426         ArrayList<ZygoteConnection> peers = new ArrayList<>();
427 
428         socketFDs.add(mZygoteSocket.getFileDescriptor());
429         peers.add(null);
430 
431         mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
432 
433         while (true) {
434             fetchUsapPoolPolicyPropsWithMinInterval();
435             mUsapPoolRefillAction = UsapPoolRefillAction.NONE;
436 
437             int[] usapPipeFDs = null;
438             StructPollfd[] pollFDs;
439 
440             // Allocate enough space for the poll structs, taking into account
441             // the state of the USAP pool for this Zygote (could be a
442             // regular Zygote, a WebView Zygote, or an AppZygote).
443             if (mUsapPoolEnabled) {
444                 usapPipeFDs = Zygote.getUsapPipeFDs();
445                 pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length];
446             } else {
447                 pollFDs = new StructPollfd[socketFDs.size()];
448             }
449 
450             /*
451              * For reasons of correctness the USAP pool pipe and event FDs
452              * must be processed before the session and server sockets.  This
453              * is to ensure that the USAP pool accounting information is
454              * accurate when handling other requests like API blacklist
455              * exemptions.
456              */
457 
458             int pollIndex = 0;
459             for (FileDescriptor socketFD : socketFDs) {
460                 pollFDs[pollIndex] = new StructPollfd();
461                 pollFDs[pollIndex].fd = socketFD;
462                 pollFDs[pollIndex].events = (short) POLLIN;
463                 ++pollIndex;
464             }
465 
466             final int usapPoolEventFDIndex = pollIndex;
467 
468             if (mUsapPoolEnabled) {
469                 pollFDs[pollIndex] = new StructPollfd();
470                 pollFDs[pollIndex].fd = mUsapPoolEventFD;
471                 pollFDs[pollIndex].events = (short) POLLIN;
472                 ++pollIndex;
473 
474                 // The usapPipeFDs array will always be filled in if the USAP Pool is enabled.
475                 assert usapPipeFDs != null;
476                 for (int usapPipeFD : usapPipeFDs) {
477                     FileDescriptor managedFd = new FileDescriptor();
478                     managedFd.setInt$(usapPipeFD);
479 
480                     pollFDs[pollIndex] = new StructPollfd();
481                     pollFDs[pollIndex].fd = managedFd;
482                     pollFDs[pollIndex].events = (short) POLLIN;
483                     ++pollIndex;
484                 }
485             }
486 
487             int pollTimeoutMs;
488 
489             if (mUsapPoolRefillTriggerTimestamp == INVALID_TIMESTAMP) {
490                 pollTimeoutMs = -1;
491             } else {
492                 long elapsedTimeMs = System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp;
493 
494                 if (elapsedTimeMs >= mUsapPoolRefillDelayMs) {
495                     // Normalize the poll timeout value when the time between one poll event and the
496                     // next pushes us over the delay value.  This prevents poll receiving a 0
497                     // timeout value, which would result in it returning immediately.
498                     pollTimeoutMs = -1;
499 
500                 } else if (elapsedTimeMs <= 0) {
501                     // This can occur if the clock used by currentTimeMillis is reset, which is
502                     // possible because it is not guaranteed to be monotonic.  Because we can't tell
503                     // how far back the clock was set the best way to recover is to simply re-start
504                     // the respawn delay countdown.
505                     pollTimeoutMs = mUsapPoolRefillDelayMs;
506 
507                 } else {
508                     pollTimeoutMs = (int) (mUsapPoolRefillDelayMs - elapsedTimeMs);
509                 }
510             }
511 
512             int pollReturnValue;
513             try {
514                 pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
515             } catch (ErrnoException ex) {
516                 throw new RuntimeException("poll failed", ex);
517             }
518 
519             if (pollReturnValue == 0) {
520                 // The poll timeout has been exceeded.  This only occurs when we have finished the
521                 // USAP pool refill delay period.
522 
523                 mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
524                 mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
525 
526             } else {
527                 boolean usapPoolFDRead = false;
528 
529                 while (--pollIndex >= 0) {
530                     if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
531                         continue;
532                     }
533 
534                     if (pollIndex == 0) {
535                         // Zygote server socket
536 
537                         ZygoteConnection newPeer = acceptCommandPeer(abiList);
538                         peers.add(newPeer);
539                         socketFDs.add(newPeer.getFileDescriptor());
540 
541                     } else if (pollIndex < usapPoolEventFDIndex) {
542                         // Session socket accepted from the Zygote server socket
543 
544                         try {
545                             ZygoteConnection connection = peers.get(pollIndex);
546                             final Runnable command = connection.processOneCommand(this);
547 
548                             // TODO (chriswailes): Is this extra check necessary?
549                             if (mIsForkChild) {
550                                 // We're in the child. We should always have a command to run at
551                                 // this stage if processOneCommand hasn't called "exec".
552                                 if (command == null) {
553                                     throw new IllegalStateException("command == null");
554                                 }
555 
556                                 return command;
557                             } else {
558                                 // We're in the server - we should never have any commands to run.
559                                 if (command != null) {
560                                     throw new IllegalStateException("command != null");
561                                 }
562 
563                                 // We don't know whether the remote side of the socket was closed or
564                                 // not until we attempt to read from it from processOneCommand. This
565                                 // shows up as a regular POLLIN event in our regular processing
566                                 // loop.
567                                 if (connection.isClosedByPeer()) {
568                                     connection.closeSocket();
569                                     peers.remove(pollIndex);
570                                     socketFDs.remove(pollIndex);
571                                 }
572                             }
573                         } catch (Exception e) {
574                             if (!mIsForkChild) {
575                                 // We're in the server so any exception here is one that has taken
576                                 // place pre-fork while processing commands or reading / writing
577                                 // from the control socket. Make a loud noise about any such
578                                 // exceptions so that we know exactly what failed and why.
579 
580                                 Slog.e(TAG, "Exception executing zygote command: ", e);
581 
582                                 // Make sure the socket is closed so that the other end knows
583                                 // immediately that something has gone wrong and doesn't time out
584                                 // waiting for a response.
585                                 ZygoteConnection conn = peers.remove(pollIndex);
586                                 conn.closeSocket();
587 
588                                 socketFDs.remove(pollIndex);
589                             } else {
590                                 // We're in the child so any exception caught here has happened post
591                                 // fork and before we execute ActivityThread.main (or any other
592                                 // main() method). Log the details of the exception and bring down
593                                 // the process.
594                                 Log.e(TAG, "Caught post-fork exception in child process.", e);
595                                 throw e;
596                             }
597                         } finally {
598                             // Reset the child flag, in the event that the child process is a child-
599                             // zygote. The flag will not be consulted this loop pass after the
600                             // Runnable is returned.
601                             mIsForkChild = false;
602                         }
603 
604                     } else {
605                         // Either the USAP pool event FD or a USAP reporting pipe.
606 
607                         // If this is the event FD the payload will be the number of USAPs removed.
608                         // If this is a reporting pipe FD the payload will be the PID of the USAP
609                         // that was just specialized.  The `continue` statements below ensure that
610                         // the messagePayload will always be valid if we complete the try block
611                         // without an exception.
612                         long messagePayload;
613 
614                         try {
615                             byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES];
616                             int readBytes =
617                                     Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);
618 
619                             if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
620                                 DataInputStream inputStream =
621                                         new DataInputStream(new ByteArrayInputStream(buffer));
622 
623                                 messagePayload = inputStream.readLong();
624                             } else {
625                                 Log.e(TAG, "Incomplete read from USAP management FD of size "
626                                         + readBytes);
627                                 continue;
628                             }
629                         } catch (Exception ex) {
630                             if (pollIndex == usapPoolEventFDIndex) {
631                                 Log.e(TAG, "Failed to read from USAP pool event FD: "
632                                         + ex.getMessage());
633                             } else {
634                                 Log.e(TAG, "Failed to read from USAP reporting pipe: "
635                                         + ex.getMessage());
636                             }
637 
638                             continue;
639                         }
640 
641                         if (pollIndex > usapPoolEventFDIndex) {
642                             Zygote.removeUsapTableEntry((int) messagePayload);
643                         }
644 
645                         usapPoolFDRead = true;
646                     }
647                 }
648 
649                 if (usapPoolFDRead) {
650                     int usapPoolCount = Zygote.getUsapPoolCount();
651 
652                     if (usapPoolCount < mUsapPoolSizeMin) {
653                         // Immediate refill
654                         mUsapPoolRefillAction = UsapPoolRefillAction.IMMEDIATE;
655                     } else if (mUsapPoolSizeMax - usapPoolCount >= mUsapPoolRefillThreshold) {
656                         // Delayed refill
657                         mUsapPoolRefillTriggerTimestamp = System.currentTimeMillis();
658                     }
659                 }
660             }
661 
662             if (mUsapPoolRefillAction != UsapPoolRefillAction.NONE) {
663                 int[] sessionSocketRawFDs =
664                         socketFDs.subList(1, socketFDs.size())
665                                 .stream()
666                                 .mapToInt(FileDescriptor::getInt$)
667                                 .toArray();
668 
669                 final boolean isPriorityRefill =
670                         mUsapPoolRefillAction == UsapPoolRefillAction.IMMEDIATE;
671 
672                 final Runnable command =
673                         fillUsapPool(sessionSocketRawFDs, isPriorityRefill);
674 
675                 if (command != null) {
676                     return command;
677                 } else if (isPriorityRefill) {
678                     // Schedule a delayed refill to finish refilling the pool.
679                     mUsapPoolRefillTriggerTimestamp = System.currentTimeMillis();
680                 }
681             }
682         }
683     }
684 }
685