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