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