ZygoteInit.java revision 0de36e51c5990b1c25c3d0fad3fea7d10aaecce5
1/*
2 * Copyright (C) 2007 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 com.android.internal.os;
18
19import static android.system.OsConstants.S_IRWXG;
20import static android.system.OsConstants.S_IRWXO;
21
22import android.content.res.Resources;
23import android.content.res.TypedArray;
24import android.net.LocalServerSocket;
25import android.opengl.EGL14;
26import android.os.Build;
27import android.os.Debug;
28import android.os.Process;
29import android.os.SystemClock;
30import android.os.SystemProperties;
31import android.os.Trace;
32import android.system.ErrnoException;
33import android.system.Os;
34import android.system.OsConstants;
35import android.util.EventLog;
36import android.util.Log;
37import android.util.Slog;
38import android.webkit.WebViewFactory;
39
40import dalvik.system.DexFile;
41import dalvik.system.PathClassLoader;
42import dalvik.system.VMRuntime;
43
44import libcore.io.IoUtils;
45
46import java.io.BufferedReader;
47import java.io.FileDescriptor;
48import java.io.FileInputStream;
49import java.io.FileNotFoundException;
50import java.io.IOException;
51import java.io.InputStream;
52import java.io.InputStreamReader;
53import java.lang.reflect.InvocationTargetException;
54import java.lang.reflect.Method;
55import java.lang.reflect.Modifier;
56import java.util.ArrayList;
57
58/**
59 * Startup class for the zygote process.
60 *
61 * Pre-initializes some classes, and then waits for commands on a UNIX domain
62 * socket. Based on these commands, forks off child processes that inherit
63 * the initial state of the VM.
64 *
65 * Please see {@link ZygoteConnection.Arguments} for documentation on the
66 * client protocol.
67 *
68 * @hide
69 */
70public class ZygoteInit {
71    private static final String TAG = "Zygote";
72
73    private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
74
75    private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
76
77    private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
78    private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
79
80    /** when preloading, GC after allocating this many bytes */
81    private static final int PRELOAD_GC_THRESHOLD = 50000;
82
83    private static final String ABI_LIST_ARG = "--abi-list=";
84
85    private static final String SOCKET_NAME_ARG = "--socket-name=";
86
87    private static LocalServerSocket sServerSocket;
88
89    /**
90     * Used to pre-load resources.  We hold a global reference on it so it
91     * never gets destroyed.
92     */
93    private static Resources mResources;
94
95    /**
96     * The number of times that the main Zygote loop
97     * should run before calling gc() again.
98     */
99    static final int GC_LOOP_COUNT = 10;
100
101    /**
102     * The path of a file that contains classes to preload.
103     */
104    private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
105
106    /** Controls whether we should preload resources during zygote init. */
107    private static final boolean PRELOAD_RESOURCES = true;
108
109    /**
110     * Invokes a static "main(argv[]) method on class "className".
111     * Converts various failing exceptions into RuntimeExceptions, with
112     * the assumption that they will then cause the VM instance to exit.
113     *
114     * @param loader class loader to use
115     * @param className Fully-qualified class name
116     * @param argv Argument vector for main()
117     */
118    static void invokeStaticMain(ClassLoader loader,
119            String className, String[] argv)
120            throws ZygoteInit.MethodAndArgsCaller {
121        Class<?> cl;
122
123        try {
124            cl = loader.loadClass(className);
125        } catch (ClassNotFoundException ex) {
126            throw new RuntimeException(
127                    "Missing class when invoking static main " + className,
128                    ex);
129        }
130
131        Method m;
132        try {
133            m = cl.getMethod("main", new Class[] { String[].class });
134        } catch (NoSuchMethodException ex) {
135            throw new RuntimeException(
136                    "Missing static main on " + className, ex);
137        } catch (SecurityException ex) {
138            throw new RuntimeException(
139                    "Problem getting static main on " + className, ex);
140        }
141
142        int modifiers = m.getModifiers();
143        if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
144            throw new RuntimeException(
145                    "Main method is not public and static on " + className);
146        }
147
148        /*
149         * This throw gets caught in ZygoteInit.main(), which responds
150         * by invoking the exception's run() method. This arrangement
151         * clears up all the stack frames that were required in setting
152         * up the process.
153         */
154        throw new ZygoteInit.MethodAndArgsCaller(m, argv);
155    }
156
157    /**
158     * Registers a server socket for zygote command connections
159     *
160     * @throws RuntimeException when open fails
161     */
162    private static void registerZygoteSocket(String socketName) {
163        if (sServerSocket == null) {
164            int fileDesc;
165            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
166            try {
167                String env = System.getenv(fullSocketName);
168                fileDesc = Integer.parseInt(env);
169            } catch (RuntimeException ex) {
170                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
171            }
172
173            try {
174                sServerSocket = new LocalServerSocket(
175                        createFileDescriptor(fileDesc));
176            } catch (IOException ex) {
177                throw new RuntimeException(
178                        "Error binding to local socket '" + fileDesc + "'", ex);
179            }
180        }
181    }
182
183    /**
184     * Waits for and accepts a single command connection. Throws
185     * RuntimeException on failure.
186     */
187    private static ZygoteConnection acceptCommandPeer(String abiList) {
188        try {
189            return new ZygoteConnection(sServerSocket.accept(), abiList);
190        } catch (IOException ex) {
191            throw new RuntimeException(
192                    "IOException during accept()", ex);
193        }
194    }
195
196    /**
197     * Close and clean up zygote sockets. Called on shutdown and on the
198     * child's exit path.
199     */
200    static void closeServerSocket() {
201        try {
202            if (sServerSocket != null) {
203                FileDescriptor fd = sServerSocket.getFileDescriptor();
204                sServerSocket.close();
205                if (fd != null) {
206                    Os.close(fd);
207                }
208            }
209        } catch (IOException ex) {
210            Log.e(TAG, "Zygote:  error closing sockets", ex);
211        } catch (ErrnoException ex) {
212            Log.e(TAG, "Zygote:  error closing descriptor", ex);
213        }
214
215        sServerSocket = null;
216    }
217
218    /**
219     * Return the server socket's underlying file descriptor, so that
220     * ZygoteConnection can pass it to the native code for proper
221     * closure after a child process is forked off.
222     */
223
224    static FileDescriptor getServerSocketFileDescriptor() {
225        return sServerSocket.getFileDescriptor();
226    }
227
228    private static final int UNPRIVILEGED_UID = 9999;
229    private static final int UNPRIVILEGED_GID = 9999;
230
231    private static final int ROOT_UID = 0;
232    private static final int ROOT_GID = 0;
233
234    /**
235     * Sets effective user ID.
236     */
237    private static void setEffectiveUser(int uid) {
238        int errno = setreuid(ROOT_UID, uid);
239        if (errno != 0) {
240            Log.e(TAG, "setreuid() failed. errno: " + errno);
241        }
242    }
243
244    /**
245     * Sets effective group ID.
246     */
247    private static void setEffectiveGroup(int gid) {
248        int errno = setregid(ROOT_GID, gid);
249        if (errno != 0) {
250            Log.e(TAG, "setregid() failed. errno: " + errno);
251        }
252    }
253
254    static void preload() {
255        Log.d(TAG, "begin preload");
256        preloadClasses();
257        preloadResources();
258        preloadOpenGL();
259        preloadSharedLibraries();
260        // Ask the WebViewFactory to do any initialization that must run in the zygote process,
261        // for memory sharing purposes.
262        WebViewFactory.prepareWebViewInZygote();
263        Log.d(TAG, "end preload");
264    }
265
266    private static void preloadSharedLibraries() {
267        Log.i(TAG, "Preloading shared libraries...");
268        System.loadLibrary("android");
269        System.loadLibrary("compiler_rt");
270        System.loadLibrary("jnigraphics");
271    }
272
273    private static void preloadOpenGL() {
274        if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
275            EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
276        }
277    }
278
279    /**
280     * Performs Zygote process initialization. Loads and initializes
281     * commonly used classes.
282     *
283     * Most classes only cause a few hundred bytes to be allocated, but
284     * a few will allocate a dozen Kbytes (in one case, 500+K).
285     */
286    private static void preloadClasses() {
287        final VMRuntime runtime = VMRuntime.getRuntime();
288
289        InputStream is;
290        try {
291            is = new FileInputStream(PRELOADED_CLASSES);
292        } catch (FileNotFoundException e) {
293            Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
294            return;
295        }
296
297        Log.i(TAG, "Preloading classes...");
298        long startTime = SystemClock.uptimeMillis();
299
300        // Drop root perms while running static initializers.
301        setEffectiveGroup(UNPRIVILEGED_GID);
302        setEffectiveUser(UNPRIVILEGED_UID);
303
304        // Alter the target heap utilization.  With explicit GCs this
305        // is not likely to have any effect.
306        float defaultUtilization = runtime.getTargetHeapUtilization();
307        runtime.setTargetHeapUtilization(0.8f);
308
309        // Start with a clean slate.
310        System.gc();
311        runtime.runFinalizationSync();
312        Debug.startAllocCounting();
313
314        try {
315            BufferedReader br
316                = new BufferedReader(new InputStreamReader(is), 256);
317
318            int count = 0;
319            String line;
320            while ((line = br.readLine()) != null) {
321                // Skip comments and blank lines.
322                line = line.trim();
323                if (line.startsWith("#") || line.equals("")) {
324                    continue;
325                }
326
327                try {
328                    if (false) {
329                        Log.v(TAG, "Preloading " + line + "...");
330                    }
331                    Class.forName(line);
332                    if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
333                        if (false) {
334                            Log.v(TAG,
335                                " GC at " + Debug.getGlobalAllocSize());
336                        }
337                        System.gc();
338                        runtime.runFinalizationSync();
339                        Debug.resetGlobalAllocSize();
340                    }
341                    count++;
342                } catch (ClassNotFoundException e) {
343                    Log.w(TAG, "Class not found for preloading: " + line);
344                } catch (UnsatisfiedLinkError e) {
345                    Log.w(TAG, "Problem preloading " + line + ": " + e);
346                } catch (Throwable t) {
347                    Log.e(TAG, "Error preloading " + line + ".", t);
348                    if (t instanceof Error) {
349                        throw (Error) t;
350                    }
351                    if (t instanceof RuntimeException) {
352                        throw (RuntimeException) t;
353                    }
354                    throw new RuntimeException(t);
355                }
356            }
357
358            Log.i(TAG, "...preloaded " + count + " classes in "
359                    + (SystemClock.uptimeMillis()-startTime) + "ms.");
360        } catch (IOException e) {
361            Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
362        } finally {
363            IoUtils.closeQuietly(is);
364            // Restore default.
365            runtime.setTargetHeapUtilization(defaultUtilization);
366
367            // Fill in dex caches with classes, fields, and methods brought in by preloading.
368            runtime.preloadDexCaches();
369
370            Debug.stopAllocCounting();
371
372            // Bring back root. We'll need it later.
373            setEffectiveUser(ROOT_UID);
374            setEffectiveGroup(ROOT_GID);
375        }
376    }
377
378    /**
379     * Load in commonly used resources, so they can be shared across
380     * processes.
381     *
382     * These tend to be a few Kbytes, but are frequently in the 20-40K
383     * range, and occasionally even larger.
384     */
385    private static void preloadResources() {
386        final VMRuntime runtime = VMRuntime.getRuntime();
387
388        Debug.startAllocCounting();
389        try {
390            System.gc();
391            runtime.runFinalizationSync();
392            mResources = Resources.getSystem();
393            mResources.startPreloading();
394            if (PRELOAD_RESOURCES) {
395                Log.i(TAG, "Preloading resources...");
396
397                long startTime = SystemClock.uptimeMillis();
398                TypedArray ar = mResources.obtainTypedArray(
399                        com.android.internal.R.array.preloaded_drawables);
400                int N = preloadDrawables(runtime, ar);
401                ar.recycle();
402                Log.i(TAG, "...preloaded " + N + " resources in "
403                        + (SystemClock.uptimeMillis()-startTime) + "ms.");
404
405                startTime = SystemClock.uptimeMillis();
406                ar = mResources.obtainTypedArray(
407                        com.android.internal.R.array.preloaded_color_state_lists);
408                N = preloadColorStateLists(runtime, ar);
409                ar.recycle();
410                Log.i(TAG, "...preloaded " + N + " resources in "
411                        + (SystemClock.uptimeMillis()-startTime) + "ms.");
412            }
413            mResources.finishPreloading();
414        } catch (RuntimeException e) {
415            Log.w(TAG, "Failure preloading resources", e);
416        } finally {
417            Debug.stopAllocCounting();
418        }
419    }
420
421    private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
422        int N = ar.length();
423        for (int i=0; i<N; i++) {
424            if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
425                if (false) {
426                    Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
427                }
428                System.gc();
429                runtime.runFinalizationSync();
430                Debug.resetGlobalAllocSize();
431            }
432            int id = ar.getResourceId(i, 0);
433            if (false) {
434                Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
435            }
436            if (id != 0) {
437                if (mResources.getColorStateList(id) == null) {
438                    throw new IllegalArgumentException(
439                            "Unable to find preloaded color resource #0x"
440                            + Integer.toHexString(id)
441                            + " (" + ar.getString(i) + ")");
442                }
443            }
444        }
445        return N;
446    }
447
448
449    private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
450        int N = ar.length();
451        for (int i=0; i<N; i++) {
452            if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
453                if (false) {
454                    Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
455                }
456                System.gc();
457                runtime.runFinalizationSync();
458                Debug.resetGlobalAllocSize();
459            }
460            int id = ar.getResourceId(i, 0);
461            if (false) {
462                Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
463            }
464            if (id != 0) {
465                if (mResources.getDrawable(id, null) == null) {
466                    throw new IllegalArgumentException(
467                            "Unable to find preloaded drawable resource #0x"
468                            + Integer.toHexString(id)
469                            + " (" + ar.getString(i) + ")");
470                }
471            }
472        }
473        return N;
474    }
475
476    /**
477     * Runs several special GCs to try to clean up a few generations of
478     * softly- and final-reachable objects, along with any other garbage.
479     * This is only useful just before a fork().
480     */
481    /*package*/ static void gc() {
482        final VMRuntime runtime = VMRuntime.getRuntime();
483
484        /* runFinalizationSync() lets finalizers be called in Zygote,
485         * which doesn't have a HeapWorker thread.
486         */
487        System.gc();
488        runtime.runFinalizationSync();
489        System.gc();
490        runtime.runFinalizationSync();
491        System.gc();
492        runtime.runFinalizationSync();
493    }
494
495    /**
496     * Finish remaining work for the newly forked system server process.
497     */
498    private static void handleSystemServerProcess(
499            ZygoteConnection.Arguments parsedArgs)
500            throws ZygoteInit.MethodAndArgsCaller {
501
502        closeServerSocket();
503
504        // set umask to 0077 so new files and directories will default to owner-only permissions.
505        Os.umask(S_IRWXG | S_IRWXO);
506
507        if (parsedArgs.niceName != null) {
508            Process.setArgV0(parsedArgs.niceName);
509        }
510
511        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
512        if (systemServerClasspath != null) {
513            performSystemServerDexOpt(systemServerClasspath);
514        }
515
516        if (parsedArgs.invokeWith != null) {
517            String[] args = parsedArgs.remainingArgs;
518            // If we have a non-null system server class path, we'll have to duplicate the
519            // existing arguments and append the classpath to it. ART will handle the classpath
520            // correctly when we exec a new process.
521            if (systemServerClasspath != null) {
522                String[] amendedArgs = new String[args.length + 2];
523                amendedArgs[0] = "-cp";
524                amendedArgs[1] = systemServerClasspath;
525                System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
526            }
527
528            WrapperInit.execApplication(parsedArgs.invokeWith,
529                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
530                    null, args);
531        } else {
532            ClassLoader cl = null;
533            if (systemServerClasspath != null) {
534                cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
535                Thread.currentThread().setContextClassLoader(cl);
536            }
537
538            /*
539             * Pass the remaining arguments to SystemServer.
540             */
541            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
542        }
543
544        /* should never reach here */
545    }
546
547    /**
548     * Performs dex-opt on the elements of {@code classPath}, if needed. We
549     * choose the instruction set of the current runtime.
550     */
551    private static void performSystemServerDexOpt(String classPath) {
552        final String[] classPathElements = classPath.split(":");
553        final InstallerConnection installer = new InstallerConnection();
554        final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
555
556        try {
557            for (String classPathElement : classPathElements) {
558                final byte dexopt = DexFile.isDexOptNeededInternal(classPathElement, "*", instructionSet,
559                        false /* defer */);
560                if (dexopt == DexFile.DEXOPT_NEEDED) {
561                    installer.dexopt(classPathElement, Process.SYSTEM_UID, false, instructionSet);
562                } else if (dexopt == DexFile.PATCHOAT_NEEDED) {
563                    installer.patchoat(classPathElement, Process.SYSTEM_UID, false, instructionSet);
564                }
565            }
566        } catch (IOException ioe) {
567            throw new RuntimeException("Error starting system_server", ioe);
568        } finally {
569            installer.disconnect();
570        }
571    }
572
573    /**
574     * Prepare the arguments and fork for the system server process.
575     */
576    private static boolean startSystemServer(String abiList, String socketName)
577            throws MethodAndArgsCaller, RuntimeException {
578        long capabilities = posixCapabilitiesAsBits(
579            OsConstants.CAP_BLOCK_SUSPEND,
580            OsConstants.CAP_KILL,
581            OsConstants.CAP_NET_ADMIN,
582            OsConstants.CAP_NET_BIND_SERVICE,
583            OsConstants.CAP_NET_BROADCAST,
584            OsConstants.CAP_NET_RAW,
585            OsConstants.CAP_SYS_MODULE,
586            OsConstants.CAP_SYS_NICE,
587            OsConstants.CAP_SYS_RESOURCE,
588            OsConstants.CAP_SYS_TIME,
589            OsConstants.CAP_SYS_TTY_CONFIG
590        );
591        /* Hardcoded command line to start the system server */
592        String args[] = {
593            "--setuid=1000",
594            "--setgid=1000",
595            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007",
596            "--capabilities=" + capabilities + "," + capabilities,
597            "--runtime-init",
598            "--nice-name=system_server",
599            "com.android.server.SystemServer",
600        };
601        ZygoteConnection.Arguments parsedArgs = null;
602
603        int pid;
604
605        try {
606            parsedArgs = new ZygoteConnection.Arguments(args);
607            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
608            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
609
610            /* Request to fork the system server process */
611            pid = Zygote.forkSystemServer(
612                    parsedArgs.uid, parsedArgs.gid,
613                    parsedArgs.gids,
614                    parsedArgs.debugFlags,
615                    null,
616                    parsedArgs.permittedCapabilities,
617                    parsedArgs.effectiveCapabilities);
618        } catch (IllegalArgumentException ex) {
619            throw new RuntimeException(ex);
620        }
621
622        /* For child process */
623        if (pid == 0) {
624            if (hasSecondZygote(abiList)) {
625                waitForSecondaryZygote(socketName);
626            }
627
628            handleSystemServerProcess(parsedArgs);
629        }
630
631        return true;
632    }
633
634    /**
635     * Gets the bit array representation of the provided list of POSIX capabilities.
636     */
637    private static long posixCapabilitiesAsBits(int... capabilities) {
638        long result = 0;
639        for (int capability : capabilities) {
640            if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
641                throw new IllegalArgumentException(String.valueOf(capability));
642            }
643            result |= (1L << capability);
644        }
645        return result;
646    }
647
648    public static void main(String argv[]) {
649        try {
650            // Start profiling the zygote initialization.
651            SamplingProfilerIntegration.start();
652
653            boolean startSystemServer = false;
654            String socketName = "zygote";
655            String abiList = null;
656            for (int i = 1; i < argv.length; i++) {
657                if ("start-system-server".equals(argv[i])) {
658                    startSystemServer = true;
659                } else if (argv[i].startsWith(ABI_LIST_ARG)) {
660                    abiList = argv[i].substring(ABI_LIST_ARG.length());
661                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
662                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());
663                } else {
664                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
665                }
666            }
667
668            if (abiList == null) {
669                throw new RuntimeException("No ABI list supplied.");
670            }
671
672            registerZygoteSocket(socketName);
673            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
674                SystemClock.uptimeMillis());
675            preload();
676            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
677                SystemClock.uptimeMillis());
678
679            // Finish profiling the zygote initialization.
680            SamplingProfilerIntegration.writeZygoteSnapshot();
681
682            // Do an initial gc to clean up after startup
683            gc();
684
685            // Disable tracing so that forked processes do not inherit stale tracing tags from
686            // Zygote.
687            Trace.setTracingEnabled(false);
688
689            if (startSystemServer) {
690                startSystemServer(abiList, socketName);
691            }
692
693            Log.i(TAG, "Accepting command socket connections");
694            runSelectLoop(abiList);
695
696            closeServerSocket();
697        } catch (MethodAndArgsCaller caller) {
698            caller.run();
699        } catch (RuntimeException ex) {
700            Log.e(TAG, "Zygote died with exception", ex);
701            closeServerSocket();
702            throw ex;
703        }
704    }
705
706    /**
707     * Return {@code true} if this device configuration has another zygote.
708     *
709     * We determine this by comparing the device ABI list with this zygotes
710     * list. If this zygote supports all ABIs this device supports, there won't
711     * be another zygote.
712     */
713    private static boolean hasSecondZygote(String abiList) {
714        return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList);
715    }
716
717    private static void waitForSecondaryZygote(String socketName) {
718        String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ?
719                Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
720        while (true) {
721            try {
722                final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName);
723                zs.close();
724                break;
725            } catch (IOException ioe) {
726                Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
727            }
728
729            try {
730                Thread.sleep(1000);
731            } catch (InterruptedException ie) {
732            }
733        }
734    }
735
736    /**
737     * Runs the zygote process's select loop. Accepts new connections as
738     * they happen, and reads commands from connections one spawn-request's
739     * worth at a time.
740     *
741     * @throws MethodAndArgsCaller in a child process when a main() should
742     * be executed.
743     */
744    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
745        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
746        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
747        FileDescriptor[] fdArray = new FileDescriptor[4];
748
749        fds.add(sServerSocket.getFileDescriptor());
750        peers.add(null);
751
752        int loopCount = GC_LOOP_COUNT;
753        while (true) {
754            int index;
755
756            /*
757             * Call gc() before we block in select().
758             * It's work that has to be done anyway, and it's better
759             * to avoid making every child do it.  It will also
760             * madvise() any free memory as a side-effect.
761             *
762             * Don't call it every time, because walking the entire
763             * heap is a lot of overhead to free a few hundred bytes.
764             */
765            if (loopCount <= 0) {
766                gc();
767                loopCount = GC_LOOP_COUNT;
768            } else {
769                loopCount--;
770            }
771
772
773            try {
774                fdArray = fds.toArray(fdArray);
775                index = selectReadable(fdArray);
776            } catch (IOException ex) {
777                throw new RuntimeException("Error in select()", ex);
778            }
779
780            if (index < 0) {
781                throw new RuntimeException("Error in select()");
782            } else if (index == 0) {
783                ZygoteConnection newPeer = acceptCommandPeer(abiList);
784                peers.add(newPeer);
785                fds.add(newPeer.getFileDescriptor());
786            } else {
787                boolean done;
788                done = peers.get(index).runOnce();
789
790                if (done) {
791                    peers.remove(index);
792                    fds.remove(index);
793                }
794            }
795        }
796    }
797
798    /**
799     * The Linux syscall "setreuid()"
800     * @param ruid real uid
801     * @param euid effective uid
802     * @return 0 on success, non-zero errno on fail
803     */
804    static native int setreuid(int ruid, int euid);
805
806    /**
807     * The Linux syscall "setregid()"
808     * @param rgid real gid
809     * @param egid effective gid
810     * @return 0 on success, non-zero errno on fail
811     */
812    static native int setregid(int rgid, int egid);
813
814    /**
815     * Invokes the linux syscall "setpgid"
816     *
817     * @param pid pid to change
818     * @param pgid new process group of pid
819     * @return 0 on success or non-zero errno on fail
820     */
821    static native int setpgid(int pid, int pgid);
822
823    /**
824     * Invokes the linux syscall "getpgid"
825     *
826     * @param pid pid to query
827     * @return pgid of pid in question
828     * @throws IOException on error
829     */
830    static native int getpgid(int pid) throws IOException;
831
832    /**
833     * Invokes the syscall dup2() to copy the specified descriptors into
834     * stdin, stdout, and stderr. The existing stdio descriptors will be
835     * closed and errors during close will be ignored. The specified
836     * descriptors will also remain open at their original descriptor numbers,
837     * so the caller may want to close the original descriptors.
838     *
839     * @param in new stdin
840     * @param out new stdout
841     * @param err new stderr
842     * @throws IOException
843     */
844    static native void reopenStdio(FileDescriptor in,
845            FileDescriptor out, FileDescriptor err) throws IOException;
846
847    /**
848     * Toggles the close-on-exec flag for the specified file descriptor.
849     *
850     * @param fd non-null; file descriptor
851     * @param flag desired close-on-exec flag state
852     * @throws IOException
853     */
854    static native void setCloseOnExec(FileDescriptor fd, boolean flag)
855            throws IOException;
856
857    /**
858     * Invokes select() on the provider array of file descriptors (selecting
859     * for readability only). Array elements of null are ignored.
860     *
861     * @param fds non-null; array of readable file descriptors
862     * @return index of descriptor that is now readable or -1 for empty array.
863     * @throws IOException if an error occurs
864     */
865    static native int selectReadable(FileDescriptor[] fds) throws IOException;
866
867    /**
868     * Creates a file descriptor from an int fd.
869     *
870     * @param fd integer OS file descriptor
871     * @return non-null; FileDescriptor instance
872     * @throws IOException if fd is invalid
873     */
874    static native FileDescriptor createFileDescriptor(int fd)
875            throws IOException;
876
877    /**
878     * Class not instantiable.
879     */
880    private ZygoteInit() {
881    }
882
883    /**
884     * Helper exception class which holds a method and arguments and
885     * can call them. This is used as part of a trampoline to get rid of
886     * the initial process setup stack frames.
887     */
888    public static class MethodAndArgsCaller extends Exception
889            implements Runnable {
890        /** method to call */
891        private final Method mMethod;
892
893        /** argument array */
894        private final String[] mArgs;
895
896        public MethodAndArgsCaller(Method method, String[] args) {
897            mMethod = method;
898            mArgs = args;
899        }
900
901        public void run() {
902            try {
903                mMethod.invoke(null, new Object[] { mArgs });
904            } catch (IllegalAccessException ex) {
905                throw new RuntimeException(ex);
906            } catch (InvocationTargetException ex) {
907                Throwable cause = ex.getCause();
908                if (cause instanceof RuntimeException) {
909                    throw (RuntimeException) cause;
910                } else if (cause instanceof Error) {
911                    throw (Error) cause;
912                }
913                throw new RuntimeException(ex);
914            }
915        }
916    }
917}
918