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