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