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