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