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