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