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.Objects;
38import java.util.TimeZone;
39import java.util.logging.LogManager;
40import org.apache.harmony.luni.internal.util.TimezoneGetter;
41
42/**
43 * Main entry point for runtime initialization.  Not for
44 * public consumption.
45 * @hide
46 */
47public class RuntimeInit {
48    final static String TAG = "AndroidRuntime";
49    final static boolean DEBUG = false;
50
51    /** true if commonInit() has been called */
52    private static boolean initialized;
53
54    private static IBinder mApplicationObject;
55
56    private static volatile boolean mCrashing = false;
57
58    private static final native void nativeFinishInit();
59    private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
60
61    private static int Clog_e(String tag, String msg, Throwable tr) {
62        return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr);
63    }
64
65    /**
66     * Logs a message when a thread encounters an uncaught exception. By
67     * default, {@link KillApplicationHandler} will terminate this process later,
68     * but apps can override that behavior.
69     */
70    private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
71        public volatile boolean mTriggered = false;
72
73        @Override
74        public void uncaughtException(Thread t, Throwable e) {
75            mTriggered = true;
76
77            // Don't re-enter if KillApplicationHandler has already run
78            if (mCrashing) return;
79
80            // mApplicationObject is null for non-zygote java programs (e.g. "am")
81            // There are also apps running with the system UID. We don't want the
82            // first clause in either of these two cases, only for system_server.
83            if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
84                Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
85            } else {
86                StringBuilder message = new StringBuilder();
87                // The "FATAL EXCEPTION" string is still used on Android even though
88                // apps can set a custom UncaughtExceptionHandler that renders uncaught
89                // exceptions non-fatal.
90                message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
91                final String processName = ActivityThread.currentProcessName();
92                if (processName != null) {
93                    message.append("Process: ").append(processName).append(", ");
94                }
95                message.append("PID: ").append(Process.myPid());
96                Clog_e(TAG, message.toString(), e);
97            }
98        }
99    }
100
101    /**
102     * Handle application death from an uncaught exception.  The framework
103     * catches these for the main threads, so this should only matter for
104     * threads created by applications. Before this method runs, the given
105     * instance of {@link LoggingHandler} should already have logged details
106     * (and if not it is run first).
107     */
108    private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
109        private final LoggingHandler mLoggingHandler;
110
111        /**
112         * Create a new KillApplicationHandler that follows the given LoggingHandler.
113         * If {@link #uncaughtException(Thread, Throwable) uncaughtException} is called
114         * on the created instance without {@code loggingHandler} having been triggered,
115         * {@link LoggingHandler#uncaughtException(Thread, Throwable)
116         * loggingHandler.uncaughtException} will be called first.
117         *
118         * @param loggingHandler the {@link LoggingHandler} expected to have run before
119         *     this instance's {@link #uncaughtException(Thread, Throwable) uncaughtException}
120         *     is being called.
121         */
122        public KillApplicationHandler(LoggingHandler loggingHandler) {
123            this.mLoggingHandler = Objects.requireNonNull(loggingHandler);
124        }
125
126        @Override
127        public void uncaughtException(Thread t, Throwable e) {
128            try {
129                ensureLogging(t, e);
130
131                // Don't re-enter -- avoid infinite loops if crash-reporting crashes.
132                if (mCrashing) return;
133                mCrashing = true;
134
135                // Try to end profiling. If a profiler is running at this point, and we kill the
136                // process (below), the in-memory buffer will be lost. So try to stop, which will
137                // flush the buffer. (This makes method trace profiling useful to debug crashes.)
138                if (ActivityThread.currentActivityThread() != null) {
139                    ActivityThread.currentActivityThread().stopProfiling();
140                }
141
142                // Bring up crash dialog, wait for it to be dismissed
143                ActivityManager.getService().handleApplicationCrash(
144                        mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
145            } catch (Throwable t2) {
146                if (t2 instanceof DeadObjectException) {
147                    // System process is dead; ignore
148                } else {
149                    try {
150                        Clog_e(TAG, "Error reporting crash", t2);
151                    } catch (Throwable t3) {
152                        // Even Clog_e() fails!  Oh well.
153                    }
154                }
155            } finally {
156                // Try everything to make sure this process goes away.
157                Process.killProcess(Process.myPid());
158                System.exit(10);
159            }
160        }
161
162        /**
163         * Ensures that the logging handler has been triggered.
164         *
165         * See b/73380984. This reinstates the pre-O behavior of
166         *
167         *   {@code thread.getUncaughtExceptionHandler().uncaughtException(thread, e);}
168         *
169         * logging the exception (in addition to killing the app). This behavior
170         * was never documented / guaranteed but helps in diagnostics of apps
171         * using the pattern.
172         *
173         * If this KillApplicationHandler is invoked the "regular" way (by
174         * {@link Thread#dispatchUncaughtException(Throwable)
175         * Thread.dispatchUncaughtException} in case of an uncaught exception)
176         * then the pre-handler (expected to be {@link #mLoggingHandler}) will already
177         * have run. Otherwise, we manually invoke it here.
178         */
179        private void ensureLogging(Thread t, Throwable e) {
180            if (!mLoggingHandler.mTriggered) {
181                try {
182                    mLoggingHandler.uncaughtException(t, e);
183                } catch (Throwable loggingThrowable) {
184                    // Ignored.
185                }
186            }
187        }
188    }
189
190    protected static final void commonInit() {
191        if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
192
193        /*
194         * set handlers; these apply to all threads in the VM. Apps can replace
195         * the default handler, but not the pre handler.
196         */
197        LoggingHandler loggingHandler = new LoggingHandler();
198        Thread.setUncaughtExceptionPreHandler(loggingHandler);
199        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
200
201        /*
202         * Install a TimezoneGetter subclass for ZoneInfo.db
203         */
204        TimezoneGetter.setInstance(new TimezoneGetter() {
205            @Override
206            public String getId() {
207                return SystemProperties.get("persist.sys.timezone");
208            }
209        });
210        TimeZone.setDefault(null);
211
212        /*
213         * Sets handler for java.util.logging to use Android log facilities.
214         * The odd "new instance-and-then-throw-away" is a mirror of how
215         * the "java.util.logging.config.class" system property works. We
216         * can't use the system property here since the logger has almost
217         * certainly already been initialized.
218         */
219        LogManager.getLogManager().reset();
220        new AndroidConfig();
221
222        /*
223         * Sets the default HTTP User-Agent used by HttpURLConnection.
224         */
225        String userAgent = getDefaultUserAgent();
226        System.setProperty("http.agent", userAgent);
227
228        /*
229         * Wire socket tagging to traffic stats.
230         */
231        NetworkManagementSocketTagger.install();
232
233        /*
234         * If we're running in an emulator launched with "-trace", put the
235         * VM into emulator trace profiling mode so that the user can hit
236         * F9/F10 at any time to capture traces.  This has performance
237         * consequences, so it's not something you want to do always.
238         */
239        String trace = SystemProperties.get("ro.kernel.android.tracing");
240        if (trace.equals("1")) {
241            Slog.i(TAG, "NOTE: emulator trace profiling enabled");
242            Debug.enableEmulatorTraceOutput();
243        }
244
245        initialized = true;
246    }
247
248    /**
249     * Returns an HTTP user agent of the form
250     * "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MASTER)".
251     */
252    private static String getDefaultUserAgent() {
253        StringBuilder result = new StringBuilder(64);
254        result.append("Dalvik/");
255        result.append(System.getProperty("java.vm.version")); // such as 1.1.0
256        result.append(" (Linux; U; Android ");
257
258        String version = Build.VERSION.RELEASE; // "1.0" or "3.4b5"
259        result.append(version.length() > 0 ? version : "1.0");
260
261        // add the model for the release build
262        if ("REL".equals(Build.VERSION.CODENAME)) {
263            String model = Build.MODEL;
264            if (model.length() > 0) {
265                result.append("; ");
266                result.append(model);
267            }
268        }
269        String id = Build.ID; // "MASTER" or "M4-rc20"
270        if (id.length() > 0) {
271            result.append(" Build/");
272            result.append(id);
273        }
274        result.append(")");
275        return result.toString();
276    }
277
278    /**
279     * Invokes a static "main(argv[]) method on class "className".
280     * Converts various failing exceptions into RuntimeExceptions, with
281     * the assumption that they will then cause the VM instance to exit.
282     *
283     * @param className Fully-qualified class name
284     * @param argv Argument vector for main()
285     * @param classLoader the classLoader to load {@className} with
286     */
287    protected static Runnable findStaticMain(String className, String[] argv,
288            ClassLoader classLoader) {
289        Class<?> cl;
290
291        try {
292            cl = Class.forName(className, true, classLoader);
293        } catch (ClassNotFoundException ex) {
294            throw new RuntimeException(
295                    "Missing class when invoking static main " + className,
296                    ex);
297        }
298
299        Method m;
300        try {
301            m = cl.getMethod("main", new Class[] { String[].class });
302        } catch (NoSuchMethodException ex) {
303            throw new RuntimeException(
304                    "Missing static main on " + className, ex);
305        } catch (SecurityException ex) {
306            throw new RuntimeException(
307                    "Problem getting static main on " + className, ex);
308        }
309
310        int modifiers = m.getModifiers();
311        if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
312            throw new RuntimeException(
313                    "Main method is not public and static on " + className);
314        }
315
316        /*
317         * This throw gets caught in ZygoteInit.main(), which responds
318         * by invoking the exception's run() method. This arrangement
319         * clears up all the stack frames that were required in setting
320         * up the process.
321         */
322        return new MethodAndArgsCaller(m, argv);
323    }
324
325    public static final void main(String[] argv) {
326        enableDdms();
327        if (argv.length == 2 && argv[1].equals("application")) {
328            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
329            redirectLogStreams();
330        } else {
331            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");
332        }
333
334        commonInit();
335
336        /*
337         * Now that we're running in interpreted code, call back into native code
338         * to run the system.
339         */
340        nativeFinishInit();
341
342        if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
343    }
344
345    protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
346            ClassLoader classLoader) {
347        // If the application calls System.exit(), terminate the process
348        // immediately without running any shutdown hooks.  It is not possible to
349        // shutdown an Android application gracefully.  Among other things, the
350        // Android runtime shutdown hooks close the Binder driver, which can cause
351        // leftover running threads to crash before the process actually exits.
352        nativeSetExitWithoutCleanup(true);
353
354        // We want to be fairly aggressive about heap utilization, to avoid
355        // holding on to a lot of memory that isn't needed.
356        VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
357        VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
358
359        final Arguments args = new Arguments(argv);
360
361        // The end of of the RuntimeInit event (see #zygoteInit).
362        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
363
364        // Remaining arguments are passed to the start class's static main
365        return findStaticMain(args.startClass, args.startArgs, classLoader);
366    }
367
368    /**
369     * Redirect System.out and System.err to the Android log.
370     */
371    public static void redirectLogStreams() {
372        System.out.close();
373        System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
374        System.err.close();
375        System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
376    }
377
378    /**
379     * Report a serious error in the current process.  May or may not cause
380     * the process to terminate (depends on system settings).
381     *
382     * @param tag to record with the error
383     * @param t exception describing the error site and conditions
384     */
385    public static void wtf(String tag, Throwable t, boolean system) {
386        try {
387            if (ActivityManager.getService().handleApplicationWtf(
388                    mApplicationObject, tag, system,
389                    new ApplicationErrorReport.ParcelableCrashInfo(t))) {
390                // The Activity Manager has already written us off -- now exit.
391                Process.killProcess(Process.myPid());
392                System.exit(10);
393            }
394        } catch (Throwable t2) {
395            if (t2 instanceof DeadObjectException) {
396                // System process is dead; ignore
397            } else {
398                Slog.e(TAG, "Error reporting WTF", t2);
399                Slog.e(TAG, "Original WTF:", t);
400            }
401        }
402    }
403
404    /**
405     * Set the object identifying this application/process, for reporting VM
406     * errors.
407     */
408    public static final void setApplicationObject(IBinder app) {
409        mApplicationObject = app;
410    }
411
412    public static final IBinder getApplicationObject() {
413        return mApplicationObject;
414    }
415
416    /**
417     * Enable DDMS.
418     */
419    static final void enableDdms() {
420        // Register handlers for DDM messages.
421        android.ddm.DdmRegister.registerHandlers();
422    }
423
424    /**
425     * Handles argument parsing for args related to the runtime.
426     *
427     * Current recognized args:
428     * <ul>
429     *   <li> <code> [--] &lt;start class name&gt;  &lt;args&gt;
430     * </ul>
431     */
432    static class Arguments {
433        /** first non-option argument */
434        String startClass;
435
436        /** all following arguments */
437        String[] startArgs;
438
439        /**
440         * Constructs instance and parses args
441         * @param args runtime command-line args
442         * @throws IllegalArgumentException
443         */
444        Arguments(String args[]) throws IllegalArgumentException {
445            parseArgs(args);
446        }
447
448        /**
449         * Parses the commandline arguments intended for the Runtime.
450         */
451        private void parseArgs(String args[])
452                throws IllegalArgumentException {
453            int curArg = 0;
454            for (; curArg < args.length; curArg++) {
455                String arg = args[curArg];
456
457                if (arg.equals("--")) {
458                    curArg++;
459                    break;
460                } else if (!arg.startsWith("--")) {
461                    break;
462                }
463            }
464
465            if (curArg == args.length) {
466                throw new IllegalArgumentException("Missing classname argument to RuntimeInit!");
467            }
468
469            startClass = args[curArg++];
470            startArgs = new String[args.length - curArg];
471            System.arraycopy(args, curArg, startArgs, 0, startArgs.length);
472        }
473    }
474
475    /**
476     * Helper class which holds a method and arguments and can call them. This is used as part of
477     * a trampoline to get rid of the initial process setup stack frames.
478     */
479    static class MethodAndArgsCaller implements Runnable {
480        /** method to call */
481        private final Method mMethod;
482
483        /** argument array */
484        private final String[] mArgs;
485
486        public MethodAndArgsCaller(Method method, String[] args) {
487            mMethod = method;
488            mArgs = args;
489        }
490
491        public void run() {
492            try {
493                mMethod.invoke(null, new Object[] { mArgs });
494            } catch (IllegalAccessException ex) {
495                throw new RuntimeException(ex);
496            } catch (InvocationTargetException ex) {
497                Throwable cause = ex.getCause();
498                if (cause instanceof RuntimeException) {
499                    throw (RuntimeException) cause;
500                } else if (cause instanceof Error) {
501                    throw (Error) cause;
502                }
503                throw new RuntimeException(ex);
504            }
505        }
506    }
507}
508