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