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