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> [--] <start class name> <args> 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