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