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