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