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.IActivityManager; 21 import android.os.Debug; 22 import android.os.IBinder; 23 import android.os.ICheckinService; 24 import android.os.Process; 25 import android.os.RemoteException; 26 import android.os.ServiceManager; 27 import android.os.SystemProperties; 28 import android.os.Build; 29 import android.server.data.CrashData; 30 import android.util.Config; 31 import android.util.Log; 32 33 import com.android.internal.logging.AndroidConfig; 34 35 import dalvik.system.VMRuntime; 36 37 import java.io.ByteArrayInputStream; 38 import java.io.ByteArrayOutputStream; 39 import java.io.DataInputStream; 40 import java.io.DataOutputStream; 41 import java.io.IOException; 42 import java.lang.reflect.Method; 43 import java.lang.reflect.Modifier; 44 import java.util.concurrent.atomic.AtomicInteger; 45 import java.util.logging.LogManager; 46 import java.util.TimeZone; 47 48 import org.apache.harmony.luni.internal.util.TimezoneGetter; 49 50 /** 51 * Main entry point for runtime initialization. Not for 52 * public consumption. 53 * @hide 54 */ 55 public class RuntimeInit { 56 private final static String TAG = "AndroidRuntime"; 57 58 /** true if commonInit() has been called */ 59 private static boolean initialized; 60 61 /** 62 * Use this to log a message when a thread exits due to an uncaught 63 * exception. The framework catches these for the main threads, so 64 * this should only matter for threads created by applications. 65 */ 66 private static class UncaughtHandler implements Thread.UncaughtExceptionHandler { uncaughtException(Thread t, Throwable e)67 public void uncaughtException(Thread t, Throwable e) { 68 try { 69 Log.e(TAG, "Uncaught handler: thread " + t.getName() 70 + " exiting due to uncaught exception"); 71 } catch (Throwable error) { 72 // Ignore the throwable, since we're in the process of crashing anyway. 73 // If we don't, the crash won't happen properly and the process will 74 // be left around in a bad state. 75 } 76 crash(TAG, e); 77 } 78 } 79 commonInit()80 private static final void commonInit() { 81 if (Config.LOGV) Log.d(TAG, "Entered RuntimeInit!"); 82 83 /* set default handler; this applies to all threads in the VM */ 84 Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler()); 85 86 int hasQwerty = getQwertyKeyboard(); 87 88 if (Config.LOGV) Log.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty); 89 if (hasQwerty == 1) { 90 System.setProperty("qwerty", "1"); 91 } 92 93 /* 94 * Install a TimezoneGetter subclass for ZoneInfo.db 95 */ 96 TimezoneGetter.setInstance(new TimezoneGetter() { 97 @Override 98 public String getId() { 99 return SystemProperties.get("persist.sys.timezone"); 100 } 101 }); 102 TimeZone.setDefault(null); 103 104 /* 105 * Sets handler for java.util.logging to use Android log facilities. 106 * The odd "new instance-and-then-throw-away" is a mirror of how 107 * the "java.util.logging.config.class" system property works. We 108 * can't use the system property here since the logger has almost 109 * certainly already been initialized. 110 */ 111 LogManager.getLogManager().reset(); 112 new AndroidConfig(); 113 114 /* 115 * Sets the default HTTP User-Agent used by HttpURLConnection. 116 */ 117 String userAgent = getDefaultUserAgent(); 118 System.setProperty("http.agent", userAgent); 119 120 /* 121 * If we're running in an emulator launched with "-trace", put the 122 * VM into emulator trace profiling mode so that the user can hit 123 * F9/F10 at any time to capture traces. This has performance 124 * consequences, so it's not something you want to do always. 125 */ 126 String trace = SystemProperties.get("ro.kernel.android.tracing"); 127 if (trace.equals("1")) { 128 Log.i(TAG, "NOTE: emulator trace profiling enabled"); 129 Debug.enableEmulatorTraceOutput(); 130 } 131 132 initialized = true; 133 } 134 135 /** 136 * Returns an HTTP user agent of the form 137 * "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MASTER)". 138 */ getDefaultUserAgent()139 private static String getDefaultUserAgent() { 140 StringBuilder result = new StringBuilder(64); 141 result.append("Dalvik/"); 142 result.append(System.getProperty("java.vm.version")); // such as 1.1.0 143 result.append(" (Linux; U; Android "); 144 145 String version = Build.VERSION.RELEASE; // "1.0" or "3.4b5" 146 result.append(version.length() > 0 ? version : "1.0"); 147 148 // add the model for the release build 149 if ("REL".equals(Build.VERSION.CODENAME)) { 150 String model = Build.MODEL; 151 if (model.length() > 0) { 152 result.append("; "); 153 result.append(model); 154 } 155 } 156 String id = Build.ID; // "MASTER" or "M4-rc20" 157 if (id.length() > 0) { 158 result.append(" Build/"); 159 result.append(id); 160 } 161 result.append(")"); 162 return result.toString(); 163 } 164 165 /** 166 * Invokes a static "main(argv[]) method on class "className". 167 * Converts various failing exceptions into RuntimeExceptions, with 168 * the assumption that they will then cause the VM instance to exit. 169 * 170 * @param className Fully-qualified class name 171 * @param argv Argument vector for main() 172 */ invokeStaticMain(String className, String[] argv)173 private static void invokeStaticMain(String className, String[] argv) 174 throws ZygoteInit.MethodAndArgsCaller { 175 176 // We want to be fairly aggressive about heap utilization, to avoid 177 // holding on to a lot of memory that isn't needed. 178 VMRuntime.getRuntime().setTargetHeapUtilization(0.75f); 179 180 Class<?> cl; 181 182 try { 183 cl = Class.forName(className); 184 } catch (ClassNotFoundException ex) { 185 throw new RuntimeException( 186 "Missing class when invoking static main " + className, 187 ex); 188 } 189 190 Method m; 191 try { 192 m = cl.getMethod("main", new Class[] { String[].class }); 193 } catch (NoSuchMethodException ex) { 194 throw new RuntimeException( 195 "Missing static main on " + className, ex); 196 } catch (SecurityException ex) { 197 throw new RuntimeException( 198 "Problem getting static main on " + className, ex); 199 } 200 201 int modifiers = m.getModifiers(); 202 if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) { 203 throw new RuntimeException( 204 "Main method is not public and static on " + className); 205 } 206 207 /* 208 * This throw gets caught in ZygoteInit.main(), which responds 209 * by invoking the exception's run() method. This arrangement 210 * clears up all the stack frames that were required in setting 211 * up the process. 212 */ 213 throw new ZygoteInit.MethodAndArgsCaller(m, argv); 214 } 215 main(String[] argv)216 public static final void main(String[] argv) { 217 commonInit(); 218 219 /* 220 * Now that we're running in interpreted code, call back into native code 221 * to run the system. 222 */ 223 finishInit(); 224 225 if (Config.LOGV) Log.d(TAG, "Leaving RuntimeInit!"); 226 } 227 finishInit()228 public static final native void finishInit(); 229 230 /** 231 * The main function called when started through the zygote process. This 232 * could be unified with main(), if the native code in finishInit() 233 * were rationalized with Zygote startup.<p> 234 * 235 * Current recognized args: 236 * <ul> 237 * <li> --nice-name=<i>nice name to appear in ps</i> 238 * <li> <code> [--] <start class name> <args> 239 * </ul> 240 * 241 * @param argv arg strings 242 */ zygoteInit(String[] argv)243 public static final void zygoteInit(String[] argv) 244 throws ZygoteInit.MethodAndArgsCaller { 245 // TODO: Doing this here works, but it seems kind of arbitrary. Find 246 // a better place. The goal is to set it up for applications, but not 247 // tools like am. 248 System.setOut(new AndroidPrintStream(Log.INFO, "System.out")); 249 System.setErr(new AndroidPrintStream(Log.WARN, "System.err")); 250 251 commonInit(); 252 zygoteInitNative(); 253 254 int curArg = 0; 255 for ( /* curArg */ ; curArg < argv.length; curArg++) { 256 String arg = argv[curArg]; 257 258 if (arg.equals("--")) { 259 curArg++; 260 break; 261 } else if (!arg.startsWith("--")) { 262 break; 263 } else if (arg.startsWith("--nice-name=")) { 264 String niceName = arg.substring(arg.indexOf('=') + 1); 265 Process.setArgV0(niceName); 266 } 267 } 268 269 if (curArg == argv.length) { 270 Log.e(TAG, "Missing classname argument to RuntimeInit!"); 271 // let the process exit 272 return; 273 } 274 275 // Remaining arguments are passed to the start class's static main 276 277 String startClass = argv[curArg++]; 278 String[] startArgs = new String[argv.length - curArg]; 279 280 System.arraycopy(argv, curArg, startArgs, 0, startArgs.length); 281 invokeStaticMain(startClass, startArgs); 282 } 283 zygoteInitNative()284 public static final native void zygoteInitNative(); 285 286 /** 287 * Returns 1 if the computer is on. If the computer isn't on, the value returned by this method is undefined. 288 */ isComputerOn()289 public static final native int isComputerOn(); 290 291 /** 292 * Turns the computer on if the computer is off. If the computer is on, the behavior of this method is undefined. 293 */ turnComputerOn()294 public static final native void turnComputerOn(); 295 296 /** 297 * 298 * @return 1 if the device has a qwerty keyboard 299 */ getQwertyKeyboard()300 public static native int getQwertyKeyboard(); 301 302 /** 303 * Report a fatal error in the current process. If this is a user-process, 304 * a dialog may be displayed informing the user of the error. This 305 * function does not return; it forces the current process to exit. 306 * 307 * @param tag to use when logging the error 308 * @param t exception that was generated by the error 309 */ crash(String tag, Throwable t)310 public static void crash(String tag, Throwable t) { 311 if (mApplicationObject != null) { 312 byte[] crashData = null; 313 try { 314 // Log exception. 315 Log.e(TAG, Log.getStackTraceString(t)); 316 crashData = marshallException(tag, t); 317 if (crashData == null) { 318 throw new NullPointerException("Can't marshall crash data"); 319 } 320 } catch (Throwable t2) { 321 try { 322 // Log exception as a string so we don't get in an infinite loop. 323 Log.e(TAG, "Error reporting crash: " 324 + Log.getStackTraceString(t2)); 325 } catch (Throwable t3) { 326 // Do nothing, must be OOM so we can't format the message 327 } 328 } 329 330 try { 331 // Display user-visible error message. 332 String msg = t.getMessage(); 333 if (msg == null) { 334 msg = t.toString(); 335 } 336 337 IActivityManager am = ActivityManagerNative.getDefault(); 338 try { 339 int res = am.handleApplicationError(mApplicationObject, 340 0, tag, msg, t.toString(), crashData); 341 // Is waiting for the debugger the right thing? 342 // For now I have turned off the Debug button, because 343 // I'm not sure what we should do if it is actually 344 // selected. 345 //Log.i(TAG, "Got app error result: " + res); 346 if (res == 1) { 347 Debug.waitForDebugger(); 348 return; 349 } 350 } catch (RemoteException e) { 351 } 352 } catch (Throwable t2) { 353 try { 354 // Log exception as a string so we don't get in an infinite loop. 355 Log.e(TAG, "Error reporting crash: " 356 + Log.getStackTraceString(t2)); 357 } catch (Throwable t3) { 358 // Do nothing, must be OOM so we can't format the message 359 } 360 } finally { 361 // Try everything to make sure this process goes away. 362 Process.killProcess(Process.myPid()); 363 System.exit(10); 364 } 365 } else { 366 try { 367 Log.e(TAG, "*** EXCEPTION IN SYSTEM PROCESS. System will crash."); 368 Log.e(tag, Log.getStackTraceString(t)); 369 reportException(tag, t, true); // synchronous 370 } catch (Throwable t2) { 371 // Do nothing, must be OOM so we can't format the message 372 } finally { 373 // Try everything to make sure this process goes away. 374 Process.killProcess(Process.myPid()); 375 System.exit(10); 376 } 377 } 378 } 379 380 /** Counter used to prevent reentrancy in {@link #reportException}. */ 381 private static final AtomicInteger sInReportException = new AtomicInteger(); 382 383 /** 384 * Report an error in the current process. The exception information will 385 * be handed off to the checkin service and eventually uploaded for analysis. 386 * This is expensive! Only use this when the exception indicates a programming 387 * error ("should not happen"). 388 * 389 * @param tag to use when logging the error 390 * @param t exception that was generated by the error 391 * @param sync true to wait for the report, false to "fire and forget" 392 */ reportException(String tag, Throwable t, boolean sync)393 public static void reportException(String tag, Throwable t, boolean sync) { 394 if (!initialized) { 395 // Exceptions during, eg, zygote cannot use this mechanism 396 return; 397 } 398 399 // It's important to prevent an infinite crash-reporting loop: 400 // while this function is running, don't let it be called again. 401 int reenter = sInReportException.getAndIncrement(); 402 if (reenter != 0) { 403 sInReportException.decrementAndGet(); 404 Log.e(TAG, "Crash logging skipped, already logging another crash"); 405 return; 406 } 407 408 // TODO: Enable callers to specify a level (i.e. warn or error). 409 try { 410 // Submit crash data to statistics service. 411 byte[] crashData = marshallException(tag, t); 412 ICheckinService checkin = ICheckinService.Stub.asInterface( 413 ServiceManager.getService("checkin")); 414 if (checkin == null) { 415 Log.e(TAG, "Crash logging skipped, no checkin service"); 416 } else if (sync) { 417 checkin.reportCrashSync(crashData); 418 } else { 419 checkin.reportCrashAsync(crashData); 420 } 421 } catch (Throwable t2) { 422 // Log exception as a string so we don't get in an infinite loop. 423 Log.e(TAG, "Crash logging failed: " + t2); 424 } finally { 425 sInReportException.decrementAndGet(); 426 } 427 } 428 marshallException(String tag, Throwable t)429 private static byte[] marshallException(String tag, Throwable t) { 430 // Convert crash data to bytes. 431 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 432 DataOutputStream dout = new DataOutputStream(bout); 433 try { 434 new CrashData(tag, t).write(dout); 435 dout.close(); 436 } catch (IOException e) { 437 return null; 438 } 439 return bout.toByteArray(); 440 } 441 442 /** 443 * Replay an encoded CrashData record back into a useable CrashData record. This can be 444 * helpful for providing debugging output after a process error. 445 * 446 * @param crashDataBytes The byte array containing the encoded crash record 447 * @return new CrashData record, or null if could not create one. 448 */ unmarshallException(byte[] crashDataBytes)449 public static CrashData unmarshallException(byte[] crashDataBytes) { 450 try { 451 ByteArrayInputStream bin = new ByteArrayInputStream(crashDataBytes); 452 DataInputStream din = new DataInputStream(bin); 453 return new CrashData(din); 454 } catch (IOException e) { 455 return null; 456 } 457 } 458 459 /** 460 * Set the object identifying this application/process, for reporting VM 461 * errors. 462 */ setApplicationObject(IBinder app)463 public static final void setApplicationObject(IBinder app) { 464 mApplicationObject = app; 465 } 466 467 /** 468 * Enable debugging features. 469 */ 470 static { 471 // Register handlers for DDM messages. android.ddm.DdmRegister.registerHandlers()472 android.ddm.DdmRegister.registerHandlers(); 473 } 474 475 private static IBinder mApplicationObject; 476 477 } 478