/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.os; import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.os.Debug; import android.os.IBinder; import android.os.ICheckinService; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.server.data.CrashData; import android.util.Config; import android.util.Log; import com.android.internal.logging.AndroidConfig; import dalvik.system.VMRuntime; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.LogManager; import java.util.TimeZone; import org.apache.harmony.luni.internal.util.TimezoneGetter; /** * Main entry point for runtime initialization. Not for * public consumption. * @hide */ public class RuntimeInit { private final static String TAG = "AndroidRuntime"; /** true if commonInit() has been called */ private static boolean initialized; /** * Use this to log a message when a thread exits due to an uncaught * exception. The framework catches these for the main threads, so * this should only matter for threads created by applications. */ private static class UncaughtHandler implements Thread.UncaughtExceptionHandler { public void uncaughtException(Thread t, Throwable e) { try { Log.e(TAG, "Uncaught handler: thread " + t.getName() + " exiting due to uncaught exception"); } catch (Throwable error) { // Ignore the throwable, since we're in the process of crashing anyway. // If we don't, the crash won't happen properly and the process will // be left around in a bad state. } crash(TAG, e); } } private static final void commonInit() { if (Config.LOGV) Log.d(TAG, "Entered RuntimeInit!"); /* set default handler; this applies to all threads in the VM */ Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler()); int hasQwerty = getQwertyKeyboard(); if (Config.LOGV) Log.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty); if (hasQwerty == 1) { System.setProperty("qwerty", "1"); } /* * Install a TimezoneGetter subclass for ZoneInfo.db */ TimezoneGetter.setInstance(new TimezoneGetter() { @Override public String getId() { return SystemProperties.get("persist.sys.timezone"); } }); TimeZone.setDefault(null); /* * Sets handler for java.util.logging to use Android log facilities. * The odd "new instance-and-then-throw-away" is a mirror of how * the "java.util.logging.config.class" system property works. We * can't use the system property here since the logger has almost * certainly already been initialized. */ LogManager.getLogManager().reset(); new AndroidConfig(); /* * If we're running in an emulator launched with "-trace", put the * VM into emulator trace profiling mode so that the user can hit * F9/F10 at any time to capture traces. This has performance * consequences, so it's not something you want to do always. */ String trace = SystemProperties.get("ro.kernel.android.tracing"); if (trace.equals("1")) { Log.i(TAG, "NOTE: emulator trace profiling enabled"); Debug.enableEmulatorTraceOutput(); } initialized = true; } /** * Invokes a static "main(argv[]) method on class "className". * Converts various failing exceptions into RuntimeExceptions, with * the assumption that they will then cause the VM instance to exit. * * @param className Fully-qualified class name * @param argv Argument vector for main() */ private static void invokeStaticMain(String className, String[] argv) throws ZygoteInit.MethodAndArgsCaller { // We want to be fairly aggressive about heap utilization, to avoid // holding on to a lot of memory that isn't needed. VMRuntime.getRuntime().setTargetHeapUtilization(0.75f); Class<?> cl; try { cl = Class.forName(className); } catch (ClassNotFoundException ex) { throw new RuntimeException( "Missing class when invoking static main " + className, ex); } Method m; try { m = cl.getMethod("main", new Class[] { String[].class }); } catch (NoSuchMethodException ex) { throw new RuntimeException( "Missing static main on " + className, ex); } catch (SecurityException ex) { throw new RuntimeException( "Problem getting static main on " + className, ex); } int modifiers = m.getModifiers(); if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) { throw new RuntimeException( "Main method is not public and static on " + className); } /* * This throw gets caught in ZygoteInit.main(), which responds * by invoking the exception's run() method. This arrangement * clears up all the stack frames that were required in setting * up the process. */ throw new ZygoteInit.MethodAndArgsCaller(m, argv); } public static final void main(String[] argv) { commonInit(); /* * Now that we're running in interpreted code, call back into native code * to run the system. */ finishInit(); if (Config.LOGV) Log.d(TAG, "Leaving RuntimeInit!"); } public static final native void finishInit(); /** * The main function called when started through the zygote process. This * could be unified with main(), if the native code in finishInit() * were rationalized with Zygote startup.<p> * * Current recognized args: * <ul> * <li> --nice-name=<i>nice name to appear in ps</i> * <li> <code> [--] <start class name> <args> * </ul> * * @param argv arg strings */ public static final void zygoteInit(String[] argv) throws ZygoteInit.MethodAndArgsCaller { // TODO: Doing this here works, but it seems kind of arbitrary. Find // a better place. The goal is to set it up for applications, but not // tools like am. System.setOut(new AndroidPrintStream(Log.INFO, "System.out")); System.setErr(new AndroidPrintStream(Log.WARN, "System.err")); commonInit(); zygoteInitNative(); int curArg = 0; for ( /* curArg */ ; curArg < argv.length; curArg++) { String arg = argv[curArg]; if (arg.equals("--")) { curArg++; break; } else if (!arg.startsWith("--")) { break; } else if (arg.startsWith("--nice-name=")) { String niceName = arg.substring(arg.indexOf('=') + 1); Process.setArgV0(niceName); } } if (curArg == argv.length) { Log.e(TAG, "Missing classname argument to RuntimeInit!"); // let the process exit return; } // Remaining arguments are passed to the start class's static main String startClass = argv[curArg++]; String[] startArgs = new String[argv.length - curArg]; System.arraycopy(argv, curArg, startArgs, 0, startArgs.length); invokeStaticMain(startClass, startArgs); } public static final native void zygoteInitNative(); /** * Returns 1 if the computer is on. If the computer isn't on, the value returned by this method is undefined. */ public static final native int isComputerOn(); /** * Turns the computer on if the computer is off. If the computer is on, the behavior of this method is undefined. */ public static final native void turnComputerOn(); /** * * @return 1 if the device has a qwerty keyboard */ public static native int getQwertyKeyboard(); /** * Report a fatal error in the current process. If this is a user-process, * a dialog may be displayed informing the user of the error. This * function does not return; it forces the current process to exit. * * @param tag to use when logging the error * @param t exception that was generated by the error */ public static void crash(String tag, Throwable t) { if (mApplicationObject != null) { byte[] crashData = null; try { // Log exception. Log.e(TAG, Log.getStackTraceString(t)); crashData = marshallException(tag, t); if (crashData == null) { throw new NullPointerException("Can't marshall crash data"); } } catch (Throwable t2) { try { // Log exception as a string so we don't get in an infinite loop. Log.e(TAG, "Error reporting crash: " + Log.getStackTraceString(t2)); } catch (Throwable t3) { // Do nothing, must be OOM so we can't format the message } } try { // Display user-visible error message. String msg = t.getMessage(); if (msg == null) { msg = t.toString(); } IActivityManager am = ActivityManagerNative.getDefault(); try { int res = am.handleApplicationError(mApplicationObject, 0, tag, msg, t.toString(), crashData); // Is waiting for the debugger the right thing? // For now I have turned off the Debug button, because // I'm not sure what we should do if it is actually // selected. //Log.i(TAG, "Got app error result: " + res); if (res == 1) { Debug.waitForDebugger(); return; } } catch (RemoteException e) { } } catch (Throwable t2) { try { // Log exception as a string so we don't get in an infinite loop. Log.e(TAG, "Error reporting crash: " + Log.getStackTraceString(t2)); } catch (Throwable t3) { // Do nothing, must be OOM so we can't format the message } } finally { // Try everything to make sure this process goes away. Process.killProcess(Process.myPid()); System.exit(10); } } else { try { Log.e(TAG, "*** EXCEPTION IN SYSTEM PROCESS. System will crash."); Log.e(tag, Log.getStackTraceString(t)); reportException(tag, t, true); // synchronous } catch (Throwable t2) { // Do nothing, must be OOM so we can't format the message } finally { // Try everything to make sure this process goes away. Process.killProcess(Process.myPid()); System.exit(10); } } } /** Counter used to prevent reentrancy in {@link #reportException}. */ private static final AtomicInteger sInReportException = new AtomicInteger(); /** * Report an error in the current process. The exception information will * be handed off to the checkin service and eventually uploaded for analysis. * This is expensive! Only use this when the exception indicates a programming * error ("should not happen"). * * @param tag to use when logging the error * @param t exception that was generated by the error * @param sync true to wait for the report, false to "fire and forget" */ public static void reportException(String tag, Throwable t, boolean sync) { if (!initialized) { // Exceptions during, eg, zygote cannot use this mechanism return; } // It's important to prevent an infinite crash-reporting loop: // while this function is running, don't let it be called again. int reenter = sInReportException.getAndIncrement(); if (reenter != 0) { sInReportException.decrementAndGet(); Log.e(TAG, "Crash logging skipped, already logging another crash"); return; } // TODO: Enable callers to specify a level (i.e. warn or error). try { // Submit crash data to statistics service. byte[] crashData = marshallException(tag, t); ICheckinService checkin = ICheckinService.Stub.asInterface( ServiceManager.getService("checkin")); if (checkin == null) { Log.e(TAG, "Crash logging skipped, no checkin service"); } else if (sync) { checkin.reportCrashSync(crashData); } else { checkin.reportCrashAsync(crashData); } } catch (Throwable t2) { // Log exception as a string so we don't get in an infinite loop. Log.e(TAG, "Crash logging failed: " + t2); } finally { sInReportException.decrementAndGet(); } } private static byte[] marshallException(String tag, Throwable t) { // Convert crash data to bytes. ByteArrayOutputStream bout = new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream(bout); try { new CrashData(tag, t).write(dout); dout.close(); } catch (IOException e) { return null; } return bout.toByteArray(); } /** * Replay an encoded CrashData record back into a useable CrashData record. This can be * helpful for providing debugging output after a process error. * * @param crashDataBytes The byte array containing the encoded crash record * @return new CrashData record, or null if could not create one. */ public static CrashData unmarshallException(byte[] crashDataBytes) { try { ByteArrayInputStream bin = new ByteArrayInputStream(crashDataBytes); DataInputStream din = new DataInputStream(bin); return new CrashData(din); } catch (IOException e) { return null; } } /** * Set the object identifying this application/process, for reporting VM * errors. */ public static final void setApplicationObject(IBinder app) { mApplicationObject = app; } /** * Enable debugging features. */ static { // Register handlers for DDM messages. android.ddm.DdmRegister.registerHandlers(); } private static IBinder mApplicationObject; }