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