[go: nahoru, domu]

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                final int dexoptNeeded = DexFile.getDexOptNeeded(
590                        classPathElement, instructionSet, "speed",
591                        false /* newProfile */);
592                if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
593                    installer.dexopt(classPathElement, Process.SYSTEM_UID, instructionSet,
594                            dexoptNeeded, 0 /*dexFlags*/, "speed", null /*volumeUuid*/,
595                            sharedLibraries);
596                }
597                if (!sharedLibraries.isEmpty()) {
598                    sharedLibraries += ":";
599                }
600                sharedLibraries += classPathElement;
601            }
602        } catch (IOException | InstallerException e) {
603            throw new RuntimeException("Error starting system_server", e);
604        } finally {
605            installer.disconnect();
606        }
607    }
608
609    /**
610     * Prepare the arguments and fork for the system server process.
611     */
612    private static boolean startSystemServer(String abiList, String socketName)
613            throws MethodAndArgsCaller, RuntimeException {
614        long capabilities = posixCapabilitiesAsBits(
615            OsConstants.CAP_IPC_LOCK,
616            OsConstants.CAP_KILL,
617            OsConstants.CAP_NET_ADMIN,
618            OsConstants.CAP_NET_BIND_SERVICE,
619            OsConstants.CAP_NET_BROADCAST,
620            OsConstants.CAP_NET_RAW,
621            OsConstants.CAP_SYS_MODULE,
622            OsConstants.CAP_SYS_NICE,
623            OsConstants.CAP_SYS_RESOURCE,
624            OsConstants.CAP_SYS_TIME,
625            OsConstants.CAP_SYS_TTY_CONFIG
626        );
627        /* Containers run without this capability, so avoid setting it in that case */
628        if (!SystemProperties.getBoolean(PROPERTY_RUNNING_IN_CONTAINER, false)) {
629            capabilities |= posixCapabilitiesAsBits(OsConstants.CAP_BLOCK_SUSPEND);
630        }
631        /* Hardcoded command line to start the system server */
632        String args[] = {
633            "--setuid=1000",
634            "--setgid=1000",
635            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007,3009,3010",
636            "--capabilities=" + capabilities + "," + capabilities,
637            "--nice-name=system_server",
638            "--runtime-args",
639            "com.android.server.SystemServer",
640        };
641        ZygoteConnection.Arguments parsedArgs = null;
642
643        int pid;
644
645        try {
646            parsedArgs = new ZygoteConnection.Arguments(args);
647            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
648            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
649
650            /* Request to fork the system server process */
651            pid = Zygote.forkSystemServer(
652                    parsedArgs.uid, parsedArgs.gid,
653                    parsedArgs.gids,
654                    parsedArgs.debugFlags,
655                    null,
656                    parsedArgs.permittedCapabilities,
657                    parsedArgs.effectiveCapabilities);
658        } catch (IllegalArgumentException ex) {
659            throw new RuntimeException(ex);
660        }
661
662        /* For child process */
663        if (pid == 0) {
664            if (hasSecondZygote(abiList)) {
665                waitForSecondaryZygote(socketName);
666            }
667
668            handleSystemServerProcess(parsedArgs);
669        }
670
671        return true;
672    }
673
674    /**
675     * Gets the bit array representation of the provided list of POSIX capabilities.
676     */
677    private static long posixCapabilitiesAsBits(int... capabilities) {
678        long result = 0;
679        for (int capability : capabilities) {
680            if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
681                throw new IllegalArgumentException(String.valueOf(capability));
682            }
683            result |= (1L << capability);
684        }
685        return result;
686    }
687
688    public static void main(String argv[]) {
689        // Mark zygote start. This ensures that thread creation will throw
690        // an error.
691        ZygoteHooks.startZygoteNoThreadCreation();
692
693        try {
694            Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit");
695            RuntimeInit.enableDdms();
696            // Start profiling the zygote initialization.
697            SamplingProfilerIntegration.start();
698
699            boolean startSystemServer = false;
700            String socketName = "zygote";
701            String abiList = null;
702            for (int i = 1; i < argv.length; i++) {
703                if ("start-system-server".equals(argv[i])) {
704                    startSystemServer = true;
705                } else if (argv[i].startsWith(ABI_LIST_ARG)) {
706                    abiList = argv[i].substring(ABI_LIST_ARG.length());
707                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
708                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());
709                } else {
710                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
711                }
712            }
713
714            if (abiList == null) {
715                throw new RuntimeException("No ABI list supplied.");
716            }
717
718            registerZygoteSocket(socketName);
719            Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygotePreload");
720            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
721                SystemClock.uptimeMillis());
722            preload();
723            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
724                SystemClock.uptimeMillis());
725            Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
726
727            // Finish profiling the zygote initialization.
728            SamplingProfilerIntegration.writeZygoteSnapshot();
729
730            // Do an initial gc to clean up after startup
731            Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PostZygoteInitGC");
732            gcAndFinalize();
733            Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
734
735            Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
736
737            // Disable tracing so that forked processes do not inherit stale tracing tags from
738            // Zygote.
739            Trace.setTracingEnabled(false);
740
741            // Zygote process unmounts root storage spaces.
742            Zygote.nativeUnmountStorageOnInit();
743
744            ZygoteHooks.stopZygoteNoThreadCreation();
745
746            if (startSystemServer) {
747                startSystemServer(abiList, socketName);
748            }
749
750            Log.i(TAG, "Accepting command socket connections");
751            runSelectLoop(abiList);
752
753            closeServerSocket();
754        } catch (MethodAndArgsCaller caller) {
755            caller.run();
756        } catch (RuntimeException ex) {
757            Log.e(TAG, "Zygote died with exception", ex);
758            closeServerSocket();
759            throw ex;
760        }
761    }
762
763    /**
764     * Return {@code true} if this device configuration has another zygote.
765     *
766     * We determine this by comparing the device ABI list with this zygotes
767     * list. If this zygote supports all ABIs this device supports, there won't
768     * be another zygote.
769     */
770    private static boolean hasSecondZygote(String abiList) {
771        return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList);
772    }
773
774    private static void waitForSecondaryZygote(String socketName) {
775        String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ?
776                Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
777        while (true) {
778            try {
779                final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName);
780                zs.close();
781                break;
782            } catch (IOException ioe) {
783                Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
784            }
785
786            try {
787                Thread.sleep(1000);
788            } catch (InterruptedException ie) {
789            }
790        }
791    }
792
793    /**
794     * Runs the zygote process's select loop. Accepts new connections as
795     * they happen, and reads commands from connections one spawn-request's
796     * worth at a time.
797     *
798     * @throws MethodAndArgsCaller in a child process when a main() should
799     * be executed.
800     */
801    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
802        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
803        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
804
805        fds.add(sServerSocket.getFileDescriptor());
806        peers.add(null);
807
808        while (true) {
809            StructPollfd[] pollFds = new StructPollfd[fds.size()];
810            for (int i = 0; i < pollFds.length; ++i) {
811                pollFds[i] = new StructPollfd();
812                pollFds[i].fd = fds.get(i);
813                pollFds[i].events = (short) POLLIN;
814            }
815            try {
816                Os.poll(pollFds, -1);
817            } catch (ErrnoException ex) {
818                throw new RuntimeException("poll failed", ex);
819            }
820            for (int i = pollFds.length - 1; i >= 0; --i) {
821                if ((pollFds[i].revents & POLLIN) == 0) {
822                    continue;
823                }
824                if (i == 0) {
825                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
826                    peers.add(newPeer);
827                    fds.add(newPeer.getFileDesciptor());
828                } else {
829                    boolean done = peers.get(i).runOnce();
830                    if (done) {
831                        peers.remove(i);
832                        fds.remove(i);
833                    }
834                }
835            }
836        }
837    }
838
839    /**
840     * Class not instantiable.
841     */
842    private ZygoteInit() {
843    }
844
845    /**
846     * Helper exception class which holds a method and arguments and
847     * can call them. This is used as part of a trampoline to get rid of
848     * the initial process setup stack frames.
849     */
850    public static class MethodAndArgsCaller extends Exception
851            implements Runnable {
852        /** method to call */
853        private final Method mMethod;
854
855        /** argument array */
856        private final String[] mArgs;
857
858        public MethodAndArgsCaller(Method method, String[] args) {
859            mMethod = method;
860            mArgs = args;
861        }
862
863        public void run() {
864            try {
865                mMethod.invoke(null, new Object[] { mArgs });
866            } catch (IllegalAccessException ex) {
867                throw new RuntimeException(ex);
868            } catch (InvocationTargetException ex) {
869                Throwable cause = ex.getCause();
870                if (cause instanceof RuntimeException) {
871                    throw (RuntimeException) cause;
872                } else if (cause instanceof Error) {
873                    throw (Error) cause;
874                }
875                throw new RuntimeException(ex);
876            }
877        }
878    }
879}
880