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     * Proportion of hidden API accesses that should be logged to the event log; 0 - 0x10000.
170     */
171    private int mHiddenApiAccessLogSampleRate;
172
173    /**
174     * The state of the connection to the primary zygote.
175     */
176    private ZygoteState primaryZygoteState;
177
178    /**
179     * The state of the connection to the secondary zygote.
180     */
181    private ZygoteState secondaryZygoteState;
182
183    /**
184     * Start a new process.
185     *
186     * <p>If processes are enabled, a new process is created and the
187     * static main() function of a <var>processClass</var> is executed there.
188     * The process will continue running after this function returns.
189     *
190     * <p>If processes are not enabled, a new thread in the caller's
191     * process is created and main() of <var>processclass</var> called there.
192     *
193     * <p>The niceName parameter, if not an empty string, is a custom name to
194     * give to the process instead of using processClass.  This allows you to
195     * make easily identifyable processes even if you are using the same base
196     * <var>processClass</var> to start them.
197     *
198     * When invokeWith is not null, the process will be started as a fresh app
199     * and not a zygote fork. Note that this is only allowed for uid 0 or when
200     * runtimeFlags contains DEBUG_ENABLE_DEBUGGER.
201     *
202     * @param processClass The class to use as the process's main entry
203     *                     point.
204     * @param niceName A more readable name to use for the process.
205     * @param uid The user-id under which the process will run.
206     * @param gid The group-id under which the process will run.
207     * @param gids Additional group-ids associated with the process.
208     * @param runtimeFlags Additional flags.
209     * @param targetSdkVersion The target SDK version for the app.
210     * @param seInfo null-ok SELinux information for the new process.
211     * @param abi non-null the ABI this app should be started with.
212     * @param instructionSet null-ok the instruction set to use.
213     * @param appDataDir null-ok the data directory of the app.
214     * @param invokeWith null-ok the command to invoke with.
215     * @param zygoteArgs Additional arguments to supply to the zygote process.
216     *
217     * @return An object that describes the result of the attempt to start the process.
218     * @throws RuntimeException on fatal start failure
219     */
220    public final Process.ProcessStartResult start(final String processClass,
221                                                  final String niceName,
222                                                  int uid, int gid, int[] gids,
223                                                  int runtimeFlags, int mountExternal,
224                                                  int targetSdkVersion,
225                                                  String seInfo,
226                                                  String abi,
227                                                  String instructionSet,
228                                                  String appDataDir,
229                                                  String invokeWith,
230                                                  String[] zygoteArgs) {
231        try {
232            return startViaZygote(processClass, niceName, uid, gid, gids,
233                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
234                    abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
235                    zygoteArgs);
236        } catch (ZygoteStartFailedEx ex) {
237            Log.e(LOG_TAG,
238                    "Starting VM process through Zygote failed");
239            throw new RuntimeException(
240                    "Starting VM process through Zygote failed", ex);
241        }
242    }
243
244    /** retry interval for opening a zygote socket */
245    static final int ZYGOTE_RETRY_MILLIS = 500;
246
247    /**
248     * Queries the zygote for the list of ABIS it supports.
249     *
250     * @throws ZygoteStartFailedEx if the query failed.
251     */
252    @GuardedBy("mLock")
253    private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
254            throws IOException {
255        // Each query starts with the argument count (1 in this case)
256        writer.write("1");
257        // ... followed by a new-line.
258        writer.newLine();
259        // ... followed by our only argument.
260        writer.write("--query-abi-list");
261        writer.newLine();
262        writer.flush();
263
264        // The response is a length prefixed stream of ASCII bytes.
265        int numBytes = inputStream.readInt();
266        byte[] bytes = new byte[numBytes];
267        inputStream.readFully(bytes);
268
269        return new String(bytes, StandardCharsets.US_ASCII);
270    }
271
272    /**
273     * Sends an argument list to the zygote process, which starts a new child
274     * and returns the child's pid. Please note: the present implementation
275     * replaces newlines in the argument list with spaces.
276     *
277     * @throws ZygoteStartFailedEx if process start failed for any reason
278     */
279    @GuardedBy("mLock")
280    private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
281            ZygoteState zygoteState, ArrayList<String> args)
282            throws ZygoteStartFailedEx {
283        try {
284            // Throw early if any of the arguments are malformed. This means we can
285            // avoid writing a partial response to the zygote.
286            int sz = args.size();
287            for (int i = 0; i < sz; i++) {
288                if (args.get(i).indexOf('\n') >= 0) {
289                    throw new ZygoteStartFailedEx("embedded newlines not allowed");
290                }
291            }
292
293            /**
294             * See com.android.internal.os.SystemZygoteInit.readArgumentList()
295             * Presently the wire format to the zygote process is:
296             * a) a count of arguments (argc, in essence)
297             * b) a number of newline-separated argument strings equal to count
298             *
299             * After the zygote process reads these it will write the pid of
300             * the child or -1 on failure, followed by boolean to
301             * indicate whether a wrapper process was used.
302             */
303            final BufferedWriter writer = zygoteState.writer;
304            final DataInputStream inputStream = zygoteState.inputStream;
305
306            writer.write(Integer.toString(args.size()));
307            writer.newLine();
308
309            for (int i = 0; i < sz; i++) {
310                String arg = args.get(i);
311                writer.write(arg);
312                writer.newLine();
313            }
314
315            writer.flush();
316
317            // Should there be a timeout on this?
318            Process.ProcessStartResult result = new Process.ProcessStartResult();
319
320            // Always read the entire result from the input stream to avoid leaving
321            // bytes in the stream for future process starts to accidentally stumble
322            // upon.
323            result.pid = inputStream.readInt();
324            result.usingWrapper = inputStream.readBoolean();
325
326            if (result.pid < 0) {
327                throw new ZygoteStartFailedEx("fork() failed");
328            }
329            return result;
330        } catch (IOException ex) {
331            zygoteState.close();
332            throw new ZygoteStartFailedEx(ex);
333        }
334    }
335
336    /**
337     * Starts a new process via the zygote mechanism.
338     *
339     * @param processClass Class name whose static main() to run
340     * @param niceName 'nice' process name to appear in ps
341     * @param uid a POSIX uid that the new process should setuid() to
342     * @param gid a POSIX gid that the new process shuold setgid() to
343     * @param gids null-ok; a list of supplementary group IDs that the
344     * new process should setgroup() to.
345     * @param runtimeFlags Additional flags for the runtime.
346     * @param targetSdkVersion The target SDK version for the app.
347     * @param seInfo null-ok SELinux information for the new process.
348     * @param abi the ABI the process should use.
349     * @param instructionSet null-ok the instruction set to use.
350     * @param appDataDir null-ok the data directory of the app.
351     * @param startChildZygote Start a sub-zygote. This creates a new zygote process
352     * that has its state cloned from this zygote process.
353     * @param extraArgs Additional arguments to supply to the zygote process.
354     * @return An object that describes the result of the attempt to start the process.
355     * @throws ZygoteStartFailedEx if process start failed for any reason
356     */
357    private Process.ProcessStartResult startViaZygote(final String processClass,
358                                                      final String niceName,
359                                                      final int uid, final int gid,
360                                                      final int[] gids,
361                                                      int runtimeFlags, int mountExternal,
362                                                      int targetSdkVersion,
363                                                      String seInfo,
364                                                      String abi,
365                                                      String instructionSet,
366                                                      String appDataDir,
367                                                      String invokeWith,
368                                                      boolean startChildZygote,
369                                                      String[] extraArgs)
370                                                      throws ZygoteStartFailedEx {
371        ArrayList<String> argsForZygote = new ArrayList<String>();
372
373        // --runtime-args, --setuid=, --setgid=,
374        // and --setgroups= must go first
375        argsForZygote.add("--runtime-args");
376        argsForZygote.add("--setuid=" + uid);
377        argsForZygote.add("--setgid=" + gid);
378        argsForZygote.add("--runtime-flags=" + runtimeFlags);
379        if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
380            argsForZygote.add("--mount-external-default");
381        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
382            argsForZygote.add("--mount-external-read");
383        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
384            argsForZygote.add("--mount-external-write");
385        }
386        argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
387
388        // --setgroups is a comma-separated list
389        if (gids != null && gids.length > 0) {
390            StringBuilder sb = new StringBuilder();
391            sb.append("--setgroups=");
392
393            int sz = gids.length;
394            for (int i = 0; i < sz; i++) {
395                if (i != 0) {
396                    sb.append(',');
397                }
398                sb.append(gids[i]);
399            }
400
401            argsForZygote.add(sb.toString());
402        }
403
404        if (niceName != null) {
405            argsForZygote.add("--nice-name=" + niceName);
406        }
407
408        if (seInfo != null) {
409            argsForZygote.add("--seinfo=" + seInfo);
410        }
411
412        if (instructionSet != null) {
413            argsForZygote.add("--instruction-set=" + instructionSet);
414        }
415
416        if (appDataDir != null) {
417            argsForZygote.add("--app-data-dir=" + appDataDir);
418        }
419
420        if (invokeWith != null) {
421            argsForZygote.add("--invoke-with");
422            argsForZygote.add(invokeWith);
423        }
424
425        if (startChildZygote) {
426            argsForZygote.add("--start-child-zygote");
427        }
428
429        argsForZygote.add(processClass);
430
431        if (extraArgs != null) {
432            for (String arg : extraArgs) {
433                argsForZygote.add(arg);
434            }
435        }
436
437        synchronized(mLock) {
438            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
439        }
440    }
441
442    /**
443     * Closes the connections to the zygote, if they exist.
444     */
445    public void close() {
446        if (primaryZygoteState != null) {
447            primaryZygoteState.close();
448        }
449        if (secondaryZygoteState != null) {
450            secondaryZygoteState.close();
451        }
452    }
453
454    /**
455     * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block
456     * and retry if the zygote is unresponsive. This method is a no-op if a connection is
457     * already open.
458     */
459    public void establishZygoteConnectionForAbi(String abi) {
460        try {
461            synchronized(mLock) {
462                openZygoteSocketIfNeeded(abi);
463            }
464        } catch (ZygoteStartFailedEx ex) {
465            throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex);
466        }
467    }
468
469    /**
470     * Push hidden API blacklisting exemptions into the zygote process(es).
471     *
472     * <p>The list of exemptions will take affect for all new processes forked from the zygote after
473     * this call.
474     *
475     * @param exemptions List of hidden API exemption prefixes. Any matching members are treated as
476     *        whitelisted/public APIs (i.e. allowed, no logging of usage).
477     */
478    public boolean setApiBlacklistExemptions(List<String> exemptions) {
479        synchronized (mLock) {
480            mApiBlacklistExemptions = exemptions;
481            boolean ok = maybeSetApiBlacklistExemptions(primaryZygoteState, true);
482            if (ok) {
483                ok = maybeSetApiBlacklistExemptions(secondaryZygoteState, true);
484            }
485            return ok;
486        }
487    }
488
489    /**
490     * Set the precentage of detected hidden API accesses that are logged to the event log.
491     *
492     * <p>This rate will take affect for all new processes forked from the zygote after this call.
493     *
494     * @param rate An integer between 0 and 0x10000 inclusive. 0 means no event logging.
495     */
496    public void setHiddenApiAccessLogSampleRate(int rate) {
497        synchronized (mLock) {
498            mHiddenApiAccessLogSampleRate = rate;
499            maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
500            maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
501        }
502    }
503
504    @GuardedBy("mLock")
505    private boolean maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
506        if (state == null || state.isClosed()) {
507            Slog.e(LOG_TAG, "Can't set API blacklist exemptions: no zygote connection");
508            return false;
509        }
510        if (!sendIfEmpty && mApiBlacklistExemptions.isEmpty()) {
511            return true;
512        }
513        try {
514            state.writer.write(Integer.toString(mApiBlacklistExemptions.size() + 1));
515            state.writer.newLine();
516            state.writer.write("--set-api-blacklist-exemptions");
517            state.writer.newLine();
518            for (int i = 0; i < mApiBlacklistExemptions.size(); ++i) {
519                state.writer.write(mApiBlacklistExemptions.get(i));
520                state.writer.newLine();
521            }
522            state.writer.flush();
523            int status = state.inputStream.readInt();
524            if (status != 0) {
525                Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status);
526            }
527            return true;
528        } catch (IOException ioe) {
529            Slog.e(LOG_TAG, "Failed to set API blacklist exemptions", ioe);
530            mApiBlacklistExemptions = Collections.emptyList();
531            return false;
532        }
533    }
534
535    private void maybeSetHiddenApiAccessLogSampleRate(ZygoteState state) {
536        if (state == null || state.isClosed()) {
537            return;
538        }
539        if (mHiddenApiAccessLogSampleRate == -1) {
540            return;
541        }
542        try {
543            state.writer.write(Integer.toString(1));
544            state.writer.newLine();
545            state.writer.write("--hidden-api-log-sampling-rate="
546                    + Integer.toString(mHiddenApiAccessLogSampleRate));
547            state.writer.newLine();
548            state.writer.flush();
549            int status = state.inputStream.readInt();
550            if (status != 0) {
551                Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate; status " + status);
552            }
553        } catch (IOException ioe) {
554            Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate", ioe);
555        }
556    }
557
558    /**
559     * Tries to open socket to Zygote process if not already open. If
560     * already open, does nothing.  May block and retry.  Requires that mLock be held.
561     */
562    @GuardedBy("mLock")
563    private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
564        Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");
565
566        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
567            try {
568                primaryZygoteState = ZygoteState.connect(mSocket);
569            } catch (IOException ioe) {
570                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
571            }
572            maybeSetApiBlacklistExemptions(primaryZygoteState, false);
573            maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
574        }
575        if (primaryZygoteState.matches(abi)) {
576            return primaryZygoteState;
577        }
578
579        // The primary zygote didn't match. Try the secondary.
580        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
581            try {
582                secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
583            } catch (IOException ioe) {
584                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
585            }
586            maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
587            maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
588        }
589
590        if (secondaryZygoteState.matches(abi)) {
591            return secondaryZygoteState;
592        }
593
594        throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
595    }
596
597    /**
598     * Instructs the zygote to pre-load the classes and native libraries at the given paths
599     * for the specified abi. Not all zygotes support this function.
600     */
601    public boolean preloadPackageForAbi(String packagePath, String libsPath, String libFileName,
602                                        String cacheKey, String abi) throws ZygoteStartFailedEx,
603                                                                            IOException {
604        synchronized(mLock) {
605            ZygoteState state = openZygoteSocketIfNeeded(abi);
606            state.writer.write("5");
607            state.writer.newLine();
608
609            state.writer.write("--preload-package");
610            state.writer.newLine();
611
612            state.writer.write(packagePath);
613            state.writer.newLine();
614
615            state.writer.write(libsPath);
616            state.writer.newLine();
617
618            state.writer.write(libFileName);
619            state.writer.newLine();
620
621            state.writer.write(cacheKey);
622            state.writer.newLine();
623
624            state.writer.flush();
625
626            return (state.inputStream.readInt() == 0);
627        }
628    }
629
630    /**
631     * Instructs the zygote to preload the default set of classes and resources. Returns
632     * {@code true} if a preload was performed as a result of this call, and {@code false}
633     * otherwise. The latter usually means that the zygote eagerly preloaded at startup
634     * or due to a previous call to {@code preloadDefault}. Note that this call is synchronous.
635     */
636    public boolean preloadDefault(String abi) throws ZygoteStartFailedEx, IOException {
637        synchronized (mLock) {
638            ZygoteState state = openZygoteSocketIfNeeded(abi);
639            // Each query starts with the argument count (1 in this case)
640            state.writer.write("1");
641            state.writer.newLine();
642            state.writer.write("--preload-default");
643            state.writer.newLine();
644            state.writer.flush();
645
646            return (state.inputStream.readInt() == 0);
647        }
648    }
649
650    /**
651     * Try connecting to the Zygote over and over again until we hit a time-out.
652     * @param socketName The name of the socket to connect to.
653     */
654    public static void waitForConnectionToZygote(String socketName) {
655        final LocalSocketAddress address =
656                new LocalSocketAddress(socketName, LocalSocketAddress.Namespace.RESERVED);
657        waitForConnectionToZygote(address);
658    }
659
660    /**
661     * Try connecting to the Zygote over and over again until we hit a time-out.
662     * @param address The name of the socket to connect to.
663     */
664    public static void waitForConnectionToZygote(LocalSocketAddress address) {
665        for (int n = 20; n >= 0; n--) {
666            try {
667                final ZygoteState zs = ZygoteState.connect(address);
668                zs.close();
669                return;
670            } catch (IOException ioe) {
671                Log.w(LOG_TAG,
672                        "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
673            }
674
675            try {
676                Thread.sleep(1000);
677            } catch (InterruptedException ie) {
678            }
679        }
680        Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + address.getName());
681    }
682
683    /**
684     * Starts a new zygote process as a child of this zygote. This is used to create
685     * secondary zygotes that inherit data from the zygote that this object
686     * communicates with. This returns a new ZygoteProcess representing a connection
687     * to the newly created zygote. Throws an exception if the zygote cannot be started.
688     */
689    public ChildZygoteProcess startChildZygote(final String processClass,
690                                               final String niceName,
691                                               int uid, int gid, int[] gids,
692                                               int runtimeFlags,
693                                               String seInfo,
694                                               String abi,
695                                               String instructionSet) {
696        // Create an unguessable address in the global abstract namespace.
697        final LocalSocketAddress serverAddress = new LocalSocketAddress(
698                processClass + "/" + UUID.randomUUID().toString());
699
700        final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName()};
701
702        Process.ProcessStartResult result;
703        try {
704            result = startViaZygote(processClass, niceName, uid, gid,
705                    gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
706                    abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
707                    true /* startChildZygote */, extraArgs);
708        } catch (ZygoteStartFailedEx ex) {
709            throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
710        }
711
712        return new ChildZygoteProcess(serverAddress, result.pid);
713    }
714}
715