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