diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
commit | 9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch) | |
tree | d88beb88001f2482911e3d28e43833b50e4b4e97 /core/java/com/android/internal/os/RuntimeInit.java | |
parent | d83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff) | |
download | frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.zip frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.gz frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'core/java/com/android/internal/os/RuntimeInit.java')
-rw-r--r-- | core/java/com/android/internal/os/RuntimeInit.java | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java new file mode 100644 index 0000000..8486272 --- /dev/null +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -0,0 +1,440 @@ +/* + * 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; + +} |