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