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.ActivityManager;
20import android.app.ActivityThread;
21import android.app.ApplicationErrorReport;
22import android.os.Build;
23import android.os.DeadObjectException;
24import android.os.Debug;
25import android.os.IBinder;
26import android.os.Process;
27import android.os.SystemProperties;
28import android.os.Trace;
29import android.util.Log;
30import android.util.Slog;
31import com.android.internal.logging.AndroidConfig;
32import com.android.server.NetworkManagementSocketTagger;
33import dalvik.system.VMRuntime;
34import java.lang.reflect.InvocationTargetException;
35import java.lang.reflect.Method;
36import java.lang.reflect.Modifier;
37import java.util.TimeZone;
38import java.util.logging.LogManager;
39import org.apache.harmony.luni.internal.util.TimezoneGetter;
40
41/**
42 * Main entry point for runtime initialization.  Not for
43 * public consumption.
44 * @hide
45 */
46public class RuntimeInit {
47    final static String TAG = "AndroidRuntime";
48    final static boolean DEBUG = false;
49
50    /** true if commonInit() has been called */
51    private static boolean initialized;
52
53    private static IBinder mApplicationObject;
54
55    private static volatile boolean mCrashing = false;
56
57    private static final native void nativeFinishInit();
58    private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
59
60    private static int Clog_e(String tag, String msg, Throwable tr) {
61        return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr);
62    }
63
64    /**
65     * Logs a message when a thread encounters an uncaught exception. By
66     * default, {@link KillApplicationHandler} will terminate this process later,
67     * but apps can override that behavior.
68     */
69    private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
70        @Override
71        public void uncaughtException(Thread t, Throwable e) {
72            // Don't re-enter if KillApplicationHandler has already run
73            if (mCrashing) return;
74            if (mApplicationObject == null) {
75                // The "FATAL EXCEPTION" string is still used on Android even though
76                // apps can set a custom UncaughtExceptionHandler that renders uncaught
77                // exceptions non-fatal.
78                Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
79            } else {
80                StringBuilder message = new StringBuilder();
81                // The "FATAL EXCEPTION" string is still used on Android even though
82                // apps can set a custom UncaughtExceptionHandler that renders uncaught
83                // exceptions non-fatal.
84                message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
85                final String processName = ActivityThread.currentProcessName();
86                if (processName != null) {
87                    message.append("Process: ").append(processName).append(", ");
88                }
89                message.append("PID: ").append(Process.myPid());
90                Clog_e(TAG, message.toString(), e);
91            }
92        }
93    }
94
95    /**
96     * Handle application death from an uncaught exception.  The framework
97     * catches these for the main threads, so this should only matter for
98     * threads created by applications.  Before this method runs,
99     * {@link LoggingHandler} will already have logged details.
100     */
101    private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
102        public void uncaughtException(Thread t, Throwable e) {
103            try {
104                // Don't re-enter -- avoid infinite loops if crash-reporting crashes.
105                if (mCrashing) return;
106                mCrashing = true;
107
108                // Try to end profiling. If a profiler is running at this point, and we kill the
109                // process (below), the in-memory buffer will be lost. So try to stop, which will
110                // flush the buffer. (This makes method trace profiling useful to debug crashes.)
111                if (ActivityThread.currentActivityThread() != null) {
112                    ActivityThread.currentActivityThread().stopProfiling();
113                }
114
115                // Bring up crash dialog, wait for it to be dismissed
116                ActivityManager.getService().handleApplicationCrash(
117                        mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
118            } catch (Throwable t2) {
119                if (t2 instanceof DeadObjectException) {
120                    // System process is dead; ignore
121                } else {
122                    try {
123                        Clog_e(TAG, "Error reporting crash", t2);
124                    } catch (Throwable t3) {
125                        // Even Clog_e() fails!  Oh well.
126                    }
127                }
128            } finally {
129                // Try everything to make sure this process goes away.
130                Process.killProcess(Process.myPid());
131                System.exit(10);
132            }
133        }
134    }
135
136    protected static final void commonInit() {
137        if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
138
139        /*
140         * set handlers; these apply to all threads in the VM. Apps can replace
141         * the default handler, but not the pre handler.
142         */
143        Thread.setUncaughtExceptionPreHandler(new LoggingHandler());
144        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());
145
146        /*
147         * Install a TimezoneGetter subclass for ZoneInfo.db
148         */
149        TimezoneGetter.setInstance(new TimezoneGetter() {
150            @Override
151            public String getId() {
152                return SystemProperties.get("persist.sys.timezone");
153            }
154        });
155        TimeZone.setDefault(null);
156
157        /*
158         * Sets handler for java.util.logging to use Android log facilities.
159         * The odd "new instance-and-then-throw-away" is a mirror of how
160         * the "java.util.logging.config.class" system property works. We
161         * can't use the system property here since the logger has almost
162         * certainly already been initialized.
163         */
164        LogManager.getLogManager().reset();
165        new AndroidConfig();
166
167        /*
168         * Sets the default HTTP User-Agent used by HttpURLConnection.
169         */
170        String userAgent = getDefaultUserAgent();
171        System.setProperty("http.agent", userAgent);
172
173        /*
174         * Wire socket tagging to traffic stats.
175         */
176        NetworkManagementSocketTagger.install();
177
178        /*
179         * If we're running in an emulator launched with "-trace", put the
180         * VM into emulator trace profiling mode so that the user can hit
181         * F9/F10 at any time to capture traces.  This has performance
182         * consequences, so it's not something you want to do always.
183         */
184        String trace = SystemProperties.get("ro.kernel.android.tracing");
185        if (trace.equals("1")) {
186            Slog.i(TAG, "NOTE: emulator trace profiling enabled");
187            Debug.enableEmulatorTraceOutput();
188        }
189
190        initialized = true;
191    }
192
193    /**
194     * Returns an HTTP user agent of the form
195     * "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MASTER)".
196     */
197    private static String getDefaultUserAgent() {
198        StringBuilder result = new StringBuilder(64);
199        result.append("Dalvik/");
200        result.append(System.getProperty("java.vm.version")); // such as 1.1.0
201        result.append(" (Linux; U; Android ");
202
203        String version = Build.VERSION.RELEASE; // "1.0" or "3.4b5"
204        result.append(version.length() > 0 ? version : "1.0");
205
206        // add the model for the release build
207        if ("REL".equals(Build.VERSION.CODENAME)) {
208            String model = Build.MODEL;
209            if (model.length() > 0) {
210                result.append("; ");
211                result.append(model);
212            }
213        }
214        String id = Build.ID; // "MASTER" or "M4-rc20"
215        if (id.length() > 0) {
216            result.append(" Build/");
217            result.append(id);
218        }
219        result.append(")");
220        return result.toString();
221    }
222
223    /**
224     * Invokes a static "main(argv[]) method on class "className".
225     * Converts various failing exceptions into RuntimeExceptions, with
226     * the assumption that they will then cause the VM instance to exit.
227     *
228     * @param className Fully-qualified class name
229     * @param argv Argument vector for main()
230     * @param classLoader the classLoader to load {@className} with
231     */
232    private static Runnable findStaticMain(String className, String[] argv,
233            ClassLoader classLoader) {
234        Class<?> cl;
235
236        try {
237            cl = Class.forName(className, true, classLoader);
238        } catch (ClassNotFoundException ex) {
239            throw new RuntimeException(
240                    "Missing class when invoking static main " + className,
241                    ex);
242        }
243
244        Method m;
245        try {
246            m = cl.getMethod("main", new Class[] { String[].class });
247        } catch (NoSuchMethodException ex) {
248            throw new RuntimeException(
249                    "Missing static main on " + className, ex);
250        } catch (SecurityException ex) {
251            throw new RuntimeException(
252                    "Problem getting static main on " + className, ex);
253        }
254
255        int modifiers = m.getModifiers();
256        if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
257            throw new RuntimeException(
258                    "Main method is not public and static on " + className);
259        }
260
261        /*
262         * This throw gets caught in ZygoteInit.main(), which responds
263         * by invoking the exception's run() method. This arrangement
264         * clears up all the stack frames that were required in setting
265         * up the process.
266         */
267        return new MethodAndArgsCaller(m, argv);
268    }
269
270    public static final void main(String[] argv) {
271        enableDdms();
272        if (argv.length == 2 && argv[1].equals("application")) {
273            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
274            redirectLogStreams();
275        } else {
276            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");
277        }
278
279        commonInit();
280
281        /*
282         * Now that we're running in interpreted code, call back into native code
283         * to run the system.
284         */
285        nativeFinishInit();
286
287        if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
288    }
289
290    protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
291            ClassLoader classLoader) {
292        // If the application calls System.exit(), terminate the process
293        // immediately without running any shutdown hooks.  It is not possible to
294        // shutdown an Android application gracefully.  Among other things, the
295        // Android runtime shutdown hooks close the Binder driver, which can cause
296        // leftover running threads to crash before the process actually exits.
297        nativeSetExitWithoutCleanup(true);
298
299        // We want to be fairly aggressive about heap utilization, to avoid
300        // holding on to a lot of memory that isn't needed.
301        VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
302        VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
303
304        final Arguments args = new Arguments(argv);
305
306        // The end of of the RuntimeInit event (see #zygoteInit).
307        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
308
309        // Remaining arguments are passed to the start class's static main
310        return findStaticMain(args.startClass, args.startArgs, classLoader);
311    }
312
313    /**
314     * Redirect System.out and System.err to the Android log.
315     */
316    public static void redirectLogStreams() {
317        System.out.close();
318        System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
319        System.err.close();
320        System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
321    }
322
323    /**
324     * Report a serious error in the current process.  May or may not cause
325     * the process to terminate (depends on system settings).
326     *
327     * @param tag to record with the error
328     * @param t exception describing the error site and conditions
329     */
330    public static void wtf(String tag, Throwable t, boolean system) {
331        try {
332            if (ActivityManager.getService().handleApplicationWtf(
333                    mApplicationObject, tag, system,
334                    new ApplicationErrorReport.ParcelableCrashInfo(t))) {
335                // The Activity Manager has already written us off -- now exit.
336                Process.killProcess(Process.myPid());
337                System.exit(10);
338            }
339        } catch (Throwable t2) {
340            if (t2 instanceof DeadObjectException) {
341                // System process is dead; ignore
342            } else {
343                Slog.e(TAG, "Error reporting WTF", t2);
344                Slog.e(TAG, "Original WTF:", t);
345            }
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 DDMS.
363     */
364    static final void enableDdms() {
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    /**
421     * Helper class which holds a method and arguments and can call them. This is used as part of
422     * a trampoline to get rid of the initial process setup stack frames.
423     */
424    static class MethodAndArgsCaller implements Runnable {
425        /** method to call */
426        private final Method mMethod;
427
428        /** argument array */
429        private final String[] mArgs;
430
431        public MethodAndArgsCaller(Method method, String[] args) {
432            mMethod = method;
433            mArgs = args;
434        }
435
436        public void run() {
437            try {
438                mMethod.invoke(null, new Object[] { mArgs });
439            } catch (IllegalAccessException ex) {
440                throw new RuntimeException(ex);
441            } catch (InvocationTargetException ex) {
442                Throwable cause = ex.getCause();
443                if (cause instanceof RuntimeException) {
444                    throw (RuntimeException) cause;
445                } else if (cause instanceof Error) {
446                    throw (Error) cause;
447                }
448                throw new RuntimeException(ex);
449            }
450        }
451    }
452}
453