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