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