• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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