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