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