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