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 Clog_e(TAG, "Error reporting crash", t2); 178 } catch (Throwable t3) { 179 // Even Clog_e() fails! Oh well. 180 } 181 } 182 } finally { 183 // Try everything to make sure this process goes away. 184 Process.killProcess(Process.myPid()); 185 System.exit(10); 186 } 187 } 188 189 /** 190 * Ensures that the logging handler has been triggered. 191 * 192 * See b/73380984. This reinstates the pre-O behavior of 193 * 194 * {@code thread.getUncaughtExceptionHandler().uncaughtException(thread, e);} 195 * 196 * logging the exception (in addition to killing the app). This behavior 197 * was never documented / guaranteed but helps in diagnostics of apps 198 * using the pattern. 199 * 200 * If this KillApplicationHandler is invoked the "regular" way (by 201 * {@link Thread#dispatchUncaughtException(Throwable) 202 * Thread.dispatchUncaughtException} in case of an uncaught exception) 203 * then the pre-handler (expected to be {@link #mLoggingHandler}) will already 204 * have run. Otherwise, we manually invoke it here. 205 */ ensureLogging(Thread t, Throwable e)206 private void ensureLogging(Thread t, Throwable e) { 207 if (!mLoggingHandler.mTriggered) { 208 try { 209 mLoggingHandler.uncaughtException(t, e); 210 } catch (Throwable loggingThrowable) { 211 // Ignored. 212 } 213 } 214 } 215 } 216 217 /** 218 * Common initialization that (unlike {@link #commonInit()} should happen prior to 219 * the Zygote fork. 220 */ preForkInit()221 public static void preForkInit() { 222 if (DEBUG) Slog.d(TAG, "Entered preForkInit."); 223 RuntimeInit.enableDdms(); 224 // TODO(b/142019040#comment13): Decide whether to load the default instance eagerly, i.e. 225 // MimeMap.setDefault(DefaultMimeMapFactory.create()); 226 /* 227 * Replace libcore's minimal default mapping between MIME types and file 228 * extensions with a mapping that's suitable for Android. Android's mapping 229 * contains many more entries that are derived from IANA registrations but 230 * with several customizations (extensions, overrides). 231 */ 232 MimeMap.setDefaultSupplier(DefaultMimeMapFactory::create); 233 } 234 235 @UnsupportedAppUsage commonInit()236 protected static final void commonInit() { 237 if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!"); 238 239 /* 240 * set handlers; these apply to all threads in the VM. Apps can replace 241 * the default handler, but not the pre handler. 242 */ 243 LoggingHandler loggingHandler = new LoggingHandler(); 244 RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler); 245 Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler)); 246 247 /* 248 * Install a time zone supplier that uses the Android persistent time zone system property. 249 */ 250 RuntimeHooks.setTimeZoneIdSupplier(() -> SystemProperties.get("persist.sys.timezone")); 251 252 /* 253 * Sets handler for java.util.logging to use Android log facilities. 254 * The odd "new instance-and-then-throw-away" is a mirror of how 255 * the "java.util.logging.config.class" system property works. We 256 * can't use the system property here since the logger has almost 257 * certainly already been initialized. 258 */ 259 LogManager.getLogManager().reset(); 260 new AndroidConfig(); 261 262 /* 263 * Sets the default HTTP User-Agent used by HttpURLConnection. 264 */ 265 String userAgent = getDefaultUserAgent(); 266 System.setProperty("http.agent", userAgent); 267 268 /* 269 * Wire socket tagging to traffic stats. 270 */ 271 TrafficStats.attachSocketTagger(); 272 273 initialized = true; 274 } 275 276 /** 277 * Returns an HTTP user agent of the form 278 * "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MAIN)". 279 */ getDefaultUserAgent()280 private static String getDefaultUserAgent() { 281 StringBuilder result = new StringBuilder(64); 282 result.append("Dalvik/"); 283 result.append(System.getProperty("java.vm.version")); // such as 1.1.0 284 result.append(" (Linux; U; Android "); 285 286 String version = Build.VERSION.RELEASE_OR_CODENAME; // "1.0" or "3.4b5" 287 result.append(version.length() > 0 ? version : "1.0"); 288 289 // add the model for the release build 290 if ("REL".equals(Build.VERSION.CODENAME)) { 291 String model = Build.MODEL; 292 if (model.length() > 0) { 293 result.append("; "); 294 result.append(model); 295 } 296 } 297 String id = Build.ID; // "MAIN" or "M4-rc20" 298 if (id.length() > 0) { 299 result.append(" Build/"); 300 result.append(id); 301 } 302 result.append(")"); 303 return result.toString(); 304 } 305 306 /** 307 * Invokes a static "main(argv[]) method on class "className". 308 * Converts various failing exceptions into RuntimeExceptions, with 309 * the assumption that they will then cause the VM instance to exit. 310 * 311 * @param className Fully-qualified class name 312 * @param argv Argument vector for main() 313 * @param classLoader the classLoader to load {@className} with 314 */ findStaticMain(String className, String[] argv, ClassLoader classLoader)315 protected static Runnable findStaticMain(String className, String[] argv, 316 ClassLoader classLoader) { 317 Class<?> cl; 318 319 try { 320 cl = Class.forName(className, true, classLoader); 321 } catch (ClassNotFoundException ex) { 322 throw new RuntimeException( 323 "Missing class when invoking static main " + className, 324 ex); 325 } 326 327 Method m; 328 try { 329 m = cl.getMethod("main", new Class[] { String[].class }); 330 } catch (NoSuchMethodException ex) { 331 throw new RuntimeException( 332 "Missing static main on " + className, ex); 333 } catch (SecurityException ex) { 334 throw new RuntimeException( 335 "Problem getting static main on " + className, ex); 336 } 337 338 int modifiers = m.getModifiers(); 339 if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) { 340 throw new RuntimeException( 341 "Main method is not public and static on " + className); 342 } 343 344 /* 345 * This throw gets caught in ZygoteInit.main(), which responds 346 * by invoking the exception's run() method. This arrangement 347 * clears up all the stack frames that were required in setting 348 * up the process. 349 */ 350 return new MethodAndArgsCaller(m, argv); 351 } 352 353 @UnsupportedAppUsage main(String[] argv)354 public static final void main(String[] argv) { 355 preForkInit(); 356 if (argv.length == 2 && argv[1].equals("application")) { 357 if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application"); 358 redirectLogStreams(); 359 } else { 360 if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool"); 361 } 362 363 commonInit(); 364 365 /* 366 * Now that we're running in interpreted code, call back into native code 367 * to run the system. 368 */ 369 nativeFinishInit(); 370 371 if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!"); 372 } 373 applicationInit(int targetSdkVersion, long[] disabledCompatChanges, String[] argv, ClassLoader classLoader)374 protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges, 375 String[] argv, ClassLoader classLoader) { 376 // If the application calls System.exit(), terminate the process 377 // immediately without running any shutdown hooks. It is not possible to 378 // shutdown an Android application gracefully. Among other things, the 379 // Android runtime shutdown hooks close the Binder driver, which can cause 380 // leftover running threads to crash before the process actually exits. 381 nativeSetExitWithoutCleanup(true); 382 383 VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion); 384 VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges); 385 386 final Arguments args = new Arguments(argv); 387 388 // The end of of the RuntimeInit event (see #zygoteInit). 389 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 390 391 // Remaining arguments are passed to the start class's static main 392 return findStaticMain(args.startClass, args.startArgs, classLoader); 393 } 394 395 /** 396 * Redirect System.out and System.err to the Android log. 397 */ 398 @android.ravenwood.annotation.RavenwoodReplace redirectLogStreams()399 public static void redirectLogStreams() { 400 System.out.close(); 401 System.setOut(new AndroidPrintStream(Log.INFO, "System.out")); 402 System.err.close(); 403 System.setErr(new AndroidPrintStream(Log.WARN, "System.err")); 404 } 405 redirectLogStreams$ravenwood()406 public static void redirectLogStreams$ravenwood() { 407 if (sOut$ravenwood == null) { 408 sOut$ravenwood = System.out; 409 System.setOut(new AndroidPrintStream(Log.INFO, "System.out")); 410 } 411 if (sErr$ravenwood == null) { 412 sErr$ravenwood = System.err; 413 System.setErr(new AndroidPrintStream(Log.WARN, "System.err")); 414 } 415 } 416 417 /** 418 * Report a serious error in the current process. May or may not cause 419 * the process to terminate (depends on system settings). 420 * 421 * @param tag to record with the error 422 * @param t exception describing the error site and conditions 423 */ 424 @android.ravenwood.annotation.RavenwoodReplace wtf(String tag, Throwable t, boolean system)425 public static void wtf(String tag, Throwable t, boolean system) { 426 try { 427 boolean exit = false; 428 final IActivityManager am = ActivityManager.getService(); 429 if (am != null) { 430 exit = am.handleApplicationWtf( 431 mApplicationObject, tag, system, 432 new ApplicationErrorReport.ParcelableCrashInfo(t), 433 Process.myPid()); 434 } else { 435 // Unlikely but possible in early system boot 436 final ApplicationWtfHandler handler = sDefaultApplicationWtfHandler; 437 if (handler != null) { 438 exit = handler.handleApplicationWtf( 439 mApplicationObject, tag, system, 440 new ApplicationErrorReport.ParcelableCrashInfo(t), 441 Process.myPid()); 442 } else { 443 // Simply log the error 444 Slog.e(TAG, "Original WTF:", t); 445 } 446 } 447 if (exit) { 448 // The Activity Manager has already written us off -- now exit. 449 Process.killProcess(Process.myPid()); 450 System.exit(10); 451 } 452 } catch (Throwable t2) { 453 if (t2 instanceof DeadObjectException) { 454 // System process is dead; ignore 455 } else { 456 Slog.e(TAG, "Error reporting WTF", t2); 457 Slog.e(TAG, "Original WTF:", t); 458 } 459 } 460 } 461 wtf$ravenwood(String tag, Throwable t, boolean system)462 public static void wtf$ravenwood(String tag, Throwable t, boolean system) { 463 // We've already emitted to logs, so there's nothing more to do here, 464 // as we don't have a DropBox pipeline configured 465 } 466 467 /** 468 * Set the default {@link ApplicationWtfHandler}, in case the ActivityManager is not ready yet. 469 */ setDefaultApplicationWtfHandler(final ApplicationWtfHandler handler)470 public static void setDefaultApplicationWtfHandler(final ApplicationWtfHandler handler) { 471 sDefaultApplicationWtfHandler = handler; 472 } 473 474 /** 475 * The handler to deal with the serious application errors. 476 */ 477 public interface ApplicationWtfHandler { 478 /** 479 * @param app object of the crashing app, null for the system server 480 * @param tag reported by the caller 481 * @param system whether this wtf is coming from the system 482 * @param crashInfo describing the context of the error 483 * @param immediateCallerPid the caller Pid 484 * @return true if the process should exit immediately (WTF is fatal) 485 */ handleApplicationWtf(IBinder app, String tag, boolean system, ApplicationErrorReport.ParcelableCrashInfo crashInfo, int immediateCallerPid)486 boolean handleApplicationWtf(IBinder app, String tag, boolean system, 487 ApplicationErrorReport.ParcelableCrashInfo crashInfo, int immediateCallerPid); 488 } 489 490 /** 491 * Set the object identifying this application/process, for reporting VM 492 * errors. 493 */ setApplicationObject(IBinder app)494 public static final void setApplicationObject(IBinder app) { 495 mApplicationObject = app; 496 } 497 498 @UnsupportedAppUsage getApplicationObject()499 public static final IBinder getApplicationObject() { 500 return mApplicationObject; 501 } 502 503 /** 504 * Enable DDMS. 505 */ enableDdms()506 private static void enableDdms() { 507 // Register handlers for DDM messages. 508 android.ddm.DdmRegister.registerHandlers(); 509 } 510 511 /** 512 * Handles argument parsing for args related to the runtime. 513 * 514 * Current recognized args: 515 * <ul> 516 * <li> <code> [--] <start class name> <args> 517 * </ul> 518 */ 519 static class Arguments { 520 /** first non-option argument */ 521 String startClass; 522 523 /** all following arguments */ 524 String[] startArgs; 525 526 /** 527 * Constructs instance and parses args 528 * @param args runtime command-line args 529 * @throws IllegalArgumentException 530 */ Arguments(String args[])531 Arguments(String args[]) throws IllegalArgumentException { 532 parseArgs(args); 533 } 534 535 /** 536 * Parses the commandline arguments intended for the Runtime. 537 */ parseArgs(String args[])538 private void parseArgs(String args[]) 539 throws IllegalArgumentException { 540 int curArg = 0; 541 for (; curArg < args.length; curArg++) { 542 String arg = args[curArg]; 543 544 if (arg.equals("--")) { 545 curArg++; 546 break; 547 } else if (!arg.startsWith("--")) { 548 break; 549 } 550 } 551 552 if (curArg == args.length) { 553 throw new IllegalArgumentException("Missing classname argument to RuntimeInit!"); 554 } 555 556 startClass = args[curArg++]; 557 startArgs = new String[args.length - curArg]; 558 System.arraycopy(args, curArg, startArgs, 0, startArgs.length); 559 } 560 } 561 562 /** 563 * Helper class which holds a method and arguments and can call them. This is used as part of 564 * a trampoline to get rid of the initial process setup stack frames. 565 */ 566 static class MethodAndArgsCaller implements Runnable { 567 /** method to call */ 568 private final Method mMethod; 569 570 /** argument array */ 571 private final String[] mArgs; 572 MethodAndArgsCaller(Method method, String[] args)573 public MethodAndArgsCaller(Method method, String[] args) { 574 mMethod = method; 575 mArgs = args; 576 } 577 run()578 public void run() { 579 try { 580 mMethod.invoke(null, new Object[] { mArgs }); 581 } catch (IllegalAccessException ex) { 582 throw new RuntimeException(ex); 583 } catch (InvocationTargetException ex) { 584 Throwable cause = ex.getCause(); 585 if (cause instanceof RuntimeException) { 586 throw (RuntimeException) cause; 587 } else if (cause instanceof Error) { 588 throw (Error) cause; 589 } 590 throw new RuntimeException(ex); 591 } 592 } 593 } 594 } 595