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
17package android.os;
18
19import android.net.LocalSocket;
20import android.net.LocalSocketAddress;
21import android.util.Log;
22import com.android.internal.annotations.GuardedBy;
23import com.android.internal.os.Zygote;
24import com.android.internal.util.Preconditions;
25import java.io.BufferedWriter;
26import java.io.DataInputStream;
27import java.io.IOException;
28import java.io.OutputStreamWriter;
29import java.nio.charset.StandardCharsets;
30import java.util.ArrayList;
31import java.util.Arrays;
32import java.util.List;
33
34/*package*/ class ZygoteStartFailedEx extends Exception {
35    ZygoteStartFailedEx(String s) {
36        super(s);
37    }
38
39    ZygoteStartFailedEx(Throwable cause) {
40        super(cause);
41    }
42
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 */
55public 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
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
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
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
122        boolean matches(String abi) {
123            return abiList.contains(abi);
124        }
125
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
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     */
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")
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")
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     */
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     */
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")
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     */
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     */
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