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