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