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