RuntimeInit.java revision 35980b2cf51c06c1bdad66b7f8fb629d9c17052f
1/*
2 * Copyright (C) 2006 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 android.app.ActivityManagerNative;
20import android.app.ActivityThread;
21import android.app.ApplicationErrorReport;
22import android.os.Build;
23import android.os.Debug;
24import android.os.IBinder;
25import android.os.Process;
26import android.os.SystemProperties;
27import android.util.Log;
28import android.util.Slog;
29import com.android.internal.logging.AndroidConfig;
30import com.android.server.NetworkManagementSocketTagger;
31import dalvik.system.VMRuntime;
32import java.lang.reflect.Method;
33import java.lang.reflect.Modifier;
34import java.util.TimeZone;
35import java.util.logging.LogManager;
36import org.apache.harmony.luni.internal.util.TimezoneGetter;
37
38/**
39 * Main entry point for runtime initialization.  Not for
40 * public consumption.
41 * @hide
42 */
43public class RuntimeInit {
44    private final static String TAG = "AndroidRuntime";
45    private final static boolean DEBUG = false;
46
47    /** true if commonInit() has been called */
48    private static boolean initialized;
49
50    private static IBinder mApplicationObject;
51
52    private static volatile boolean mCrashing = false;
53
54    private static final native void nativeZygoteInit();
55    private static final native void nativeFinishInit();
56    private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
57
58    /**
59     * Use this to log a message when a thread exits due to an uncaught
60     * exception.  The framework catches these for the main threads, so
61     * this should only matter for threads created by applications.
62     */
63    private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
64        public void uncaughtException(Thread t, Throwable e) {
65            try {
66                // Don't re-enter -- avoid infinite loops if crash-reporting crashes.
67                if (mCrashing) return;
68                mCrashing = true;
69
70                if (mApplicationObject == null) {
71                    Slog.e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
72                } else {
73                    StringBuilder message = new StringBuilder();
74                    message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
75                    final String processName = ActivityThread.currentProcessName();
76                    if (processName != null) {
77                        message.append("Process: ").append(processName).append(", ");
78                    }
79                    message.append("PID: ").append(Process.myPid());
80                    Slog.e(TAG, message.toString(), e);
81                }
82
83                // Bring up crash dialog, wait for it to be dismissed
84                ActivityManagerNative.getDefault().handleApplicationCrash(
85                        mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
86            } catch (Throwable t2) {
87                try {
88                    Slog.e(TAG, "Error reporting crash", t2);
89                } catch (Throwable t3) {
90                    // Even Slog.e() fails!  Oh well.
91                }
92            } finally {
93                // Try everything to make sure this process goes away.
94                Process.killProcess(Process.myPid());
95                System.exit(10);
96            }
97        }
98    }
99
100    private static final void commonInit() {
101        if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
102
103        /* set default handler; this applies to all threads in the VM */
104        Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
105
106        /*
107         * Install a TimezoneGetter subclass for ZoneInfo.db
108         */
109        TimezoneGetter.setInstance(new TimezoneGetter() {
110            @Override
111            public String getId() {
112                return SystemProperties.get("persist.sys.timezone");
113            }
114        });
115        TimeZone.setDefault(null);
116
117        /*
118         * Sets handler for java.util.logging to use Android log facilities.
119         * The odd "new instance-and-then-throw-away" is a mirror of how
120         * the "java.util.logging.config.class" system property works. We
121         * can't use the system property here since the logger has almost
122         * certainly already been initialized.
123         */
124        LogManager.getLogManager().reset();
125        new AndroidConfig();
126
127        /*
128         * Sets the default HTTP User-Agent used by HttpURLConnection.
129         */
130        String userAgent = getDefaultUserAgent();
131        System.setProperty("http.agent", userAgent);
132
133        /*
134         * Wire socket tagging to traffic stats.
135         */
136        NetworkManagementSocketTagger.install();
137
138        /*
139         * If we're running in an emulator launched with "-trace", put the
140         * VM into emulator trace profiling mode so that the user can hit
141         * F9/F10 at any time to capture traces.  This has performance
142         * consequences, so it's not something you want to do always.
143         */
144        String trace = SystemProperties.get("ro.kernel.android.tracing");
145        if (trace.equals("1")) {
146            Slog.i(TAG, "NOTE: emulator trace profiling enabled");
147            Debug.enableEmulatorTraceOutput();
148        }
149
150        initialized = true;
151    }
152
153    /**
154     * Returns an HTTP user agent of the form
155     * "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MASTER)".
156     */
157    private static String getDefaultUserAgent() {
158        StringBuilder result = new StringBuilder(64);
159        result.append("Dalvik/");
160        result.append(System.getProperty("java.vm.version")); // such as 1.1.0
161        result.append(" (Linux; U; Android ");
162
163        String version = Build.VERSION.RELEASE; // "1.0" or "3.4b5"
164        result.append(version.length() > 0 ? version : "1.0");
165
166        // add the model for the release build
167        if ("REL".equals(Build.VERSION.CODENAME)) {
168            String model = Build.MODEL;
169            if (model.length() > 0) {
170                result.append("; ");
171                result.append(model);
172            }
173        }
174        String id = Build.ID; // "MASTER" or "M4-rc20"
175        if (id.length() > 0) {
176            result.append(" Build/");
177            result.append(id);
178        }
179        result.append(")");
180        return result.toString();
181    }
182
183    /**
184     * Invokes a static "main(argv[]) method on class "className".
185     * Converts various failing exceptions into RuntimeExceptions, with
186     * the assumption that they will then cause the VM instance to exit.
187     *
188     * @param className Fully-qualified class name
189     * @param argv Argument vector for main()
190     */
191    private static void invokeStaticMain(String className, String[] argv)
192            throws ZygoteInit.MethodAndArgsCaller {
193        Class<?> cl;
194
195        try {
196            cl = Class.forName(className);
197        } catch (ClassNotFoundException ex) {
198            throw new RuntimeException(
199                    "Missing class when invoking static main " + className,
200                    ex);
201        }
202
203        Method m;
204        try {
205            m = cl.getMethod("main", new Class[] { String[].class });
206        } catch (NoSuchMethodException ex) {
207            throw new RuntimeException(
208                    "Missing static main on " + className, ex);
209        } catch (SecurityException ex) {
210            throw new RuntimeException(
211                    "Problem getting static main on " + className, ex);
212        }
213
214        int modifiers = m.getModifiers();
215        if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
216            throw new RuntimeException(
217                    "Main method is not public and static on " + className);
218        }
219
220        /*
221         * This throw gets caught in ZygoteInit.main(), which responds
222         * by invoking the exception's run() method. This arrangement
223         * clears up all the stack frames that were required in setting
224         * up the process.
225         */
226        throw new ZygoteInit.MethodAndArgsCaller(m, argv);
227    }
228
229    public static final void main(String[] argv) {
230        if (argv.length == 2 && argv[1].equals("application")) {
231            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
232            redirectLogStreams();
233        } else {
234            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");
235        }
236
237        commonInit();
238
239        /*
240         * Now that we're running in interpreted code, call back into native code
241         * to run the system.
242         */
243        nativeFinishInit();
244
245        if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
246    }
247
248    /**
249     * The main function called when started through the zygote process. This
250     * could be unified with main(), if the native code in nativeFinishInit()
251     * were rationalized with Zygote startup.<p>
252     *
253     * Current recognized args:
254     * <ul>
255     *   <li> <code> [--] &lt;start class name&gt;  &lt;args&gt;
256     * </ul>
257     *
258     * @param targetSdkVersion target SDK version
259     * @param argv arg strings
260     */
261    public static final void zygoteInit(int targetSdkVersion, String[] argv)
262            throws ZygoteInit.MethodAndArgsCaller {
263        if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
264
265        redirectLogStreams();
266
267        commonInit();
268        nativeZygoteInit();
269
270        applicationInit(targetSdkVersion, argv);
271    }
272
273    /**
274     * The main function called when an application is started through a
275     * wrapper process.
276     *
277     * When the wrapper starts, the runtime starts {@link RuntimeInit#main}
278     * which calls {@link WrapperInit#main} which then calls this method.
279     * So we don't need to call commonInit() here.
280     *
281     * @param targetSdkVersion target SDK version
282     * @param argv arg strings
283     */
284    public static void wrapperInit(int targetSdkVersion, String[] argv)
285            throws ZygoteInit.MethodAndArgsCaller {
286        if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper");
287
288        applicationInit(targetSdkVersion, argv);
289    }
290
291    private static void applicationInit(int targetSdkVersion, String[] argv)
292            throws ZygoteInit.MethodAndArgsCaller {
293        // If the application calls System.exit(), terminate the process
294        // immediately without running any shutdown hooks.  It is not possible to
295        // shutdown an Android application gracefully.  Among other things, the
296        // Android runtime shutdown hooks close the Binder driver, which can cause
297        // leftover running threads to crash before the process actually exits.
298        nativeSetExitWithoutCleanup(true);
299
300        // We want to be fairly aggressive about heap utilization, to avoid
301        // holding on to a lot of memory that isn't needed.
302        VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
303        VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
304
305        final Arguments args;
306        try {
307            args = new Arguments(argv);
308        } catch (IllegalArgumentException ex) {
309            Slog.e(TAG, ex.getMessage());
310            // let the process exit
311            return;
312        }
313
314        // Remaining arguments are passed to the start class's static main
315        invokeStaticMain(args.startClass, args.startArgs);
316    }
317
318    /**
319     * Redirect System.out and System.err to the Android log.
320     */
321    public static void redirectLogStreams() {
322        System.out.close();
323        System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
324        System.err.close();
325        System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
326    }
327
328    /**
329     * Report a serious error in the current process.  May or may not cause
330     * the process to terminate (depends on system settings).
331     *
332     * @param tag to record with the error
333     * @param t exception describing the error site and conditions
334     */
335    public static void wtf(String tag, Throwable t) {
336        try {
337            if (ActivityManagerNative.getDefault().handleApplicationWtf(
338                    mApplicationObject, tag, new ApplicationErrorReport.CrashInfo(t))) {
339                // The Activity Manager has already written us off -- now exit.
340                Process.killProcess(Process.myPid());
341                System.exit(10);
342            }
343        } catch (Throwable t2) {
344            Slog.e(TAG, "Error reporting WTF", t2);
345            Slog.e(TAG, "Original WTF:", t);
346        }
347    }
348
349    /**
350     * Set the object identifying this application/process, for reporting VM
351     * errors.
352     */
353    public static final void setApplicationObject(IBinder app) {
354        mApplicationObject = app;
355    }
356
357    public static final IBinder getApplicationObject() {
358        return mApplicationObject;
359    }
360
361    /**
362     * Enable debugging features.
363     */
364    static {
365        // Register handlers for DDM messages.
366        android.ddm.DdmRegister.registerHandlers();
367    }
368
369    /**
370     * Handles argument parsing for args related to the runtime.
371     *
372     * Current recognized args:
373     * <ul>
374     *   <li> <code> [--] &lt;start class name&gt;  &lt;args&gt;
375     * </ul>
376     */
377    static class Arguments {
378        /** first non-option argument */
379        String startClass;
380
381        /** all following arguments */
382        String[] startArgs;
383
384        /**
385         * Constructs instance and parses args
386         * @param args runtime command-line args
387         * @throws IllegalArgumentException
388         */
389        Arguments(String args[]) throws IllegalArgumentException {
390            parseArgs(args);
391        }
392
393        /**
394         * Parses the commandline arguments intended for the Runtime.
395         */
396        private void parseArgs(String args[])
397                throws IllegalArgumentException {
398            int curArg = 0;
399            for (; curArg < args.length; curArg++) {
400                String arg = args[curArg];
401
402                if (arg.equals("--")) {
403                    curArg++;
404                    break;
405                } else if (!arg.startsWith("--")) {
406                    break;
407                }
408            }
409
410            if (curArg == args.length) {
411                throw new IllegalArgumentException("Missing classname argument to RuntimeInit!");
412            }
413
414            startClass = args[curArg++];
415            startArgs = new String[args.length - curArg];
416            System.arraycopy(args, curArg, startArgs, 0, startArgs.length);
417        }
418    }
419}
420