blob: 670f7949dd1992c87c6e35d235a201e95b407451 [file] [log] [blame]
Robert Sesek8f8d1872016-03-18 16:52:57 -04001/*
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;
Gustav Senntonf0c52b52017-04-27 17:00:50 +010022import android.util.Slog;
Nicolas Geoffray81edac42017-09-07 14:13:29 +010023
Robert Sesekded20982016-08-15 13:59:13 -040024import com.android.internal.annotations.GuardedBy;
Robert Sesek8f8d1872016-03-18 16:52:57 -040025import com.android.internal.os.Zygote;
Robert Sesekded20982016-08-15 13:59:13 -040026import com.android.internal.util.Preconditions;
Nicolas Geoffray81edac42017-09-07 14:13:29 +010027
Robert Sesek8f8d1872016-03-18 16:52:57 -040028import 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.List;
36
37/*package*/ class ZygoteStartFailedEx extends Exception {
38 ZygoteStartFailedEx(String s) {
39 super(s);
40 }
41
42 ZygoteStartFailedEx(Throwable cause) {
43 super(cause);
44 }
45
46 ZygoteStartFailedEx(String s, Throwable cause) {
47 super(s, cause);
48 }
49}
50
51/**
52 * Maintains communication state with the zygote processes. This class is responsible
53 * for the sockets opened to the zygotes and for starting processes on behalf of the
54 * {@link android.os.Process} class.
55 *
56 * {@hide}
57 */
58public class ZygoteProcess {
59 private static final String LOG_TAG = "ZygoteProcess";
60
61 /**
62 * The name of the socket used to communicate with the primary zygote.
63 */
64 private final String mSocket;
65
66 /**
67 * The name of the secondary (alternate ABI) zygote socket.
68 */
69 private final String mSecondarySocket;
70
71 public ZygoteProcess(String primarySocket, String secondarySocket) {
72 mSocket = primarySocket;
73 mSecondarySocket = secondarySocket;
74 }
75
76 /**
77 * State for communicating with the zygote process.
78 */
79 public static class ZygoteState {
80 final LocalSocket socket;
81 final DataInputStream inputStream;
82 final BufferedWriter writer;
83 final List<String> abiList;
84
85 boolean mClosed;
86
87 private ZygoteState(LocalSocket socket, DataInputStream inputStream,
88 BufferedWriter writer, List<String> abiList) {
89 this.socket = socket;
90 this.inputStream = inputStream;
91 this.writer = writer;
92 this.abiList = abiList;
93 }
94
95 public static ZygoteState connect(String socketAddress) throws IOException {
96 DataInputStream zygoteInputStream = null;
97 BufferedWriter zygoteWriter = null;
98 final LocalSocket zygoteSocket = new LocalSocket();
99
100 try {
101 zygoteSocket.connect(new LocalSocketAddress(socketAddress,
102 LocalSocketAddress.Namespace.RESERVED));
103
104 zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
105
106 zygoteWriter = new BufferedWriter(new OutputStreamWriter(
107 zygoteSocket.getOutputStream()), 256);
108 } catch (IOException ex) {
109 try {
110 zygoteSocket.close();
111 } catch (IOException ignore) {
112 }
113
114 throw ex;
115 }
116
117 String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
Robert Sesekded20982016-08-15 13:59:13 -0400118 Log.i("Zygote", "Process: zygote socket " + socketAddress + " opened, supported ABIS: "
119 + abiListString);
Robert Sesek8f8d1872016-03-18 16:52:57 -0400120
121 return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
122 Arrays.asList(abiListString.split(",")));
123 }
124
125 boolean matches(String abi) {
126 return abiList.contains(abi);
127 }
128
129 public void close() {
130 try {
131 socket.close();
132 } catch (IOException ex) {
133 Log.e(LOG_TAG,"I/O exception on routine close", ex);
134 }
135
136 mClosed = true;
137 }
138
139 boolean isClosed() {
140 return mClosed;
141 }
142 }
143
144 /**
Robert Sesekded20982016-08-15 13:59:13 -0400145 * Lock object to protect access to the two ZygoteStates below. This lock must be
146 * acquired while communicating over the ZygoteState's socket, to prevent
147 * interleaved access.
148 */
149 private final Object mLock = new Object();
150
151 /**
Robert Sesek8f8d1872016-03-18 16:52:57 -0400152 * The state of the connection to the primary zygote.
153 */
154 private ZygoteState primaryZygoteState;
155
156 /**
157 * The state of the connection to the secondary zygote.
158 */
159 private ZygoteState secondaryZygoteState;
160
161 /**
162 * Start a new process.
163 *
164 * <p>If processes are enabled, a new process is created and the
165 * static main() function of a <var>processClass</var> is executed there.
166 * The process will continue running after this function returns.
167 *
168 * <p>If processes are not enabled, a new thread in the caller's
169 * process is created and main() of <var>processClass</var> called there.
170 *
171 * <p>The niceName parameter, if not an empty string, is a custom name to
172 * give to the process instead of using processClass. This allows you to
173 * make easily identifyable processes even if you are using the same base
174 * <var>processClass</var> to start them.
175 *
Tamas Berghammerb8f7c352016-11-11 16:08:26 +0000176 * When invokeWith is not null, the process will be started as a fresh app
Tamas Berghammer0ca16fa2016-11-11 16:08:26 +0000177 * and not a zygote fork. Note that this is only allowed for uid 0 or when
Nicolas Geoffray81edac42017-09-07 14:13:29 +0100178 * runtimeFlags contains DEBUG_ENABLE_DEBUGGER.
Tamas Berghammerb8f7c352016-11-11 16:08:26 +0000179 *
Robert Sesek8f8d1872016-03-18 16:52:57 -0400180 * @param processClass The class to use as the process's main entry
181 * point.
182 * @param niceName A more readable name to use for the process.
183 * @param uid The user-id under which the process will run.
184 * @param gid The group-id under which the process will run.
185 * @param gids Additional group-ids associated with the process.
Nicolas Geoffray81edac42017-09-07 14:13:29 +0100186 * @param runtimeFlags Additional flags.
Robert Sesek8f8d1872016-03-18 16:52:57 -0400187 * @param targetSdkVersion The target SDK version for the app.
188 * @param seInfo null-ok SELinux information for the new process.
189 * @param abi non-null the ABI this app should be started with.
190 * @param instructionSet null-ok the instruction set to use.
191 * @param appDataDir null-ok the data directory of the app.
Tamas Berghammerb8f7c352016-11-11 16:08:26 +0000192 * @param invokeWith null-ok the command to invoke with.
Robert Sesek8f8d1872016-03-18 16:52:57 -0400193 * @param zygoteArgs Additional arguments to supply to the zygote process.
194 *
195 * @return An object that describes the result of the attempt to start the process.
196 * @throws RuntimeException on fatal start failure
197 */
198 public final Process.ProcessStartResult start(final String processClass,
199 final String niceName,
200 int uid, int gid, int[] gids,
Nicolas Geoffray81edac42017-09-07 14:13:29 +0100201 int runtimeFlags, int mountExternal,
Robert Sesek8f8d1872016-03-18 16:52:57 -0400202 int targetSdkVersion,
203 String seInfo,
204 String abi,
205 String instructionSet,
206 String appDataDir,
Tamas Berghammerb8f7c352016-11-11 16:08:26 +0000207 String invokeWith,
Robert Sesek8f8d1872016-03-18 16:52:57 -0400208 String[] zygoteArgs) {
209 try {
210 return startViaZygote(processClass, niceName, uid, gid, gids,
Nicolas Geoffray81edac42017-09-07 14:13:29 +0100211 runtimeFlags, mountExternal, targetSdkVersion, seInfo,
Tamas Berghammerb8f7c352016-11-11 16:08:26 +0000212 abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
Robert Sesek8f8d1872016-03-18 16:52:57 -0400213 } catch (ZygoteStartFailedEx ex) {
214 Log.e(LOG_TAG,
215 "Starting VM process through Zygote failed");
216 throw new RuntimeException(
217 "Starting VM process through Zygote failed", ex);
218 }
219 }
220
221 /** retry interval for opening a zygote socket */
222 static final int ZYGOTE_RETRY_MILLIS = 500;
223
224 /**
225 * Queries the zygote for the list of ABIS it supports.
226 *
227 * @throws ZygoteStartFailedEx if the query failed.
228 */
Robert Sesekded20982016-08-15 13:59:13 -0400229 @GuardedBy("mLock")
Robert Sesek8f8d1872016-03-18 16:52:57 -0400230 private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
231 throws IOException {
232 // Each query starts with the argument count (1 in this case)
233 writer.write("1");
234 // ... followed by a new-line.
235 writer.newLine();
236 // ... followed by our only argument.
237 writer.write("--query-abi-list");
238 writer.newLine();
239 writer.flush();
240
241 // The response is a length prefixed stream of ASCII bytes.
242 int numBytes = inputStream.readInt();
243 byte[] bytes = new byte[numBytes];
244 inputStream.readFully(bytes);
245
246 return new String(bytes, StandardCharsets.US_ASCII);
247 }
248
249 /**
250 * Sends an argument list to the zygote process, which starts a new child
251 * and returns the child's pid. Please note: the present implementation
252 * replaces newlines in the argument list with spaces.
253 *
254 * @throws ZygoteStartFailedEx if process start failed for any reason
255 */
Robert Sesekded20982016-08-15 13:59:13 -0400256 @GuardedBy("mLock")
Robert Sesek8f8d1872016-03-18 16:52:57 -0400257 private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
258 ZygoteState zygoteState, ArrayList<String> args)
259 throws ZygoteStartFailedEx {
260 try {
Robert Sesek0b58f192016-10-10 18:34:42 -0400261 // Throw early if any of the arguments are malformed. This means we can
262 // avoid writing a partial response to the zygote.
263 int sz = args.size();
264 for (int i = 0; i < sz; i++) {
265 if (args.get(i).indexOf('\n') >= 0) {
266 throw new ZygoteStartFailedEx("embedded newlines not allowed");
267 }
268 }
269
Robert Sesek8f8d1872016-03-18 16:52:57 -0400270 /**
271 * See com.android.internal.os.SystemZygoteInit.readArgumentList()
272 * Presently the wire format to the zygote process is:
273 * a) a count of arguments (argc, in essence)
274 * b) a number of newline-separated argument strings equal to count
275 *
276 * After the zygote process reads these it will write the pid of
277 * the child or -1 on failure, followed by boolean to
278 * indicate whether a wrapper process was used.
279 */
280 final BufferedWriter writer = zygoteState.writer;
281 final DataInputStream inputStream = zygoteState.inputStream;
282
283 writer.write(Integer.toString(args.size()));
284 writer.newLine();
285
Robert Sesek8f8d1872016-03-18 16:52:57 -0400286 for (int i = 0; i < sz; i++) {
287 String arg = args.get(i);
Robert Sesek8f8d1872016-03-18 16:52:57 -0400288 writer.write(arg);
289 writer.newLine();
290 }
291
292 writer.flush();
293
294 // Should there be a timeout on this?
295 Process.ProcessStartResult result = new Process.ProcessStartResult();
Robert Sesek0b58f192016-10-10 18:34:42 -0400296
297 // Always read the entire result from the input stream to avoid leaving
298 // bytes in the stream for future process starts to accidentally stumble
299 // upon.
Robert Sesek8f8d1872016-03-18 16:52:57 -0400300 result.pid = inputStream.readInt();
Robert Sesek0b58f192016-10-10 18:34:42 -0400301 result.usingWrapper = inputStream.readBoolean();
302
Robert Sesek8f8d1872016-03-18 16:52:57 -0400303 if (result.pid < 0) {
304 throw new ZygoteStartFailedEx("fork() failed");
305 }
Robert Sesek8f8d1872016-03-18 16:52:57 -0400306 return result;
307 } catch (IOException ex) {
308 zygoteState.close();
309 throw new ZygoteStartFailedEx(ex);
310 }
311 }
312
313 /**
314 * Starts a new process via the zygote mechanism.
315 *
316 * @param processClass Class name whose static main() to run
317 * @param niceName 'nice' process name to appear in ps
318 * @param uid a POSIX uid that the new process should setuid() to
319 * @param gid a POSIX gid that the new process shuold setgid() to
320 * @param gids null-ok; a list of supplementary group IDs that the
321 * new process should setgroup() to.
Nicolas Geoffray81edac42017-09-07 14:13:29 +0100322 * @param runtimeFlags Additional flags for the runtime.
Robert Sesek8f8d1872016-03-18 16:52:57 -0400323 * @param targetSdkVersion The target SDK version for the app.
324 * @param seInfo null-ok SELinux information for the new process.
325 * @param abi the ABI the process should use.
326 * @param instructionSet null-ok the instruction set to use.
327 * @param appDataDir null-ok the data directory of the app.
328 * @param extraArgs Additional arguments to supply to the zygote process.
329 * @return An object that describes the result of the attempt to start the process.
330 * @throws ZygoteStartFailedEx if process start failed for any reason
331 */
332 private Process.ProcessStartResult startViaZygote(final String processClass,
333 final String niceName,
334 final int uid, final int gid,
335 final int[] gids,
Nicolas Geoffray81edac42017-09-07 14:13:29 +0100336 int runtimeFlags, int mountExternal,
Robert Sesek8f8d1872016-03-18 16:52:57 -0400337 int targetSdkVersion,
338 String seInfo,
339 String abi,
340 String instructionSet,
341 String appDataDir,
Tamas Berghammerb8f7c352016-11-11 16:08:26 +0000342 String invokeWith,
Robert Sesek8f8d1872016-03-18 16:52:57 -0400343 String[] extraArgs)
344 throws ZygoteStartFailedEx {
Robert Sesekded20982016-08-15 13:59:13 -0400345 ArrayList<String> argsForZygote = new ArrayList<String>();
Robert Sesek8f8d1872016-03-18 16:52:57 -0400346
Robert Sesekded20982016-08-15 13:59:13 -0400347 // --runtime-args, --setuid=, --setgid=,
348 // and --setgroups= must go first
349 argsForZygote.add("--runtime-args");
350 argsForZygote.add("--setuid=" + uid);
351 argsForZygote.add("--setgid=" + gid);
Nicolas Geoffray81edac42017-09-07 14:13:29 +0100352 argsForZygote.add("--runtime-flags=" + runtimeFlags);
Robert Sesekded20982016-08-15 13:59:13 -0400353 if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
354 argsForZygote.add("--mount-external-default");
355 } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
356 argsForZygote.add("--mount-external-read");
357 } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
358 argsForZygote.add("--mount-external-write");
359 }
360 argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
Robert Sesek8f8d1872016-03-18 16:52:57 -0400361
Robert Sesekded20982016-08-15 13:59:13 -0400362 // --setgroups is a comma-separated list
363 if (gids != null && gids.length > 0) {
364 StringBuilder sb = new StringBuilder();
365 sb.append("--setgroups=");
Robert Sesek8f8d1872016-03-18 16:52:57 -0400366
Robert Sesekded20982016-08-15 13:59:13 -0400367 int sz = gids.length;
368 for (int i = 0; i < sz; i++) {
369 if (i != 0) {
370 sb.append(',');
Robert Sesek8f8d1872016-03-18 16:52:57 -0400371 }
Robert Sesekded20982016-08-15 13:59:13 -0400372 sb.append(gids[i]);
Robert Sesek8f8d1872016-03-18 16:52:57 -0400373 }
374
Robert Sesekded20982016-08-15 13:59:13 -0400375 argsForZygote.add(sb.toString());
376 }
377
378 if (niceName != null) {
379 argsForZygote.add("--nice-name=" + niceName);
380 }
381
382 if (seInfo != null) {
383 argsForZygote.add("--seinfo=" + seInfo);
384 }
385
386 if (instructionSet != null) {
387 argsForZygote.add("--instruction-set=" + instructionSet);
388 }
389
390 if (appDataDir != null) {
391 argsForZygote.add("--app-data-dir=" + appDataDir);
392 }
393
Tamas Berghammerb8f7c352016-11-11 16:08:26 +0000394 if (invokeWith != null) {
395 argsForZygote.add("--invoke-with");
396 argsForZygote.add(invokeWith);
397 }
398
Robert Sesekded20982016-08-15 13:59:13 -0400399 argsForZygote.add(processClass);
400
401 if (extraArgs != null) {
402 for (String arg : extraArgs) {
403 argsForZygote.add(arg);
Robert Sesek8f8d1872016-03-18 16:52:57 -0400404 }
Robert Sesekded20982016-08-15 13:59:13 -0400405 }
Robert Sesek8f8d1872016-03-18 16:52:57 -0400406
Robert Sesekded20982016-08-15 13:59:13 -0400407 synchronized(mLock) {
Robert Sesek8f8d1872016-03-18 16:52:57 -0400408 return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
409 }
410 }
411
412 /**
413 * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block
414 * and retry if the zygote is unresponsive. This method is a no-op if a connection is
415 * already open.
416 */
417 public void establishZygoteConnectionForAbi(String abi) {
418 try {
Robert Sesekded20982016-08-15 13:59:13 -0400419 synchronized(mLock) {
420 openZygoteSocketIfNeeded(abi);
421 }
Robert Sesek8f8d1872016-03-18 16:52:57 -0400422 } catch (ZygoteStartFailedEx ex) {
423 throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex);
424 }
425 }
426
427 /**
428 * Tries to open socket to Zygote process if not already open. If
Robert Sesekded20982016-08-15 13:59:13 -0400429 * already open, does nothing. May block and retry. Requires that mLock be held.
Robert Sesek8f8d1872016-03-18 16:52:57 -0400430 */
Robert Sesekded20982016-08-15 13:59:13 -0400431 @GuardedBy("mLock")
Robert Sesek8f8d1872016-03-18 16:52:57 -0400432 private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
Robert Sesekded20982016-08-15 13:59:13 -0400433 Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");
434
Robert Sesek8f8d1872016-03-18 16:52:57 -0400435 if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
436 try {
437 primaryZygoteState = ZygoteState.connect(mSocket);
438 } catch (IOException ioe) {
439 throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
440 }
441 }
442
443 if (primaryZygoteState.matches(abi)) {
444 return primaryZygoteState;
445 }
446
447 // The primary zygote didn't match. Try the secondary.
448 if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
449 try {
450 secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
451 } catch (IOException ioe) {
452 throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
453 }
454 }
455
456 if (secondaryZygoteState.matches(abi)) {
457 return secondaryZygoteState;
458 }
459
460 throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
461 }
Robert Sesekded20982016-08-15 13:59:13 -0400462
463 /**
464 * Instructs the zygote to pre-load the classes and native libraries at the given paths
465 * for the specified abi. Not all zygotes support this function.
466 */
Narayan Kamathbae484a2017-07-03 14:12:26 +0100467 public boolean preloadPackageForAbi(String packagePath, String libsPath, String cacheKey,
468 String abi) throws ZygoteStartFailedEx, IOException {
Robert Sesekded20982016-08-15 13:59:13 -0400469 synchronized(mLock) {
470 ZygoteState state = openZygoteSocketIfNeeded(abi);
Torne (Richard Coles)04526702017-01-13 14:19:39 +0000471 state.writer.write("4");
Robert Sesekded20982016-08-15 13:59:13 -0400472 state.writer.newLine();
473
474 state.writer.write("--preload-package");
475 state.writer.newLine();
476
477 state.writer.write(packagePath);
478 state.writer.newLine();
479
480 state.writer.write(libsPath);
481 state.writer.newLine();
482
Torne (Richard Coles)04526702017-01-13 14:19:39 +0000483 state.writer.write(cacheKey);
484 state.writer.newLine();
485
Robert Sesekded20982016-08-15 13:59:13 -0400486 state.writer.flush();
Narayan Kamathbae484a2017-07-03 14:12:26 +0100487
488 return (state.inputStream.readInt() == 0);
Robert Sesekded20982016-08-15 13:59:13 -0400489 }
490 }
Narayan Kamath669afcc2017-02-06 20:24:08 +0000491
492 /**
493 * Instructs the zygote to preload the default set of classes and resources. Returns
494 * {@code true} if a preload was performed as a result of this call, and {@code false}
495 * otherwise. The latter usually means that the zygote eagerly preloaded at startup
496 * or due to a previous call to {@code preloadDefault}. Note that this call is synchronous.
497 */
498 public boolean preloadDefault(String abi) throws ZygoteStartFailedEx, IOException {
499 synchronized (mLock) {
500 ZygoteState state = openZygoteSocketIfNeeded(abi);
501 // Each query starts with the argument count (1 in this case)
502 state.writer.write("1");
503 state.writer.newLine();
504 state.writer.write("--preload-default");
505 state.writer.newLine();
506 state.writer.flush();
507
508 return (state.inputStream.readInt() == 0);
509 }
510 }
Gustav Senntonf0c52b52017-04-27 17:00:50 +0100511
512 /**
513 * Try connecting to the Zygote over and over again until we hit a time-out.
514 * @param socketName The name of the socket to connect to.
515 */
516 public static void waitForConnectionToZygote(String socketName) {
517 for (int n = 20; n >= 0; n--) {
518 try {
519 final ZygoteState zs = ZygoteState.connect(socketName);
520 zs.close();
521 return;
522 } catch (IOException ioe) {
523 Log.w(LOG_TAG,
524 "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
525 }
526
527 try {
528 Thread.sleep(1000);
529 } catch (InterruptedException ie) {
530 }
531 }
532 Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + socketName);
533 }
Robert Sesek8f8d1872016-03-18 16:52:57 -0400534}