ZygoteProcess.java revision 8faeab8735f1a5759b24583d55853a488639546b
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 android.util.Slog;
23
24import com.android.internal.annotations.GuardedBy;
25import com.android.internal.os.Zygote;
26import com.android.internal.util.Preconditions;
27
28import 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.Collections;
36import java.util.List;
37import java.util.UUID;
38
39/*package*/ class ZygoteStartFailedEx extends Exception {
40    ZygoteStartFailedEx(String s) {
41        super(s);
42    }
43
44    ZygoteStartFailedEx(Throwable cause) {
45        super(cause);
46    }
47
48    ZygoteStartFailedEx(String s, Throwable cause) {
49        super(s, cause);
50    }
51}
52
53/**
54 * Maintains communication state with the zygote processes. This class is responsible
55 * for the sockets opened to the zygotes and for starting processes on behalf of the
56 * {@link android.os.Process} class.
57 *
58 * {@hide}
59 */
60public class ZygoteProcess {
61    private static final String LOG_TAG = "ZygoteProcess";
62
63    /**
64     * The name of the socket used to communicate with the primary zygote.
65     */
66    private final LocalSocketAddress mSocket;
67
68    /**
69     * The name of the secondary (alternate ABI) zygote socket.
70     */
71    private final LocalSocketAddress mSecondarySocket;
72
73    public ZygoteProcess(String primarySocket, String secondarySocket) {
74        this(new LocalSocketAddress(primarySocket, LocalSocketAddress.Namespace.RESERVED),
75                new LocalSocketAddress(secondarySocket, LocalSocketAddress.Namespace.RESERVED));
76    }
77
78    public ZygoteProcess(LocalSocketAddress primarySocket, LocalSocketAddress secondarySocket) {
79        mSocket = primarySocket;
80        mSecondarySocket = secondarySocket;
81    }
82
83    public LocalSocketAddress getPrimarySocketAddress() {
84        return mSocket;
85    }
86
87    /**
88     * State for communicating with the zygote process.
89     */
90    public static class ZygoteState {
91        final LocalSocket socket;
92        final DataInputStream inputStream;
93        final BufferedWriter writer;
94        final List<String> abiList;
95
96        boolean mClosed;
97
98        private ZygoteState(LocalSocket socket, DataInputStream inputStream,
99                BufferedWriter writer, List<String> abiList) {
100            this.socket = socket;
101            this.inputStream = inputStream;
102            this.writer = writer;
103            this.abiList = abiList;
104        }
105
106        public static ZygoteState connect(LocalSocketAddress address) throws IOException {
107            DataInputStream zygoteInputStream = null;
108            BufferedWriter zygoteWriter = null;
109            final LocalSocket zygoteSocket = new LocalSocket();
110
111            try {
112                zygoteSocket.connect(address);
113
114                zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
115
116                zygoteWriter = new BufferedWriter(new OutputStreamWriter(
117                        zygoteSocket.getOutputStream()), 256);
118            } catch (IOException ex) {
119                try {
120                    zygoteSocket.close();
121                } catch (IOException ignore) {
122                }
123
124                throw ex;
125            }
126
127            String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
128            Log.i("Zygote", "Process: zygote socket " + address.getNamespace() + "/"
129                    + address.getName() + " opened, supported ABIS: " + abiListString);
130
131            return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
132                    Arrays.asList(abiListString.split(",")));
133        }
134
135        boolean matches(String abi) {
136            return abiList.contains(abi);
137        }
138
139        public void close() {
140            try {
141                socket.close();
142            } catch (IOException ex) {
143                Log.e(LOG_TAG,"I/O exception on routine close", ex);
144            }
145
146            mClosed = true;
147        }
148
149        boolean isClosed() {
150            return mClosed;
151        }
152    }
153
154    /**
155     * Lock object to protect access to the two ZygoteStates below. This lock must be
156     * acquired while communicating over the ZygoteState's socket, to prevent
157     * interleaved access.
158     */
159    private final Object mLock = new Object();
160
161    /**
162     * List of exemptions to the API blacklist. These are prefix matches on the runtime format
163     * symbol signature. Any matching symbol is treated by the runtime as being on the light grey
164     * list.
165     */
166    private List<String> mApiBlacklistExemptions = Collections.emptyList();
167
168    /**
169     * The state of the connection to the primary zygote.
170     */
171    private ZygoteState primaryZygoteState;
172
173    /**
174     * The state of the connection to the secondary zygote.
175     */
176    private ZygoteState secondaryZygoteState;
177
178    /**
179     * Start a new process.
180     *
181     * <p>If processes are enabled, a new process is created and the
182     * static main() function of a <var>processClass</var> is executed there.
183     * The process will continue running after this function returns.
184     *
185     * <p>If processes are not enabled, a new thread in the caller's
186     * process is created and main() of <var>processclass</var> called there.
187     *
188     * <p>The niceName parameter, if not an empty string, is a custom name to
189     * give to the process instead of using processClass.  This allows you to
190     * make easily identifyable processes even if you are using the same base
191     * <var>processClass</var> to start them.
192     *
193     * When invokeWith is not null, the process will be started as a fresh app
194     * and not a zygote fork. Note that this is only allowed for uid 0 or when
195     * runtimeFlags contains DEBUG_ENABLE_DEBUGGER.
196     *
197     * @param processClass The class to use as the process's main entry
198     *                     point.
199     * @param niceName A more readable name to use for the process.
200     * @param uid The user-id under which the process will run.
201     * @param gid The group-id under which the process will run.
202     * @param gids Additional group-ids associated with the process.
203     * @param runtimeFlags Additional flags.
204     * @param targetSdkVersion The target SDK version for the app.
205     * @param seInfo null-ok SELinux information for the new process.
206     * @param abi non-null the ABI this app should be started with.
207     * @param instructionSet null-ok the instruction set to use.
208     * @param appDataDir null-ok the data directory of the app.
209     * @param invokeWith null-ok the command to invoke with.
210     * @param zygoteArgs Additional arguments to supply to the zygote process.
211     *
212     * @return An object that describes the result of the attempt to start the process.
213     * @throws RuntimeException on fatal start failure
214     */
215    public final Process.ProcessStartResult start(final String processClass,
216                                                  final String niceName,
217                                                  int uid, int gid, int[] gids,
218                                                  int runtimeFlags, int mountExternal,
219                                                  int targetSdkVersion,
220                                                  String seInfo,
221                                                  String abi,
222                                                  String instructionSet,
223                                                  String appDataDir,
224                                                  String invokeWith,
225                                                  String[] zygoteArgs) {
226        try {
227            return startViaZygote(processClass, niceName, uid, gid, gids,
228                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
229                    abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
230                    zygoteArgs);
231        } catch (ZygoteStartFailedEx ex) {
232            Log.e(LOG_TAG,
233                    "Starting VM process through Zygote failed");
234            throw new RuntimeException(
235                    "Starting VM process through Zygote failed", ex);
236        }
237    }
238
239    /** retry interval for opening a zygote socket */
240    static final int ZYGOTE_RETRY_MILLIS = 500;
241
242    /**
243     * Queries the zygote for the list of ABIS it supports.
244     *
245     * @throws ZygoteStartFailedEx if the query failed.
246     */
247    @GuardedBy("mLock")
248    private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
249            throws IOException {
250        // Each query starts with the argument count (1 in this case)
251        writer.write("1");
252        // ... followed by a new-line.
253        writer.newLine();
254        // ... followed by our only argument.
255        writer.write("--query-abi-list");
256        writer.newLine();
257        writer.flush();
258
259        // The response is a length prefixed stream of ASCII bytes.
260        int numBytes = inputStream.readInt();
261        byte[] bytes = new byte[numBytes];
262        inputStream.readFully(bytes);
263
264        return new String(bytes, StandardCharsets.US_ASCII);
265    }
266
267    /**
268     * Sends an argument list to the zygote process, which starts a new child
269     * and returns the child's pid. Please note: the present implementation
270     * replaces newlines in the argument list with spaces.
271     *
272     * @throws ZygoteStartFailedEx if process start failed for any reason
273     */
274    @GuardedBy("mLock")
275    private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
276            ZygoteState zygoteState, ArrayList<String> args)
277            throws ZygoteStartFailedEx {
278        try {
279            // Throw early if any of the arguments are malformed. This means we can
280            // avoid writing a partial response to the zygote.
281            int sz = args.size();
282            for (int i = 0; i < sz; i++) {
283                if (args.get(i).indexOf('\n') >= 0) {
284                    throw new ZygoteStartFailedEx("embedded newlines not allowed");
285                }
286            }
287
288            /**
289             * See com.android.internal.os.SystemZygoteInit.readArgumentList()
290             * Presently the wire format to the zygote process is:
291             * a) a count of arguments (argc, in essence)
292             * b) a number of newline-separated argument strings equal to count
293             *
294             * After the zygote process reads these it will write the pid of
295             * the child or -1 on failure, followed by boolean to
296             * indicate whether a wrapper process was used.
297             */
298            final BufferedWriter writer = zygoteState.writer;
299            final DataInputStream inputStream = zygoteState.inputStream;
300
301            writer.write(Integer.toString(args.size()));
302            writer.newLine();
303
304            for (int i = 0; i < sz; i++) {
305                String arg = args.get(i);
306                writer.write(arg);
307                writer.newLine();
308            }
309
310            writer.flush();
311
312            // Should there be a timeout on this?
313            Process.ProcessStartResult result = new Process.ProcessStartResult();
314
315            // Always read the entire result from the input stream to avoid leaving
316            // bytes in the stream for future process starts to accidentally stumble
317            // upon.
318            result.pid = inputStream.readInt();
319            result.usingWrapper = inputStream.readBoolean();
320
321            if (result.pid < 0) {
322                throw new ZygoteStartFailedEx("fork() failed");
323            }
324            return result;
325        } catch (IOException ex) {
326            zygoteState.close();
327            throw new ZygoteStartFailedEx(ex);
328        }
329    }
330
331    /**
332     * Starts a new process via the zygote mechanism.
333     *
334     * @param processClass Class name whose static main() to run
335     * @param niceName 'nice' process name to appear in ps
336     * @param uid a POSIX uid that the new process should setuid() to
337     * @param gid a POSIX gid that the new process shuold setgid() to
338     * @param gids null-ok; a list of supplementary group IDs that the
339     * new process should setgroup() to.
340     * @param runtimeFlags Additional flags for the runtime.
341     * @param targetSdkVersion The target SDK version for the app.
342     * @param seInfo null-ok SELinux information for the new process.
343     * @param abi the ABI the process should use.
344     * @param instructionSet null-ok the instruction set to use.
345     * @param appDataDir null-ok the data directory of the app.
346     * @param startChildZygote Start a sub-zygote. This creates a new zygote process
347     * that has its state cloned from this zygote process.
348     * @param extraArgs Additional arguments to supply to the zygote process.
349     * @return An object that describes the result of the attempt to start the process.
350     * @throws ZygoteStartFailedEx if process start failed for any reason
351     */
352    private Process.ProcessStartResult startViaZygote(final String processClass,
353                                                      final String niceName,
354                                                      final int uid, final int gid,
355                                                      final int[] gids,
356                                                      int runtimeFlags, int mountExternal,
357                                                      int targetSdkVersion,
358                                                      String seInfo,
359                                                      String abi,
360                                                      String instructionSet,
361                                                      String appDataDir,
362                                                      String invokeWith,
363                                                      boolean startChildZygote,
364                                                      String[] extraArgs)
365                                                      throws ZygoteStartFailedEx {
366        ArrayList<String> argsForZygote = new ArrayList<String>();
367
368        // --runtime-args, --setuid=, --setgid=,
369        // and --setgroups= must go first
370        argsForZygote.add("--runtime-args");
371        argsForZygote.add("--setuid=" + uid);
372        argsForZygote.add("--setgid=" + gid);
373        argsForZygote.add("--runtime-flags=" + runtimeFlags);
374        if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
375            argsForZygote.add("--mount-external-default");
376        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
377            argsForZygote.add("--mount-external-read");
378        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
379            argsForZygote.add("--mount-external-write");
380        }
381        argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
382
383        // --setgroups is a comma-separated list
384        if (gids != null && gids.length > 0) {
385            StringBuilder sb = new StringBuilder();
386            sb.append("--setgroups=");
387
388            int sz = gids.length;
389            for (int i = 0; i < sz; i++) {
390                if (i != 0) {
391                    sb.append(',');
392                }
393                sb.append(gids[i]);
394            }
395
396            argsForZygote.add(sb.toString());
397        }
398
399        if (niceName != null) {
400            argsForZygote.add("--nice-name=" + niceName);
401        }
402
403        if (seInfo != null) {
404            argsForZygote.add("--seinfo=" + seInfo);
405        }
406
407        if (instructionSet != null) {
408            argsForZygote.add("--instruction-set=" + instructionSet);
409        }
410
411        if (appDataDir != null) {
412            argsForZygote.add("--app-data-dir=" + appDataDir);
413        }
414
415        if (invokeWith != null) {
416            argsForZygote.add("--invoke-with");
417            argsForZygote.add(invokeWith);
418        }
419
420        if (startChildZygote) {
421            argsForZygote.add("--start-child-zygote");
422        }
423
424        argsForZygote.add(processClass);
425
426        if (extraArgs != null) {
427            for (String arg : extraArgs) {
428                argsForZygote.add(arg);
429            }
430        }
431
432        synchronized(mLock) {
433            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
434        }
435    }
436
437    /**
438     * Closes the connections to the zygote, if they exist.
439     */
440    public void close() {
441        if (primaryZygoteState != null) {
442            primaryZygoteState.close();
443        }
444        if (secondaryZygoteState != null) {
445            secondaryZygoteState.close();
446        }
447    }
448
449    /**
450     * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block
451     * and retry if the zygote is unresponsive. This method is a no-op if a connection is
452     * already open.
453     */
454    public void establishZygoteConnectionForAbi(String abi) {
455        try {
456            synchronized(mLock) {
457                openZygoteSocketIfNeeded(abi);
458            }
459        } catch (ZygoteStartFailedEx ex) {
460            throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex);
461        }
462    }
463
464    /**
465     * Push hidden API blacklisting exemptions into the zygote process(es).
466     *
467     * <p>The list of exemptions will take affect for all new processes forked from the zygote after
468     * this call.
469     *
470     * @param exemptions List of hidden API exemption prefixes.
471     */
472    public void setApiBlacklistExemptions(List<String> exemptions) {
473        synchronized (mLock) {
474            mApiBlacklistExemptions = exemptions;
475            maybeSetApiBlacklistExemptions(primaryZygoteState, true);
476            maybeSetApiBlacklistExemptions(secondaryZygoteState, true);
477        }
478    }
479
480    @GuardedBy("mLock")
481    private void maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
482        if (state == null || state.isClosed()) {
483            return;
484        }
485        if (!sendIfEmpty && mApiBlacklistExemptions.isEmpty()) {
486            return;
487        }
488        try {
489            state.writer.write(Integer.toString(mApiBlacklistExemptions.size() + 1));
490            state.writer.newLine();
491            state.writer.write("--set-api-blacklist-exemptions");
492            state.writer.newLine();
493            for (int i = 0; i < mApiBlacklistExemptions.size(); ++i) {
494                state.writer.write(mApiBlacklistExemptions.get(i));
495                state.writer.newLine();
496            }
497            state.writer.flush();
498            int status = state.inputStream.readInt();
499            if (status != 0) {
500                Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status);
501            }
502        } catch (IOException ioe) {
503            Slog.e(LOG_TAG, "Failed to set API blacklist exemptions", ioe);
504        }
505    }
506
507    /**
508     * Tries to open socket to Zygote process if not already open. If
509     * already open, does nothing.  May block and retry.  Requires that mLock be held.
510     */
511    @GuardedBy("mLock")
512    private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
513        Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");
514
515        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
516            try {
517                primaryZygoteState = ZygoteState.connect(mSocket);
518            } catch (IOException ioe) {
519                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
520            }
521            maybeSetApiBlacklistExemptions(primaryZygoteState, false);
522        }
523        if (primaryZygoteState.matches(abi)) {
524            return primaryZygoteState;
525        }
526
527        // The primary zygote didn't match. Try the secondary.
528        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
529            try {
530                secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
531            } catch (IOException ioe) {
532                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
533            }
534            maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
535        }
536
537        if (secondaryZygoteState.matches(abi)) {
538            return secondaryZygoteState;
539        }
540
541        throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
542    }
543
544    /**
545     * Instructs the zygote to pre-load the classes and native libraries at the given paths
546     * for the specified abi. Not all zygotes support this function.
547     */
548    public boolean preloadPackageForAbi(String packagePath, String libsPath, String libFileName,
549                                        String cacheKey, String abi) throws ZygoteStartFailedEx,
550                                                                            IOException {
551        synchronized(mLock) {
552            ZygoteState state = openZygoteSocketIfNeeded(abi);
553            state.writer.write("5");
554            state.writer.newLine();
555
556            state.writer.write("--preload-package");
557            state.writer.newLine();
558
559            state.writer.write(packagePath);
560            state.writer.newLine();
561
562            state.writer.write(libsPath);
563            state.writer.newLine();
564
565            state.writer.write(libFileName);
566            state.writer.newLine();
567
568            state.writer.write(cacheKey);
569            state.writer.newLine();
570
571            state.writer.flush();
572
573            return (state.inputStream.readInt() == 0);
574        }
575    }
576
577    /**
578     * Instructs the zygote to preload the default set of classes and resources. Returns
579     * {@code true} if a preload was performed as a result of this call, and {@code false}
580     * otherwise. The latter usually means that the zygote eagerly preloaded at startup
581     * or due to a previous call to {@code preloadDefault}. Note that this call is synchronous.
582     */
583    public boolean preloadDefault(String abi) throws ZygoteStartFailedEx, IOException {
584        synchronized (mLock) {
585            ZygoteState state = openZygoteSocketIfNeeded(abi);
586            // Each query starts with the argument count (1 in this case)
587            state.writer.write("1");
588            state.writer.newLine();
589            state.writer.write("--preload-default");
590            state.writer.newLine();
591            state.writer.flush();
592
593            return (state.inputStream.readInt() == 0);
594        }
595    }
596
597    /**
598     * Try connecting to the Zygote over and over again until we hit a time-out.
599     * @param socketName The name of the socket to connect to.
600     */
601    public static void waitForConnectionToZygote(String socketName) {
602        final LocalSocketAddress address =
603                new LocalSocketAddress(socketName, LocalSocketAddress.Namespace.RESERVED);
604        waitForConnectionToZygote(address);
605    }
606
607    /**
608     * Try connecting to the Zygote over and over again until we hit a time-out.
609     * @param address The name of the socket to connect to.
610     */
611    public static void waitForConnectionToZygote(LocalSocketAddress address) {
612        for (int n = 20; n >= 0; n--) {
613            try {
614                final ZygoteState zs = ZygoteState.connect(address);
615                zs.close();
616                return;
617            } catch (IOException ioe) {
618                Log.w(LOG_TAG,
619                        "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
620            }
621
622            try {
623                Thread.sleep(1000);
624            } catch (InterruptedException ie) {
625            }
626        }
627        Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + address.getName());
628    }
629
630    /**
631     * Starts a new zygote process as a child of this zygote. This is used to create
632     * secondary zygotes that inherit data from the zygote that this object
633     * communicates with. This returns a new ZygoteProcess representing a connection
634     * to the newly created zygote. Throws an exception if the zygote cannot be started.
635     */
636    public ChildZygoteProcess startChildZygote(final String processClass,
637                                               final String niceName,
638                                               int uid, int gid, int[] gids,
639                                               int runtimeFlags,
640                                               String seInfo,
641                                               String abi,
642                                               String instructionSet) {
643        // Create an unguessable address in the global abstract namespace.
644        final LocalSocketAddress serverAddress = new LocalSocketAddress(
645                processClass + "/" + UUID.randomUUID().toString());
646
647        final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName()};
648
649        Process.ProcessStartResult result;
650        try {
651            result = startViaZygote(processClass, niceName, uid, gid,
652                    gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
653                    abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
654                    true /* startChildZygote */, extraArgs);
655        } catch (ZygoteStartFailedEx ex) {
656            throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
657        }
658
659        return new ChildZygoteProcess(serverAddress, result.pid);
660    }
661}
662