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