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