ZygoteProcess.java revision 33d5138e132561d6c3c02fc0e265aa3c158f6798
1/* 2 * Copyright (C) 2016 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 17package android.os; 18 19import android.net.LocalSocket; 20import android.net.LocalSocketAddress; 21import android.util.Log; 22import android.util.Slog; 23 24import com.android.internal.annotations.GuardedBy; 25import com.android.internal.os.Zygote; 26import com.android.internal.util.Preconditions; 27 28import java.io.BufferedWriter; 29import java.io.DataInputStream; 30import java.io.IOException; 31import java.io.OutputStreamWriter; 32import java.nio.charset.StandardCharsets; 33import java.util.ArrayList; 34import java.util.Arrays; 35import java.util.Collections; 36import java.util.List; 37import java.util.UUID; 38 39/*package*/ class ZygoteStartFailedEx extends Exception { 40 ZygoteStartFailedEx(String s) { 41 super(s); 42 } 43 44 ZygoteStartFailedEx(Throwable cause) { 45 super(cause); 46 } 47 48 ZygoteStartFailedEx(String s, Throwable cause) { 49 super(s, cause); 50 } 51} 52 53/** 54 * Maintains communication state with the zygote processes. This class is responsible 55 * for the sockets opened to the zygotes and for starting processes on behalf of the 56 * {@link android.os.Process} class. 57 * 58 * {@hide} 59 */ 60public class ZygoteProcess { 61 private static final String LOG_TAG = "ZygoteProcess"; 62 63 /** 64 * The name of the socket used to communicate with the primary zygote. 65 */ 66 private final LocalSocketAddress mSocket; 67 68 /** 69 * The name of the secondary (alternate ABI) zygote socket. 70 */ 71 private final LocalSocketAddress mSecondarySocket; 72 73 public ZygoteProcess(String primarySocket, String secondarySocket) { 74 this(new LocalSocketAddress(primarySocket, LocalSocketAddress.Namespace.RESERVED), 75 new LocalSocketAddress(secondarySocket, LocalSocketAddress.Namespace.RESERVED)); 76 } 77 78 public ZygoteProcess(LocalSocketAddress primarySocket, LocalSocketAddress secondarySocket) { 79 mSocket = primarySocket; 80 mSecondarySocket = secondarySocket; 81 } 82 83 public LocalSocketAddress getPrimarySocketAddress() { 84 return mSocket; 85 } 86 87 /** 88 * State for communicating with the zygote process. 89 */ 90 public static class ZygoteState { 91 final LocalSocket socket; 92 final DataInputStream inputStream; 93 final BufferedWriter writer; 94 final List<String> abiList; 95 96 boolean mClosed; 97 98 private ZygoteState(LocalSocket socket, DataInputStream inputStream, 99 BufferedWriter writer, List<String> abiList) { 100 this.socket = socket; 101 this.inputStream = inputStream; 102 this.writer = writer; 103 this.abiList = abiList; 104 } 105 106 public static ZygoteState connect(LocalSocketAddress address) throws IOException { 107 DataInputStream zygoteInputStream = null; 108 BufferedWriter zygoteWriter = null; 109 final LocalSocket zygoteSocket = new LocalSocket(); 110 111 try { 112 zygoteSocket.connect(address); 113 114 zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream()); 115 116 zygoteWriter = new BufferedWriter(new OutputStreamWriter( 117 zygoteSocket.getOutputStream()), 256); 118 } catch (IOException ex) { 119 try { 120 zygoteSocket.close(); 121 } catch (IOException ignore) { 122 } 123 124 throw ex; 125 } 126 127 String abiListString = getAbiList(zygoteWriter, zygoteInputStream); 128 Log.i("Zygote", "Process: zygote socket " + address.getNamespace() + "/" 129 + address.getName() + " opened, supported ABIS: " + abiListString); 130 131 return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter, 132 Arrays.asList(abiListString.split(","))); 133 } 134 135 boolean matches(String abi) { 136 return abiList.contains(abi); 137 } 138 139 public void close() { 140 try { 141 socket.close(); 142 } catch (IOException ex) { 143 Log.e(LOG_TAG,"I/O exception on routine close", ex); 144 } 145 146 mClosed = true; 147 } 148 149 boolean isClosed() { 150 return mClosed; 151 } 152 } 153 154 /** 155 * Lock object to protect access to the two ZygoteStates below. This lock must be 156 * acquired while communicating over the ZygoteState's socket, to prevent 157 * interleaved access. 158 */ 159 private final Object mLock = new Object(); 160 161 /** 162 * List of exemptions to the API blacklist. These are prefix matches on the runtime format 163 * symbol signature. Any matching symbol is treated by the runtime as being on the light grey 164 * list. 165 */ 166 private List<String> mApiBlacklistExemptions = Collections.emptyList(); 167 168 /** 169 * The state of the connection to the primary zygote. 170 */ 171 private ZygoteState primaryZygoteState; 172 173 /** 174 * The state of the connection to the secondary zygote. 175 */ 176 private ZygoteState secondaryZygoteState; 177 178 /** 179 * Start a new process. 180 * 181 * <p>If processes are enabled, a new process is created and the 182 * static main() function of a <var>processClass</var> is executed there. 183 * The process will continue running after this function returns. 184 * 185 * <p>If processes are not enabled, a new thread in the caller's 186 * process is created and main() of <var>processclass</var> called there. 187 * 188 * <p>The niceName parameter, if not an empty string, is a custom name to 189 * give to the process instead of using processClass. This allows you to 190 * make easily identifyable processes even if you are using the same base 191 * <var>processClass</var> to start them. 192 * 193 * When invokeWith is not null, the process will be started as a fresh app 194 * and not a zygote fork. Note that this is only allowed for uid 0 or when 195 * runtimeFlags contains DEBUG_ENABLE_DEBUGGER. 196 * 197 * @param processClass The class to use as the process's main entry 198 * point. 199 * @param niceName A more readable name to use for the process. 200 * @param uid The user-id under which the process will run. 201 * @param gid The group-id under which the process will run. 202 * @param gids Additional group-ids associated with the process. 203 * @param runtimeFlags Additional flags. 204 * @param targetSdkVersion The target SDK version for the app. 205 * @param seInfo null-ok SELinux information for the new process. 206 * @param abi non-null the ABI this app should be started with. 207 * @param instructionSet null-ok the instruction set to use. 208 * @param appDataDir null-ok the data directory of the app. 209 * @param invokeWith null-ok the command to invoke with. 210 * @param zygoteArgs Additional arguments to supply to the zygote process. 211 * 212 * @return An object that describes the result of the attempt to start the process. 213 * @throws RuntimeException on fatal start failure 214 */ 215 public final Process.ProcessStartResult start(final String processClass, 216 final String niceName, 217 int uid, int gid, int[] gids, 218 int runtimeFlags, int mountExternal, 219 int targetSdkVersion, 220 String seInfo, 221 String abi, 222 String instructionSet, 223 String appDataDir, 224 String invokeWith, 225 String[] zygoteArgs) { 226 try { 227 return startViaZygote(processClass, niceName, uid, gid, gids, 228 runtimeFlags, mountExternal, targetSdkVersion, seInfo, 229 abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */, 230 zygoteArgs); 231 } catch (ZygoteStartFailedEx ex) { 232 Log.e(LOG_TAG, 233 "Starting VM process through Zygote failed"); 234 throw new RuntimeException( 235 "Starting VM process through Zygote failed", ex); 236 } 237 } 238 239 /** retry interval for opening a zygote socket */ 240 static final int ZYGOTE_RETRY_MILLIS = 500; 241 242 /** 243 * Queries the zygote for the list of ABIS it supports. 244 * 245 * @throws ZygoteStartFailedEx if the query failed. 246 */ 247 @GuardedBy("mLock") 248 private static String getAbiList(BufferedWriter writer, DataInputStream inputStream) 249 throws IOException { 250 // Each query starts with the argument count (1 in this case) 251 writer.write("1"); 252 // ... followed by a new-line. 253 writer.newLine(); 254 // ... followed by our only argument. 255 writer.write("--query-abi-list"); 256 writer.newLine(); 257 writer.flush(); 258 259 // The response is a length prefixed stream of ASCII bytes. 260 int numBytes = inputStream.readInt(); 261 byte[] bytes = new byte[numBytes]; 262 inputStream.readFully(bytes); 263 264 return new String(bytes, StandardCharsets.US_ASCII); 265 } 266 267 /** 268 * Sends an argument list to the zygote process, which starts a new child 269 * and returns the child's pid. Please note: the present implementation 270 * replaces newlines in the argument list with spaces. 271 * 272 * @throws ZygoteStartFailedEx if process start failed for any reason 273 */ 274 @GuardedBy("mLock") 275 private static Process.ProcessStartResult zygoteSendArgsAndGetResult( 276 ZygoteState zygoteState, ArrayList<String> args) 277 throws ZygoteStartFailedEx { 278 try { 279 // Throw early if any of the arguments are malformed. This means we can 280 // avoid writing a partial response to the zygote. 281 int sz = args.size(); 282 for (int i = 0; i < sz; i++) { 283 if (args.get(i).indexOf('\n') >= 0) { 284 throw new ZygoteStartFailedEx("embedded newlines not allowed"); 285 } 286 } 287 288 /** 289 * See com.android.internal.os.SystemZygoteInit.readArgumentList() 290 * Presently the wire format to the zygote process is: 291 * a) a count of arguments (argc, in essence) 292 * b) a number of newline-separated argument strings equal to count 293 * 294 * After the zygote process reads these it will write the pid of 295 * the child or -1 on failure, followed by boolean to 296 * indicate whether a wrapper process was used. 297 */ 298 final BufferedWriter writer = zygoteState.writer; 299 final DataInputStream inputStream = zygoteState.inputStream; 300 301 writer.write(Integer.toString(args.size())); 302 writer.newLine(); 303 304 for (int i = 0; i < sz; i++) { 305 String arg = args.get(i); 306 writer.write(arg); 307 writer.newLine(); 308 } 309 310 writer.flush(); 311 312 // Should there be a timeout on this? 313 Process.ProcessStartResult result = new Process.ProcessStartResult(); 314 315 // Always read the entire result from the input stream to avoid leaving 316 // bytes in the stream for future process starts to accidentally stumble 317 // upon. 318 result.pid = inputStream.readInt(); 319 result.usingWrapper = inputStream.readBoolean(); 320 321 if (result.pid < 0) { 322 throw new ZygoteStartFailedEx("fork() failed"); 323 } 324 return result; 325 } catch (IOException ex) { 326 zygoteState.close(); 327 throw new ZygoteStartFailedEx(ex); 328 } 329 } 330 331 /** 332 * Starts a new process via the zygote mechanism. 333 * 334 * @param processClass Class name whose static main() to run 335 * @param niceName 'nice' process name to appear in ps 336 * @param uid a POSIX uid that the new process should setuid() to 337 * @param gid a POSIX gid that the new process shuold setgid() to 338 * @param gids null-ok; a list of supplementary group IDs that the 339 * new process should setgroup() to. 340 * @param runtimeFlags Additional flags for the runtime. 341 * @param targetSdkVersion The target SDK version for the app. 342 * @param seInfo null-ok SELinux information for the new process. 343 * @param abi the ABI the process should use. 344 * @param instructionSet null-ok the instruction set to use. 345 * @param appDataDir null-ok the data directory of the app. 346 * @param startChildZygote Start a sub-zygote. This creates a new zygote process 347 * that has its state cloned from this zygote process. 348 * @param extraArgs Additional arguments to supply to the zygote process. 349 * @return An object that describes the result of the attempt to start the process. 350 * @throws ZygoteStartFailedEx if process start failed for any reason 351 */ 352 private Process.ProcessStartResult startViaZygote(final String processClass, 353 final String niceName, 354 final int uid, final int gid, 355 final int[] gids, 356 int runtimeFlags, int mountExternal, 357 int targetSdkVersion, 358 String seInfo, 359 String abi, 360 String instructionSet, 361 String appDataDir, 362 String invokeWith, 363 boolean startChildZygote, 364 String[] extraArgs) 365 throws ZygoteStartFailedEx { 366 ArrayList<String> argsForZygote = new ArrayList<String>(); 367 368 // --runtime-args, --setuid=, --setgid=, 369 // and --setgroups= must go first 370 argsForZygote.add("--runtime-args"); 371 argsForZygote.add("--setuid=" + uid); 372 argsForZygote.add("--setgid=" + gid); 373 argsForZygote.add("--runtime-flags=" + runtimeFlags); 374 if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) { 375 argsForZygote.add("--mount-external-default"); 376 } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) { 377 argsForZygote.add("--mount-external-read"); 378 } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) { 379 argsForZygote.add("--mount-external-write"); 380 } 381 argsForZygote.add("--target-sdk-version=" + targetSdkVersion); 382 383 // --setgroups is a comma-separated list 384 if (gids != null && gids.length > 0) { 385 StringBuilder sb = new StringBuilder(); 386 sb.append("--setgroups="); 387 388 int sz = gids.length; 389 for (int i = 0; i < sz; i++) { 390 if (i != 0) { 391 sb.append(','); 392 } 393 sb.append(gids[i]); 394 } 395 396 argsForZygote.add(sb.toString()); 397 } 398 399 if (niceName != null) { 400 argsForZygote.add("--nice-name=" + niceName); 401 } 402 403 if (seInfo != null) { 404 argsForZygote.add("--seinfo=" + seInfo); 405 } 406 407 if (instructionSet != null) { 408 argsForZygote.add("--instruction-set=" + instructionSet); 409 } 410 411 if (appDataDir != null) { 412 argsForZygote.add("--app-data-dir=" + appDataDir); 413 } 414 415 if (invokeWith != null) { 416 argsForZygote.add("--invoke-with"); 417 argsForZygote.add(invokeWith); 418 } 419 420 if (startChildZygote) { 421 argsForZygote.add("--start-child-zygote"); 422 } 423 424 argsForZygote.add(processClass); 425 426 if (extraArgs != null) { 427 for (String arg : extraArgs) { 428 argsForZygote.add(arg); 429 } 430 } 431 432 synchronized(mLock) { 433 return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote); 434 } 435 } 436 437 /** 438 * Closes the connections to the zygote, if they exist. 439 */ 440 public void close() { 441 if (primaryZygoteState != null) { 442 primaryZygoteState.close(); 443 } 444 if (secondaryZygoteState != null) { 445 secondaryZygoteState.close(); 446 } 447 } 448 449 /** 450 * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block 451 * and retry if the zygote is unresponsive. This method is a no-op if a connection is 452 * already open. 453 */ 454 public void establishZygoteConnectionForAbi(String abi) { 455 try { 456 synchronized(mLock) { 457 openZygoteSocketIfNeeded(abi); 458 } 459 } catch (ZygoteStartFailedEx ex) { 460 throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex); 461 } 462 } 463 464 /** 465 * Push hidden API blacklisting exemptions into the zygote process(es). 466 * 467 * <p>The list of exemptions will take affect for all new processes forked from the zygote after 468 * this call. 469 * 470 * @param exemptions List of hidden API exemption prefixes. Any matching members are treated as 471 * whitelisted/public APIs (i.e. allowed, no logging of usage). 472 */ 473 public void setApiBlacklistExemptions(List<String> exemptions) { 474 synchronized (mLock) { 475 mApiBlacklistExemptions = exemptions; 476 maybeSetApiBlacklistExemptions(primaryZygoteState, true); 477 maybeSetApiBlacklistExemptions(secondaryZygoteState, true); 478 } 479 } 480 481 @GuardedBy("mLock") 482 private void maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) { 483 if (state == null || state.isClosed()) { 484 return; 485 } 486 if (!sendIfEmpty && mApiBlacklistExemptions.isEmpty()) { 487 return; 488 } 489 try { 490 state.writer.write(Integer.toString(mApiBlacklistExemptions.size() + 1)); 491 state.writer.newLine(); 492 state.writer.write("--set-api-blacklist-exemptions"); 493 state.writer.newLine(); 494 for (int i = 0; i < mApiBlacklistExemptions.size(); ++i) { 495 state.writer.write(mApiBlacklistExemptions.get(i)); 496 state.writer.newLine(); 497 } 498 state.writer.flush(); 499 int status = state.inputStream.readInt(); 500 if (status != 0) { 501 Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status); 502 } 503 } catch (IOException ioe) { 504 Slog.e(LOG_TAG, "Failed to set API blacklist exemptions", ioe); 505 } 506 } 507 508 /** 509 * Tries to open socket to Zygote process if not already open. If 510 * already open, does nothing. May block and retry. Requires that mLock be held. 511 */ 512 @GuardedBy("mLock") 513 private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx { 514 Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held"); 515 516 if (primaryZygoteState == null || primaryZygoteState.isClosed()) { 517 try { 518 primaryZygoteState = ZygoteState.connect(mSocket); 519 } catch (IOException ioe) { 520 throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe); 521 } 522 maybeSetApiBlacklistExemptions(primaryZygoteState, false); 523 } 524 if (primaryZygoteState.matches(abi)) { 525 return primaryZygoteState; 526 } 527 528 // The primary zygote didn't match. Try the secondary. 529 if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) { 530 try { 531 secondaryZygoteState = ZygoteState.connect(mSecondarySocket); 532 } catch (IOException ioe) { 533 throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe); 534 } 535 maybeSetApiBlacklistExemptions(secondaryZygoteState, false); 536 } 537 538 if (secondaryZygoteState.matches(abi)) { 539 return secondaryZygoteState; 540 } 541 542 throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi); 543 } 544 545 /** 546 * Instructs the zygote to pre-load the classes and native libraries at the given paths 547 * for the specified abi. Not all zygotes support this function. 548 */ 549 public boolean preloadPackageForAbi(String packagePath, String libsPath, String libFileName, 550 String cacheKey, String abi) throws ZygoteStartFailedEx, 551 IOException { 552 synchronized(mLock) { 553 ZygoteState state = openZygoteSocketIfNeeded(abi); 554 state.writer.write("5"); 555 state.writer.newLine(); 556 557 state.writer.write("--preload-package"); 558 state.writer.newLine(); 559 560 state.writer.write(packagePath); 561 state.writer.newLine(); 562 563 state.writer.write(libsPath); 564 state.writer.newLine(); 565 566 state.writer.write(libFileName); 567 state.writer.newLine(); 568 569 state.writer.write(cacheKey); 570 state.writer.newLine(); 571 572 state.writer.flush(); 573 574 return (state.inputStream.readInt() == 0); 575 } 576 } 577 578 /** 579 * Instructs the zygote to preload the default set of classes and resources. Returns 580 * {@code true} if a preload was performed as a result of this call, and {@code false} 581 * otherwise. The latter usually means that the zygote eagerly preloaded at startup 582 * or due to a previous call to {@code preloadDefault}. Note that this call is synchronous. 583 */ 584 public boolean preloadDefault(String abi) throws ZygoteStartFailedEx, IOException { 585 synchronized (mLock) { 586 ZygoteState state = openZygoteSocketIfNeeded(abi); 587 // Each query starts with the argument count (1 in this case) 588 state.writer.write("1"); 589 state.writer.newLine(); 590 state.writer.write("--preload-default"); 591 state.writer.newLine(); 592 state.writer.flush(); 593 594 return (state.inputStream.readInt() == 0); 595 } 596 } 597 598 /** 599 * Try connecting to the Zygote over and over again until we hit a time-out. 600 * @param socketName The name of the socket to connect to. 601 */ 602 public static void waitForConnectionToZygote(String socketName) { 603 final LocalSocketAddress address = 604 new LocalSocketAddress(socketName, LocalSocketAddress.Namespace.RESERVED); 605 waitForConnectionToZygote(address); 606 } 607 608 /** 609 * Try connecting to the Zygote over and over again until we hit a time-out. 610 * @param address The name of the socket to connect to. 611 */ 612 public static void waitForConnectionToZygote(LocalSocketAddress address) { 613 for (int n = 20; n >= 0; n--) { 614 try { 615 final ZygoteState zs = ZygoteState.connect(address); 616 zs.close(); 617 return; 618 } catch (IOException ioe) { 619 Log.w(LOG_TAG, 620 "Got error connecting to zygote, retrying. msg= " + ioe.getMessage()); 621 } 622 623 try { 624 Thread.sleep(1000); 625 } catch (InterruptedException ie) { 626 } 627 } 628 Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + address.getName()); 629 } 630 631 /** 632 * Starts a new zygote process as a child of this zygote. This is used to create 633 * secondary zygotes that inherit data from the zygote that this object 634 * communicates with. This returns a new ZygoteProcess representing a connection 635 * to the newly created zygote. Throws an exception if the zygote cannot be started. 636 */ 637 public ChildZygoteProcess startChildZygote(final String processClass, 638 final String niceName, 639 int uid, int gid, int[] gids, 640 int runtimeFlags, 641 String seInfo, 642 String abi, 643 String instructionSet) { 644 // Create an unguessable address in the global abstract namespace. 645 final LocalSocketAddress serverAddress = new LocalSocketAddress( 646 processClass + "/" + UUID.randomUUID().toString()); 647 648 final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName()}; 649 650 Process.ProcessStartResult result; 651 try { 652 result = startViaZygote(processClass, niceName, uid, gid, 653 gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo, 654 abi, instructionSet, null /* appDataDir */, null /* invokeWith */, 655 true /* startChildZygote */, extraArgs); 656 } catch (ZygoteStartFailedEx ex) { 657 throw new RuntimeException("Starting child-zygote through Zygote failed", ex); 658 } 659 660 return new ChildZygoteProcess(serverAddress, result.pid); 661 } 662} 663