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