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