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 17 package android.os; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.UnsupportedAppUsage; 22 import android.content.pm.ApplicationInfo; 23 import android.net.LocalSocket; 24 import android.net.LocalSocketAddress; 25 import android.util.Log; 26 import android.util.Slog; 27 28 import com.android.internal.annotations.GuardedBy; 29 import com.android.internal.os.Zygote; 30 import com.android.internal.os.ZygoteConfig; 31 32 import java.io.BufferedWriter; 33 import java.io.DataInputStream; 34 import java.io.IOException; 35 import java.io.OutputStreamWriter; 36 import java.nio.charset.StandardCharsets; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.Base64; 40 import java.util.Collections; 41 import java.util.List; 42 import java.util.UUID; 43 44 /*package*/ class ZygoteStartFailedEx extends Exception { 45 @UnsupportedAppUsage ZygoteStartFailedEx(String s)46 ZygoteStartFailedEx(String s) { 47 super(s); 48 } 49 50 @UnsupportedAppUsage ZygoteStartFailedEx(Throwable cause)51 ZygoteStartFailedEx(Throwable cause) { 52 super(cause); 53 } 54 ZygoteStartFailedEx(String s, Throwable cause)55 ZygoteStartFailedEx(String s, Throwable cause) { 56 super(s, cause); 57 } 58 } 59 60 /** 61 * Maintains communication state with the zygote processes. This class is responsible 62 * for the sockets opened to the zygotes and for starting processes on behalf of the 63 * {@link android.os.Process} class. 64 * 65 * {@hide} 66 */ 67 public class ZygoteProcess { 68 69 private static final int ZYGOTE_CONNECT_TIMEOUT_MS = 20000; 70 71 /** 72 * Use a relatively short delay, because for app zygote, this is in the critical path of 73 * service launch. 74 */ 75 private static final int ZYGOTE_CONNECT_RETRY_DELAY_MS = 50; 76 77 private static final String LOG_TAG = "ZygoteProcess"; 78 79 /** 80 * The default value for enabling the unspecialized app process (USAP) pool. This value will 81 * not be used if the devices has a DeviceConfig profile pushed to it that contains a value for 82 * this key. 83 */ 84 private static final String USAP_POOL_ENABLED_DEFAULT = "false"; 85 86 /** 87 * The name of the socket used to communicate with the primary zygote. 88 */ 89 private final LocalSocketAddress mZygoteSocketAddress; 90 91 /** 92 * The name of the secondary (alternate ABI) zygote socket. 93 */ 94 private final LocalSocketAddress mZygoteSecondarySocketAddress; 95 96 /** 97 * The name of the socket used to communicate with the primary USAP pool. 98 */ 99 private final LocalSocketAddress mUsapPoolSocketAddress; 100 101 /** 102 * The name of the socket used to communicate with the secondary (alternate ABI) USAP pool. 103 */ 104 private final LocalSocketAddress mUsapPoolSecondarySocketAddress; 105 ZygoteProcess()106 public ZygoteProcess() { 107 mZygoteSocketAddress = 108 new LocalSocketAddress(Zygote.PRIMARY_SOCKET_NAME, 109 LocalSocketAddress.Namespace.RESERVED); 110 mZygoteSecondarySocketAddress = 111 new LocalSocketAddress(Zygote.SECONDARY_SOCKET_NAME, 112 LocalSocketAddress.Namespace.RESERVED); 113 114 mUsapPoolSocketAddress = 115 new LocalSocketAddress(Zygote.USAP_POOL_PRIMARY_SOCKET_NAME, 116 LocalSocketAddress.Namespace.RESERVED); 117 mUsapPoolSecondarySocketAddress = 118 new LocalSocketAddress(Zygote.USAP_POOL_SECONDARY_SOCKET_NAME, 119 LocalSocketAddress.Namespace.RESERVED); 120 } 121 ZygoteProcess(LocalSocketAddress primarySocketAddress, LocalSocketAddress secondarySocketAddress)122 public ZygoteProcess(LocalSocketAddress primarySocketAddress, 123 LocalSocketAddress secondarySocketAddress) { 124 mZygoteSocketAddress = primarySocketAddress; 125 mZygoteSecondarySocketAddress = secondarySocketAddress; 126 127 mUsapPoolSocketAddress = null; 128 mUsapPoolSecondarySocketAddress = null; 129 } 130 getPrimarySocketAddress()131 public LocalSocketAddress getPrimarySocketAddress() { 132 return mZygoteSocketAddress; 133 } 134 135 /** 136 * State for communicating with the zygote process. 137 */ 138 private static class ZygoteState implements AutoCloseable { 139 final LocalSocketAddress mZygoteSocketAddress; 140 final LocalSocketAddress mUsapSocketAddress; 141 142 private final LocalSocket mZygoteSessionSocket; 143 144 final DataInputStream mZygoteInputStream; 145 final BufferedWriter mZygoteOutputWriter; 146 147 private final List<String> mAbiList; 148 149 private boolean mClosed; 150 ZygoteState(LocalSocketAddress zygoteSocketAddress, LocalSocketAddress usapSocketAddress, LocalSocket zygoteSessionSocket, DataInputStream zygoteInputStream, BufferedWriter zygoteOutputWriter, List<String> abiList)151 private ZygoteState(LocalSocketAddress zygoteSocketAddress, 152 LocalSocketAddress usapSocketAddress, 153 LocalSocket zygoteSessionSocket, 154 DataInputStream zygoteInputStream, 155 BufferedWriter zygoteOutputWriter, 156 List<String> abiList) { 157 this.mZygoteSocketAddress = zygoteSocketAddress; 158 this.mUsapSocketAddress = usapSocketAddress; 159 this.mZygoteSessionSocket = zygoteSessionSocket; 160 this.mZygoteInputStream = zygoteInputStream; 161 this.mZygoteOutputWriter = zygoteOutputWriter; 162 this.mAbiList = abiList; 163 } 164 165 /** 166 * Create a new ZygoteState object by connecting to the given Zygote socket and saving the 167 * given USAP socket address. 168 * 169 * @param zygoteSocketAddress Zygote socket to connect to 170 * @param usapSocketAddress USAP socket address to save for later 171 * @return A new ZygoteState object containing a session socket for the given Zygote socket 172 * address 173 * @throws IOException 174 */ connect(@onNull LocalSocketAddress zygoteSocketAddress, @Nullable LocalSocketAddress usapSocketAddress)175 static ZygoteState connect(@NonNull LocalSocketAddress zygoteSocketAddress, 176 @Nullable LocalSocketAddress usapSocketAddress) 177 throws IOException { 178 179 DataInputStream zygoteInputStream; 180 BufferedWriter zygoteOutputWriter; 181 final LocalSocket zygoteSessionSocket = new LocalSocket(); 182 183 if (zygoteSocketAddress == null) { 184 throw new IllegalArgumentException("zygoteSocketAddress can't be null"); 185 } 186 187 try { 188 zygoteSessionSocket.connect(zygoteSocketAddress); 189 zygoteInputStream = new DataInputStream(zygoteSessionSocket.getInputStream()); 190 zygoteOutputWriter = 191 new BufferedWriter( 192 new OutputStreamWriter(zygoteSessionSocket.getOutputStream()), 193 Zygote.SOCKET_BUFFER_SIZE); 194 } catch (IOException ex) { 195 try { 196 zygoteSessionSocket.close(); 197 } catch (IOException ignore) { } 198 199 throw ex; 200 } 201 202 return new ZygoteState(zygoteSocketAddress, usapSocketAddress, 203 zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter, 204 getAbiList(zygoteOutputWriter, zygoteInputStream)); 205 } 206 getUsapSessionSocket()207 LocalSocket getUsapSessionSocket() throws IOException { 208 final LocalSocket usapSessionSocket = new LocalSocket(); 209 usapSessionSocket.connect(this.mUsapSocketAddress); 210 211 return usapSessionSocket; 212 } 213 matches(String abi)214 boolean matches(String abi) { 215 return mAbiList.contains(abi); 216 } 217 close()218 public void close() { 219 try { 220 mZygoteSessionSocket.close(); 221 } catch (IOException ex) { 222 Log.e(LOG_TAG,"I/O exception on routine close", ex); 223 } 224 225 mClosed = true; 226 } 227 isClosed()228 boolean isClosed() { 229 return mClosed; 230 } 231 } 232 233 /** 234 * Lock object to protect access to the two ZygoteStates below. This lock must be 235 * acquired while communicating over the ZygoteState's socket, to prevent 236 * interleaved access. 237 */ 238 private final Object mLock = new Object(); 239 240 /** 241 * List of exemptions to the API blacklist. These are prefix matches on the runtime format 242 * symbol signature. Any matching symbol is treated by the runtime as being on the light grey 243 * list. 244 */ 245 private List<String> mApiBlacklistExemptions = Collections.emptyList(); 246 247 /** 248 * Proportion of hidden API accesses that should be logged to the event log; 0 - 0x10000. 249 */ 250 private int mHiddenApiAccessLogSampleRate; 251 252 /** 253 * Proportion of hidden API accesses that should be logged to statslog; 0 - 0x10000. 254 */ 255 private int mHiddenApiAccessStatslogSampleRate; 256 257 /** 258 * The state of the connection to the primary zygote. 259 */ 260 private ZygoteState primaryZygoteState; 261 262 /** 263 * The state of the connection to the secondary zygote. 264 */ 265 private ZygoteState secondaryZygoteState; 266 267 /** 268 * If the USAP pool should be created and used to start applications. 269 * 270 * Setting this value to false will disable the creation, maintenance, and use of the USAP 271 * pool. When the USAP pool is disabled the application lifecycle will be identical to 272 * previous versions of Android. 273 */ 274 private boolean mUsapPoolEnabled = false; 275 276 /** 277 * Start a new process. 278 * 279 * <p>If processes are enabled, a new process is created and the 280 * static main() function of a <var>processClass</var> is executed there. 281 * The process will continue running after this function returns. 282 * 283 * <p>If processes are not enabled, a new thread in the caller's 284 * process is created and main() of <var>processclass</var> called there. 285 * 286 * <p>The niceName parameter, if not an empty string, is a custom name to 287 * give to the process instead of using processClass. This allows you to 288 * make easily identifyable processes even if you are using the same base 289 * <var>processClass</var> to start them. 290 * 291 * When invokeWith is not null, the process will be started as a fresh app 292 * and not a zygote fork. Note that this is only allowed for uid 0 or when 293 * runtimeFlags contains DEBUG_ENABLE_DEBUGGER. 294 * 295 * @param processClass The class to use as the process's main entry 296 * point. 297 * @param niceName A more readable name to use for the process. 298 * @param uid The user-id under which the process will run. 299 * @param gid The group-id under which the process will run. 300 * @param gids Additional group-ids associated with the process. 301 * @param runtimeFlags Additional flags. 302 * @param targetSdkVersion The target SDK version for the app. 303 * @param seInfo null-ok SELinux information for the new process. 304 * @param abi non-null the ABI this app should be started with. 305 * @param instructionSet null-ok the instruction set to use. 306 * @param appDataDir null-ok the data directory of the app. 307 * @param invokeWith null-ok the command to invoke with. 308 * @param packageName null-ok the name of the package this process belongs to. 309 * @param zygoteArgs Additional arguments to supply to the zygote process. 310 * 311 * @return An object that describes the result of the attempt to start the process. 312 * @throws RuntimeException on fatal start failure 313 */ start(@onNull final String processClass, final String niceName, int uid, int gid, @Nullable int[] gids, int runtimeFlags, int mountExternal, int targetSdkVersion, @Nullable String seInfo, @NonNull String abi, @Nullable String instructionSet, @Nullable String appDataDir, @Nullable String invokeWith, @Nullable String packageName, boolean useUsapPool, @Nullable String[] zygoteArgs)314 public final Process.ProcessStartResult start(@NonNull final String processClass, 315 final String niceName, 316 int uid, int gid, @Nullable int[] gids, 317 int runtimeFlags, int mountExternal, 318 int targetSdkVersion, 319 @Nullable String seInfo, 320 @NonNull String abi, 321 @Nullable String instructionSet, 322 @Nullable String appDataDir, 323 @Nullable String invokeWith, 324 @Nullable String packageName, 325 boolean useUsapPool, 326 @Nullable String[] zygoteArgs) { 327 // TODO (chriswailes): Is there a better place to check this value? 328 if (fetchUsapPoolEnabledPropWithMinInterval()) { 329 informZygotesOfUsapPoolStatus(); 330 } 331 332 try { 333 return startViaZygote(processClass, niceName, uid, gid, gids, 334 runtimeFlags, mountExternal, targetSdkVersion, seInfo, 335 abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false, 336 packageName, useUsapPool, zygoteArgs); 337 } catch (ZygoteStartFailedEx ex) { 338 Log.e(LOG_TAG, 339 "Starting VM process through Zygote failed"); 340 throw new RuntimeException( 341 "Starting VM process through Zygote failed", ex); 342 } 343 } 344 345 /** retry interval for opening a zygote socket */ 346 static final int ZYGOTE_RETRY_MILLIS = 500; 347 348 /** 349 * Queries the zygote for the list of ABIS it supports. 350 */ 351 @GuardedBy("mLock") getAbiList(BufferedWriter writer, DataInputStream inputStream)352 private static List<String> getAbiList(BufferedWriter writer, DataInputStream inputStream) 353 throws IOException { 354 // Each query starts with the argument count (1 in this case) 355 writer.write("1"); 356 // ... followed by a new-line. 357 writer.newLine(); 358 // ... followed by our only argument. 359 writer.write("--query-abi-list"); 360 writer.newLine(); 361 writer.flush(); 362 363 // The response is a length prefixed stream of ASCII bytes. 364 int numBytes = inputStream.readInt(); 365 byte[] bytes = new byte[numBytes]; 366 inputStream.readFully(bytes); 367 368 final String rawList = new String(bytes, StandardCharsets.US_ASCII); 369 370 return Arrays.asList(rawList.split(",")); 371 } 372 373 /** 374 * Sends an argument list to the zygote process, which starts a new child 375 * and returns the child's pid. Please note: the present implementation 376 * replaces newlines in the argument list with spaces. 377 * 378 * @throws ZygoteStartFailedEx if process start failed for any reason 379 */ 380 @GuardedBy("mLock") zygoteSendArgsAndGetResult( ZygoteState zygoteState, boolean useUsapPool, @NonNull ArrayList<String> args)381 private Process.ProcessStartResult zygoteSendArgsAndGetResult( 382 ZygoteState zygoteState, boolean useUsapPool, @NonNull ArrayList<String> args) 383 throws ZygoteStartFailedEx { 384 // Throw early if any of the arguments are malformed. This means we can 385 // avoid writing a partial response to the zygote. 386 for (String arg : args) { 387 // Making two indexOf calls here is faster than running a manually fused loop due 388 // to the fact that indexOf is a optimized intrinsic. 389 if (arg.indexOf('\n') >= 0) { 390 throw new ZygoteStartFailedEx("Embedded newlines not allowed"); 391 } else if (arg.indexOf('\r') >= 0) { 392 throw new ZygoteStartFailedEx("Embedded carriage returns not allowed"); 393 } 394 } 395 396 /* 397 * See com.android.internal.os.ZygoteArguments.parseArgs() 398 * Presently the wire format to the zygote process is: 399 * a) a count of arguments (argc, in essence) 400 * b) a number of newline-separated argument strings equal to count 401 * 402 * After the zygote process reads these it will write the pid of 403 * the child or -1 on failure, followed by boolean to 404 * indicate whether a wrapper process was used. 405 */ 406 String msgStr = args.size() + "\n" + String.join("\n", args) + "\n"; 407 408 if (useUsapPool && mUsapPoolEnabled && canAttemptUsap(args)) { 409 try { 410 return attemptUsapSendArgsAndGetResult(zygoteState, msgStr); 411 } catch (IOException ex) { 412 // If there was an IOException using the USAP pool we will log the error and 413 // attempt to start the process through the Zygote. 414 Log.e(LOG_TAG, "IO Exception while communicating with USAP pool - " 415 + ex.getMessage()); 416 } 417 } 418 419 return attemptZygoteSendArgsAndGetResult(zygoteState, msgStr); 420 } 421 attemptZygoteSendArgsAndGetResult( ZygoteState zygoteState, String msgStr)422 private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult( 423 ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx { 424 try { 425 final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter; 426 final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream; 427 428 zygoteWriter.write(msgStr); 429 zygoteWriter.flush(); 430 431 // Always read the entire result from the input stream to avoid leaving 432 // bytes in the stream for future process starts to accidentally stumble 433 // upon. 434 Process.ProcessStartResult result = new Process.ProcessStartResult(); 435 result.pid = zygoteInputStream.readInt(); 436 result.usingWrapper = zygoteInputStream.readBoolean(); 437 438 if (result.pid < 0) { 439 throw new ZygoteStartFailedEx("fork() failed"); 440 } 441 442 return result; 443 } catch (IOException ex) { 444 zygoteState.close(); 445 Log.e(LOG_TAG, "IO Exception while communicating with Zygote - " 446 + ex.toString()); 447 throw new ZygoteStartFailedEx(ex); 448 } 449 } 450 attemptUsapSendArgsAndGetResult( ZygoteState zygoteState, String msgStr)451 private Process.ProcessStartResult attemptUsapSendArgsAndGetResult( 452 ZygoteState zygoteState, String msgStr) 453 throws ZygoteStartFailedEx, IOException { 454 try (LocalSocket usapSessionSocket = zygoteState.getUsapSessionSocket()) { 455 final BufferedWriter usapWriter = 456 new BufferedWriter( 457 new OutputStreamWriter(usapSessionSocket.getOutputStream()), 458 Zygote.SOCKET_BUFFER_SIZE); 459 final DataInputStream usapReader = 460 new DataInputStream(usapSessionSocket.getInputStream()); 461 462 usapWriter.write(msgStr); 463 usapWriter.flush(); 464 465 Process.ProcessStartResult result = new Process.ProcessStartResult(); 466 result.pid = usapReader.readInt(); 467 // USAPs can't be used to spawn processes that need wrappers. 468 result.usingWrapper = false; 469 470 if (result.pid >= 0) { 471 return result; 472 } else { 473 throw new ZygoteStartFailedEx("USAP specialization failed"); 474 } 475 } 476 } 477 478 /** 479 * Flags that may not be passed to a USAP. 480 */ 481 private static final String[] INVALID_USAP_FLAGS = { 482 "--query-abi-list", 483 "--get-pid", 484 "--preload-default", 485 "--preload-package", 486 "--preload-app", 487 "--start-child-zygote", 488 "--set-api-blacklist-exemptions", 489 "--hidden-api-log-sampling-rate", 490 "--hidden-api-statslog-sampling-rate", 491 "--invoke-with" 492 }; 493 494 /** 495 * Tests a command list to see if it is valid to send to a USAP. 496 * @param args Zygote/USAP command arguments 497 * @return True if the command can be passed to a USAP; false otherwise 498 */ canAttemptUsap(ArrayList<String> args)499 private static boolean canAttemptUsap(ArrayList<String> args) { 500 for (String flag : args) { 501 for (String badFlag : INVALID_USAP_FLAGS) { 502 if (flag.startsWith(badFlag)) { 503 return false; 504 } 505 } 506 if (flag.startsWith("--nice-name=")) { 507 // Check if the wrap property is set, usap would ignore it. 508 String niceName = flag.substring(12); 509 String property_value = SystemProperties.get("wrap." + niceName); 510 if (property_value != null && property_value.length() != 0) { 511 return false; 512 } 513 } 514 } 515 516 return true; 517 } 518 519 /** 520 * Starts a new process via the zygote mechanism. 521 * 522 * @param processClass Class name whose static main() to run 523 * @param niceName 'nice' process name to appear in ps 524 * @param uid a POSIX uid that the new process should setuid() to 525 * @param gid a POSIX gid that the new process shuold setgid() to 526 * @param gids null-ok; a list of supplementary group IDs that the 527 * new process should setgroup() to. 528 * @param runtimeFlags Additional flags for the runtime. 529 * @param targetSdkVersion The target SDK version for the app. 530 * @param seInfo null-ok SELinux information for the new process. 531 * @param abi the ABI the process should use. 532 * @param instructionSet null-ok the instruction set to use. 533 * @param appDataDir null-ok the data directory of the app. 534 * @param startChildZygote Start a sub-zygote. This creates a new zygote process 535 * that has its state cloned from this zygote process. 536 * @param packageName null-ok the name of the package this process belongs to. 537 * @param extraArgs Additional arguments to supply to the zygote process. 538 * @return An object that describes the result of the attempt to start the process. 539 * @throws ZygoteStartFailedEx if process start failed for any reason 540 */ startViaZygote(@onNull final String processClass, @Nullable final String niceName, final int uid, final int gid, @Nullable final int[] gids, int runtimeFlags, int mountExternal, int targetSdkVersion, @Nullable String seInfo, @NonNull String abi, @Nullable String instructionSet, @Nullable String appDataDir, @Nullable String invokeWith, boolean startChildZygote, @Nullable String packageName, boolean useUsapPool, @Nullable String[] extraArgs)541 private Process.ProcessStartResult startViaZygote(@NonNull final String processClass, 542 @Nullable final String niceName, 543 final int uid, final int gid, 544 @Nullable final int[] gids, 545 int runtimeFlags, int mountExternal, 546 int targetSdkVersion, 547 @Nullable String seInfo, 548 @NonNull String abi, 549 @Nullable String instructionSet, 550 @Nullable String appDataDir, 551 @Nullable String invokeWith, 552 boolean startChildZygote, 553 @Nullable String packageName, 554 boolean useUsapPool, 555 @Nullable String[] extraArgs) 556 throws ZygoteStartFailedEx { 557 ArrayList<String> argsForZygote = new ArrayList<>(); 558 559 // --runtime-args, --setuid=, --setgid=, 560 // and --setgroups= must go first 561 argsForZygote.add("--runtime-args"); 562 argsForZygote.add("--setuid=" + uid); 563 argsForZygote.add("--setgid=" + gid); 564 argsForZygote.add("--runtime-flags=" + runtimeFlags); 565 if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) { 566 argsForZygote.add("--mount-external-default"); 567 } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) { 568 argsForZygote.add("--mount-external-read"); 569 } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) { 570 argsForZygote.add("--mount-external-write"); 571 } else if (mountExternal == Zygote.MOUNT_EXTERNAL_FULL) { 572 argsForZygote.add("--mount-external-full"); 573 } else if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER) { 574 argsForZygote.add("--mount-external-installer"); 575 } else if (mountExternal == Zygote.MOUNT_EXTERNAL_LEGACY) { 576 argsForZygote.add("--mount-external-legacy"); 577 } 578 579 argsForZygote.add("--target-sdk-version=" + targetSdkVersion); 580 581 // --setgroups is a comma-separated list 582 if (gids != null && gids.length > 0) { 583 StringBuilder sb = new StringBuilder(); 584 sb.append("--setgroups="); 585 586 int sz = gids.length; 587 for (int i = 0; i < sz; i++) { 588 if (i != 0) { 589 sb.append(','); 590 } 591 sb.append(gids[i]); 592 } 593 594 argsForZygote.add(sb.toString()); 595 } 596 597 if (niceName != null) { 598 argsForZygote.add("--nice-name=" + niceName); 599 } 600 601 if (seInfo != null) { 602 argsForZygote.add("--seinfo=" + seInfo); 603 } 604 605 if (instructionSet != null) { 606 argsForZygote.add("--instruction-set=" + instructionSet); 607 } 608 609 if (appDataDir != null) { 610 argsForZygote.add("--app-data-dir=" + appDataDir); 611 } 612 613 if (invokeWith != null) { 614 argsForZygote.add("--invoke-with"); 615 argsForZygote.add(invokeWith); 616 } 617 618 if (startChildZygote) { 619 argsForZygote.add("--start-child-zygote"); 620 } 621 622 if (packageName != null) { 623 argsForZygote.add("--package-name=" + packageName); 624 } 625 626 argsForZygote.add(processClass); 627 628 if (extraArgs != null) { 629 Collections.addAll(argsForZygote, extraArgs); 630 } 631 632 synchronized(mLock) { 633 // The USAP pool can not be used if the application will not use the systems graphics 634 // driver. If that driver is requested use the Zygote application start path. 635 return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), 636 useUsapPool, 637 argsForZygote); 638 } 639 } 640 fetchUsapPoolEnabledProp()641 private boolean fetchUsapPoolEnabledProp() { 642 boolean origVal = mUsapPoolEnabled; 643 644 final String propertyString = Zygote.getConfigurationProperty( 645 ZygoteConfig.USAP_POOL_ENABLED, USAP_POOL_ENABLED_DEFAULT); 646 647 if (!propertyString.isEmpty()) { 648 mUsapPoolEnabled = Zygote.getConfigurationPropertyBoolean( 649 ZygoteConfig.USAP_POOL_ENABLED, 650 Boolean.parseBoolean(USAP_POOL_ENABLED_DEFAULT)); 651 } 652 653 boolean valueChanged = origVal != mUsapPoolEnabled; 654 655 if (valueChanged) { 656 Log.i(LOG_TAG, "usapPoolEnabled = " + mUsapPoolEnabled); 657 } 658 659 return valueChanged; 660 } 661 662 private boolean mIsFirstPropCheck = true; 663 private long mLastPropCheckTimestamp = 0; 664 fetchUsapPoolEnabledPropWithMinInterval()665 private boolean fetchUsapPoolEnabledPropWithMinInterval() { 666 final long currentTimestamp = SystemClock.elapsedRealtime(); 667 668 if (SystemProperties.get("dalvik.vm.boot-image", "").endsWith("apex.art")) { 669 // TODO(b/119800099): In jitzygote mode, we want to start using USAP processes 670 // only once the boot classpath has been compiled. There is currently no callback 671 // from the runtime to notify the zygote about end of compilation, so for now just 672 // arbitrarily start USAP processes 15 seconds after boot. 673 if (currentTimestamp <= 15000) { 674 return false; 675 } 676 } 677 678 if (mIsFirstPropCheck 679 || (currentTimestamp - mLastPropCheckTimestamp >= Zygote.PROPERTY_CHECK_INTERVAL)) { 680 mIsFirstPropCheck = false; 681 mLastPropCheckTimestamp = currentTimestamp; 682 return fetchUsapPoolEnabledProp(); 683 } 684 685 return false; 686 } 687 688 /** 689 * Closes the connections to the zygote, if they exist. 690 */ close()691 public void close() { 692 if (primaryZygoteState != null) { 693 primaryZygoteState.close(); 694 } 695 if (secondaryZygoteState != null) { 696 secondaryZygoteState.close(); 697 } 698 } 699 700 /** 701 * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block 702 * and retry if the zygote is unresponsive. This method is a no-op if a connection is 703 * already open. 704 */ establishZygoteConnectionForAbi(String abi)705 public void establishZygoteConnectionForAbi(String abi) { 706 try { 707 synchronized(mLock) { 708 openZygoteSocketIfNeeded(abi); 709 } 710 } catch (ZygoteStartFailedEx ex) { 711 throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex); 712 } 713 } 714 715 /** 716 * Attempt to retrieve the PID of the zygote serving the given abi. 717 */ getZygotePid(String abi)718 public int getZygotePid(String abi) { 719 try { 720 synchronized (mLock) { 721 ZygoteState state = openZygoteSocketIfNeeded(abi); 722 723 // Each query starts with the argument count (1 in this case) 724 state.mZygoteOutputWriter.write("1"); 725 // ... followed by a new-line. 726 state.mZygoteOutputWriter.newLine(); 727 // ... followed by our only argument. 728 state.mZygoteOutputWriter.write("--get-pid"); 729 state.mZygoteOutputWriter.newLine(); 730 state.mZygoteOutputWriter.flush(); 731 732 // The response is a length prefixed stream of ASCII bytes. 733 int numBytes = state.mZygoteInputStream.readInt(); 734 byte[] bytes = new byte[numBytes]; 735 state.mZygoteInputStream.readFully(bytes); 736 737 return Integer.parseInt(new String(bytes, StandardCharsets.US_ASCII)); 738 } 739 } catch (Exception ex) { 740 throw new RuntimeException("Failure retrieving pid", ex); 741 } 742 } 743 744 /** 745 * Push hidden API blacklisting exemptions into the zygote process(es). 746 * 747 * <p>The list of exemptions will take affect for all new processes forked from the zygote after 748 * this call. 749 * 750 * @param exemptions List of hidden API exemption prefixes. Any matching members are treated as 751 * whitelisted/public APIs (i.e. allowed, no logging of usage). 752 */ setApiBlacklistExemptions(List<String> exemptions)753 public boolean setApiBlacklistExemptions(List<String> exemptions) { 754 synchronized (mLock) { 755 mApiBlacklistExemptions = exemptions; 756 boolean ok = maybeSetApiBlacklistExemptions(primaryZygoteState, true); 757 if (ok) { 758 ok = maybeSetApiBlacklistExemptions(secondaryZygoteState, true); 759 } 760 return ok; 761 } 762 } 763 764 /** 765 * Set the precentage of detected hidden API accesses that are logged to the event log. 766 * 767 * <p>This rate will take affect for all new processes forked from the zygote after this call. 768 * 769 * @param rate An integer between 0 and 0x10000 inclusive. 0 means no event logging. 770 */ setHiddenApiAccessLogSampleRate(int rate)771 public void setHiddenApiAccessLogSampleRate(int rate) { 772 synchronized (mLock) { 773 mHiddenApiAccessLogSampleRate = rate; 774 maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState); 775 maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState); 776 } 777 } 778 779 /** 780 * Set the precentage of detected hidden API accesses that are logged to the new event log. 781 * 782 * <p>This rate will take affect for all new processes forked from the zygote after this call. 783 * 784 * @param rate An integer between 0 and 0x10000 inclusive. 0 means no event logging. 785 */ setHiddenApiAccessStatslogSampleRate(int rate)786 public void setHiddenApiAccessStatslogSampleRate(int rate) { 787 synchronized (mLock) { 788 mHiddenApiAccessStatslogSampleRate = rate; 789 maybeSetHiddenApiAccessStatslogSampleRate(primaryZygoteState); 790 maybeSetHiddenApiAccessStatslogSampleRate(secondaryZygoteState); 791 } 792 } 793 794 @GuardedBy("mLock") maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty)795 private boolean maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) { 796 if (state == null || state.isClosed()) { 797 Slog.e(LOG_TAG, "Can't set API blacklist exemptions: no zygote connection"); 798 return false; 799 } else if (!sendIfEmpty && mApiBlacklistExemptions.isEmpty()) { 800 return true; 801 } 802 803 try { 804 state.mZygoteOutputWriter.write(Integer.toString(mApiBlacklistExemptions.size() + 1)); 805 state.mZygoteOutputWriter.newLine(); 806 state.mZygoteOutputWriter.write("--set-api-blacklist-exemptions"); 807 state.mZygoteOutputWriter.newLine(); 808 for (int i = 0; i < mApiBlacklistExemptions.size(); ++i) { 809 state.mZygoteOutputWriter.write(mApiBlacklistExemptions.get(i)); 810 state.mZygoteOutputWriter.newLine(); 811 } 812 state.mZygoteOutputWriter.flush(); 813 int status = state.mZygoteInputStream.readInt(); 814 if (status != 0) { 815 Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status); 816 } 817 return true; 818 } catch (IOException ioe) { 819 Slog.e(LOG_TAG, "Failed to set API blacklist exemptions", ioe); 820 mApiBlacklistExemptions = Collections.emptyList(); 821 return false; 822 } 823 } 824 maybeSetHiddenApiAccessLogSampleRate(ZygoteState state)825 private void maybeSetHiddenApiAccessLogSampleRate(ZygoteState state) { 826 if (state == null || state.isClosed() || mHiddenApiAccessLogSampleRate == -1) { 827 return; 828 } 829 830 try { 831 state.mZygoteOutputWriter.write(Integer.toString(1)); 832 state.mZygoteOutputWriter.newLine(); 833 state.mZygoteOutputWriter.write("--hidden-api-log-sampling-rate=" 834 + mHiddenApiAccessLogSampleRate); 835 state.mZygoteOutputWriter.newLine(); 836 state.mZygoteOutputWriter.flush(); 837 int status = state.mZygoteInputStream.readInt(); 838 if (status != 0) { 839 Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate; status " + status); 840 } 841 } catch (IOException ioe) { 842 Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate", ioe); 843 } 844 } 845 maybeSetHiddenApiAccessStatslogSampleRate(ZygoteState state)846 private void maybeSetHiddenApiAccessStatslogSampleRate(ZygoteState state) { 847 if (state == null || state.isClosed() || mHiddenApiAccessStatslogSampleRate == -1) { 848 return; 849 } 850 851 try { 852 state.mZygoteOutputWriter.write(Integer.toString(1)); 853 state.mZygoteOutputWriter.newLine(); 854 state.mZygoteOutputWriter.write("--hidden-api-statslog-sampling-rate=" 855 + mHiddenApiAccessStatslogSampleRate); 856 state.mZygoteOutputWriter.newLine(); 857 state.mZygoteOutputWriter.flush(); 858 int status = state.mZygoteInputStream.readInt(); 859 if (status != 0) { 860 Slog.e(LOG_TAG, "Failed to set hidden API statslog sampling rate; status " 861 + status); 862 } 863 } catch (IOException ioe) { 864 Slog.e(LOG_TAG, "Failed to set hidden API statslog sampling rate", ioe); 865 } 866 } 867 868 /** 869 * Creates a ZygoteState for the primary zygote if it doesn't exist or has been disconnected. 870 */ 871 @GuardedBy("mLock") attemptConnectionToPrimaryZygote()872 private void attemptConnectionToPrimaryZygote() throws IOException { 873 if (primaryZygoteState == null || primaryZygoteState.isClosed()) { 874 primaryZygoteState = 875 ZygoteState.connect(mZygoteSocketAddress, mUsapPoolSocketAddress); 876 877 maybeSetApiBlacklistExemptions(primaryZygoteState, false); 878 maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState); 879 maybeSetHiddenApiAccessStatslogSampleRate(primaryZygoteState); 880 } 881 } 882 883 /** 884 * Creates a ZygoteState for the secondary zygote if it doesn't exist or has been disconnected. 885 */ 886 @GuardedBy("mLock") attemptConnectionToSecondaryZygote()887 private void attemptConnectionToSecondaryZygote() throws IOException { 888 if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) { 889 secondaryZygoteState = 890 ZygoteState.connect(mZygoteSecondarySocketAddress, 891 mUsapPoolSecondarySocketAddress); 892 893 maybeSetApiBlacklistExemptions(secondaryZygoteState, false); 894 maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState); 895 maybeSetHiddenApiAccessStatslogSampleRate(secondaryZygoteState); 896 } 897 } 898 899 /** 900 * Tries to open a session socket to a Zygote process with a compatible ABI if one is not 901 * already open. If a compatible session socket is already open that session socket is returned. 902 * This function may block and may have to try connecting to multiple Zygotes to find the 903 * appropriate one. Requires that mLock be held. 904 */ 905 @GuardedBy("mLock") openZygoteSocketIfNeeded(String abi)906 private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx { 907 try { 908 attemptConnectionToPrimaryZygote(); 909 910 if (primaryZygoteState.matches(abi)) { 911 return primaryZygoteState; 912 } 913 914 if (mZygoteSecondarySocketAddress != null) { 915 // The primary zygote didn't match. Try the secondary. 916 attemptConnectionToSecondaryZygote(); 917 918 if (secondaryZygoteState.matches(abi)) { 919 return secondaryZygoteState; 920 } 921 } 922 } catch (IOException ioe) { 923 throw new ZygoteStartFailedEx("Error connecting to zygote", ioe); 924 } 925 926 throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi); 927 } 928 929 /** 930 * Instructs the zygote to pre-load the application code for the given Application. 931 * Only the app zygote supports this function. 932 * TODO preloadPackageForAbi() can probably be removed and the callers an use this instead. 933 */ preloadApp(ApplicationInfo appInfo, String abi)934 public boolean preloadApp(ApplicationInfo appInfo, String abi) 935 throws ZygoteStartFailedEx, IOException { 936 synchronized (mLock) { 937 ZygoteState state = openZygoteSocketIfNeeded(abi); 938 state.mZygoteOutputWriter.write("2"); 939 state.mZygoteOutputWriter.newLine(); 940 941 state.mZygoteOutputWriter.write("--preload-app"); 942 state.mZygoteOutputWriter.newLine(); 943 944 // Zygote args needs to be strings, so in order to pass ApplicationInfo, 945 // write it to a Parcel, and base64 the raw Parcel bytes to the other side. 946 Parcel parcel = Parcel.obtain(); 947 appInfo.writeToParcel(parcel, 0 /* flags */); 948 String encodedParcelData = Base64.getEncoder().encodeToString(parcel.marshall()); 949 parcel.recycle(); 950 state.mZygoteOutputWriter.write(encodedParcelData); 951 state.mZygoteOutputWriter.newLine(); 952 953 state.mZygoteOutputWriter.flush(); 954 955 return (state.mZygoteInputStream.readInt() == 0); 956 } 957 } 958 959 /** 960 * Instructs the zygote to pre-load the classes and native libraries at the given paths 961 * for the specified abi. Not all zygotes support this function. 962 */ preloadPackageForAbi( String packagePath, String libsPath, String libFileName, String cacheKey, String abi)963 public boolean preloadPackageForAbi( 964 String packagePath, String libsPath, String libFileName, String cacheKey, String abi) 965 throws ZygoteStartFailedEx, IOException { 966 synchronized (mLock) { 967 ZygoteState state = openZygoteSocketIfNeeded(abi); 968 state.mZygoteOutputWriter.write("5"); 969 state.mZygoteOutputWriter.newLine(); 970 971 state.mZygoteOutputWriter.write("--preload-package"); 972 state.mZygoteOutputWriter.newLine(); 973 974 state.mZygoteOutputWriter.write(packagePath); 975 state.mZygoteOutputWriter.newLine(); 976 977 state.mZygoteOutputWriter.write(libsPath); 978 state.mZygoteOutputWriter.newLine(); 979 980 state.mZygoteOutputWriter.write(libFileName); 981 state.mZygoteOutputWriter.newLine(); 982 983 state.mZygoteOutputWriter.write(cacheKey); 984 state.mZygoteOutputWriter.newLine(); 985 986 state.mZygoteOutputWriter.flush(); 987 988 return (state.mZygoteInputStream.readInt() == 0); 989 } 990 } 991 992 /** 993 * Instructs the zygote to preload the default set of classes and resources. Returns 994 * {@code true} if a preload was performed as a result of this call, and {@code false} 995 * otherwise. The latter usually means that the zygote eagerly preloaded at startup 996 * or due to a previous call to {@code preloadDefault}. Note that this call is synchronous. 997 */ preloadDefault(String abi)998 public boolean preloadDefault(String abi) throws ZygoteStartFailedEx, IOException { 999 synchronized (mLock) { 1000 ZygoteState state = openZygoteSocketIfNeeded(abi); 1001 // Each query starts with the argument count (1 in this case) 1002 state.mZygoteOutputWriter.write("1"); 1003 state.mZygoteOutputWriter.newLine(); 1004 state.mZygoteOutputWriter.write("--preload-default"); 1005 state.mZygoteOutputWriter.newLine(); 1006 state.mZygoteOutputWriter.flush(); 1007 1008 return (state.mZygoteInputStream.readInt() == 0); 1009 } 1010 } 1011 1012 /** 1013 * Try connecting to the Zygote over and over again until we hit a time-out. 1014 * @param zygoteSocketName The name of the socket to connect to. 1015 */ waitForConnectionToZygote(String zygoteSocketName)1016 public static void waitForConnectionToZygote(String zygoteSocketName) { 1017 final LocalSocketAddress zygoteSocketAddress = 1018 new LocalSocketAddress(zygoteSocketName, LocalSocketAddress.Namespace.RESERVED); 1019 waitForConnectionToZygote(zygoteSocketAddress); 1020 } 1021 1022 /** 1023 * Try connecting to the Zygote over and over again until we hit a time-out. 1024 * @param zygoteSocketAddress The name of the socket to connect to. 1025 */ waitForConnectionToZygote(LocalSocketAddress zygoteSocketAddress)1026 public static void waitForConnectionToZygote(LocalSocketAddress zygoteSocketAddress) { 1027 int numRetries = ZYGOTE_CONNECT_TIMEOUT_MS / ZYGOTE_CONNECT_RETRY_DELAY_MS; 1028 for (int n = numRetries; n >= 0; n--) { 1029 try { 1030 final ZygoteState zs = 1031 ZygoteState.connect(zygoteSocketAddress, null); 1032 zs.close(); 1033 return; 1034 } catch (IOException ioe) { 1035 Log.w(LOG_TAG, 1036 "Got error connecting to zygote, retrying. msg= " + ioe.getMessage()); 1037 } 1038 1039 try { 1040 Thread.sleep(ZYGOTE_CONNECT_RETRY_DELAY_MS); 1041 } catch (InterruptedException ignored) { } 1042 } 1043 Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " 1044 + zygoteSocketAddress.getName()); 1045 } 1046 1047 /** 1048 * Sends messages to the zygotes telling them to change the status of their USAP pools. If 1049 * this notification fails the ZygoteProcess will fall back to the previous behavior. 1050 */ informZygotesOfUsapPoolStatus()1051 private void informZygotesOfUsapPoolStatus() { 1052 final String command = "1\n--usap-pool-enabled=" + mUsapPoolEnabled + "\n"; 1053 1054 synchronized (mLock) { 1055 try { 1056 attemptConnectionToPrimaryZygote(); 1057 1058 primaryZygoteState.mZygoteOutputWriter.write(command); 1059 primaryZygoteState.mZygoteOutputWriter.flush(); 1060 } catch (IOException ioe) { 1061 mUsapPoolEnabled = !mUsapPoolEnabled; 1062 Log.w(LOG_TAG, "Failed to inform zygotes of USAP pool status: " 1063 + ioe.getMessage()); 1064 return; 1065 } 1066 1067 if (mZygoteSecondarySocketAddress != null) { 1068 try { 1069 attemptConnectionToSecondaryZygote(); 1070 1071 try { 1072 secondaryZygoteState.mZygoteOutputWriter.write(command); 1073 secondaryZygoteState.mZygoteOutputWriter.flush(); 1074 1075 // Wait for the secondary Zygote to finish its work. 1076 secondaryZygoteState.mZygoteInputStream.readInt(); 1077 } catch (IOException ioe) { 1078 throw new IllegalStateException( 1079 "USAP pool state change cause an irrecoverable error", 1080 ioe); 1081 } 1082 } catch (IOException ioe) { 1083 // No secondary zygote present. This is expected on some devices. 1084 } 1085 } 1086 1087 // Wait for the response from the primary zygote here so the primary/secondary zygotes 1088 // can work concurrently. 1089 try { 1090 // Wait for the primary zygote to finish its work. 1091 primaryZygoteState.mZygoteInputStream.readInt(); 1092 } catch (IOException ioe) { 1093 throw new IllegalStateException( 1094 "USAP pool state change cause an irrecoverable error", 1095 ioe); 1096 } 1097 } 1098 } 1099 1100 /** 1101 * Starts a new zygote process as a child of this zygote. This is used to create 1102 * secondary zygotes that inherit data from the zygote that this object 1103 * communicates with. This returns a new ZygoteProcess representing a connection 1104 * to the newly created zygote. Throws an exception if the zygote cannot be started. 1105 * 1106 * @param processClass The class to use as the child zygote's main entry 1107 * point. 1108 * @param niceName A more readable name to use for the process. 1109 * @param uid The user-id under which the child zygote will run. 1110 * @param gid The group-id under which the child zygote will run. 1111 * @param gids Additional group-ids associated with the child zygote process. 1112 * @param runtimeFlags Additional flags. 1113 * @param seInfo null-ok SELinux information for the child zygote process. 1114 * @param abi non-null the ABI of the child zygote 1115 * @param acceptedAbiList ABIs this child zygote will accept connections for; this 1116 * may be different from <code>abi</code> in case the children 1117 * spawned from this Zygote only communicate using ABI-safe methods. 1118 * @param instructionSet null-ok the instruction set to use. 1119 * @param uidRangeStart The first UID in the range the child zygote may setuid()/setgid() to 1120 * @param uidRangeEnd The last UID in the range the child zygote may setuid()/setgid() to 1121 */ startChildZygote(final String processClass, final String niceName, int uid, int gid, int[] gids, int runtimeFlags, String seInfo, String abi, String acceptedAbiList, String instructionSet, int uidRangeStart, int uidRangeEnd)1122 public ChildZygoteProcess startChildZygote(final String processClass, 1123 final String niceName, 1124 int uid, int gid, int[] gids, 1125 int runtimeFlags, 1126 String seInfo, 1127 String abi, 1128 String acceptedAbiList, 1129 String instructionSet, 1130 int uidRangeStart, 1131 int uidRangeEnd) { 1132 // Create an unguessable address in the global abstract namespace. 1133 final LocalSocketAddress serverAddress = new LocalSocketAddress( 1134 processClass + "/" + UUID.randomUUID().toString()); 1135 1136 final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName(), 1137 Zygote.CHILD_ZYGOTE_ABI_LIST_ARG + acceptedAbiList, 1138 Zygote.CHILD_ZYGOTE_UID_RANGE_START + uidRangeStart, 1139 Zygote.CHILD_ZYGOTE_UID_RANGE_END + uidRangeEnd}; 1140 1141 Process.ProcessStartResult result; 1142 try { 1143 result = startViaZygote(processClass, niceName, uid, gid, 1144 gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo, 1145 abi, instructionSet, null /* appDataDir */, null /* invokeWith */, 1146 true /* startChildZygote */, null /* packageName */, 1147 false /* useUsapPool */, extraArgs); 1148 } catch (ZygoteStartFailedEx ex) { 1149 throw new RuntimeException("Starting child-zygote through Zygote failed", ex); 1150 } 1151 1152 return new ChildZygoteProcess(serverAddress, result.pid); 1153 } 1154 } 1155