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