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