595 lines
22 KiB
Java
595 lines
22 KiB
Java
/*
|
|
* 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.ActivityManager;
|
|
import android.app.ActivityThread;
|
|
import android.app.ApplicationErrorReport;
|
|
import android.app.IActivityManager;
|
|
import android.compat.annotation.UnsupportedAppUsage;
|
|
import android.content.type.DefaultMimeMapFactory;
|
|
import android.net.TrafficStats;
|
|
import android.os.Build;
|
|
import android.os.DeadObjectException;
|
|
import android.os.IBinder;
|
|
import android.os.Process;
|
|
import android.os.SystemProperties;
|
|
import android.os.Trace;
|
|
import android.util.Log;
|
|
import android.util.Slog;
|
|
|
|
import com.android.internal.logging.AndroidConfig;
|
|
|
|
import dalvik.system.RuntimeHooks;
|
|
import dalvik.system.VMRuntime;
|
|
|
|
import libcore.content.type.MimeMap;
|
|
|
|
import java.io.PrintStream;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.Modifier;
|
|
import java.util.Objects;
|
|
import java.util.logging.LogManager;
|
|
|
|
/**
|
|
* Main entry point for runtime initialization. Not for
|
|
* public consumption.
|
|
* @hide
|
|
*/
|
|
@android.ravenwood.annotation.RavenwoodKeepPartialClass
|
|
public class RuntimeInit {
|
|
final static String TAG = "AndroidRuntime";
|
|
final static boolean DEBUG = false;
|
|
|
|
/** true if commonInit() has been called */
|
|
@UnsupportedAppUsage
|
|
private static boolean initialized;
|
|
|
|
@UnsupportedAppUsage
|
|
private static IBinder mApplicationObject;
|
|
|
|
private static volatile boolean mCrashing = false;
|
|
private static final String SYSPROP_CRASH_COUNT = "sys.system_server.crash_java";
|
|
private static int mCrashCount;
|
|
|
|
private static volatile ApplicationWtfHandler sDefaultApplicationWtfHandler;
|
|
|
|
/**
|
|
* Stored values of System.out and System.err before they've been replaced by
|
|
* redirectLogStreams(). Kept open here for other Ravenwood internals to use.
|
|
*/
|
|
public static PrintStream sOut$ravenwood;
|
|
public static PrintStream sErr$ravenwood;
|
|
|
|
private static final native void nativeFinishInit();
|
|
|
|
private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
|
|
|
|
private static int Clog_e(String tag, String msg, Throwable tr) {
|
|
return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr);
|
|
}
|
|
|
|
public static void logUncaught(String threadName, String processName, int pid, Throwable e) {
|
|
StringBuilder message = new StringBuilder();
|
|
// The "FATAL EXCEPTION" string is still used on Android even though
|
|
// apps can set a custom UncaughtExceptionHandler that renders uncaught
|
|
// exceptions non-fatal.
|
|
message.append("FATAL EXCEPTION: ").append(threadName).append("\n");
|
|
if (processName != null) {
|
|
message.append("Process: ").append(processName).append(", ");
|
|
}
|
|
message.append("PID: ").append(pid);
|
|
Clog_e(TAG, message.toString(), e);
|
|
}
|
|
|
|
/**
|
|
* Logs a message when a thread encounters an uncaught exception. By
|
|
* default, {@link KillApplicationHandler} will terminate this process later,
|
|
* but apps can override that behavior.
|
|
*/
|
|
private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
|
|
public volatile boolean mTriggered = false;
|
|
|
|
@Override
|
|
public void uncaughtException(Thread t, Throwable e) {
|
|
mTriggered = true;
|
|
|
|
// Don't re-enter if KillApplicationHandler has already run
|
|
if (mCrashing) return;
|
|
|
|
// mApplicationObject is null for non-zygote java programs (e.g. "am")
|
|
// There are also apps running with the system UID. We don't want the
|
|
// first clause in either of these two cases, only for system_server.
|
|
if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
|
|
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
|
|
mCrashCount = SystemProperties.getInt(SYSPROP_CRASH_COUNT, 0) + 1;
|
|
SystemProperties.set(SYSPROP_CRASH_COUNT, String.valueOf(mCrashCount));
|
|
} else {
|
|
logUncaught(t.getName(), ActivityThread.currentProcessName(), Process.myPid(), e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle application death from an uncaught exception. The framework
|
|
* catches these for the main threads, so this should only matter for
|
|
* threads created by applications. Before this method runs, the given
|
|
* instance of {@link LoggingHandler} should already have logged details
|
|
* (and if not it is run first).
|
|
*/
|
|
private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
|
|
private final LoggingHandler mLoggingHandler;
|
|
|
|
/**
|
|
* Create a new KillApplicationHandler that follows the given LoggingHandler.
|
|
* If {@link #uncaughtException(Thread, Throwable) uncaughtException} is called
|
|
* on the created instance without {@code loggingHandler} having been triggered,
|
|
* {@link LoggingHandler#uncaughtException(Thread, Throwable)
|
|
* loggingHandler.uncaughtException} will be called first.
|
|
*
|
|
* @param loggingHandler the {@link LoggingHandler} expected to have run before
|
|
* this instance's {@link #uncaughtException(Thread, Throwable) uncaughtException}
|
|
* is being called.
|
|
*/
|
|
public KillApplicationHandler(LoggingHandler loggingHandler) {
|
|
this.mLoggingHandler = Objects.requireNonNull(loggingHandler);
|
|
}
|
|
|
|
@Override
|
|
public void uncaughtException(Thread t, Throwable e) {
|
|
try {
|
|
ensureLogging(t, e);
|
|
|
|
// Don't re-enter -- avoid infinite loops if crash-reporting crashes.
|
|
if (mCrashing) return;
|
|
mCrashing = true;
|
|
|
|
// Try to end profiling. If a profiler is running at this point, and we kill the
|
|
// process (below), the in-memory buffer will be lost. So try to stop, which will
|
|
// flush the buffer. (This makes method trace profiling useful to debug crashes.)
|
|
if (ActivityThread.currentActivityThread() != null) {
|
|
ActivityThread.currentActivityThread().stopProfiling();
|
|
}
|
|
|
|
// Bring up crash dialog, wait for it to be dismissed
|
|
ActivityManager.getService().handleApplicationCrash(
|
|
mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
|
|
} catch (Throwable t2) {
|
|
if (t2 instanceof DeadObjectException) {
|
|
// System process is dead; ignore
|
|
} else {
|
|
try {
|
|
Clog_e(TAG, "Error reporting crash", t2);
|
|
} catch (Throwable t3) {
|
|
// Even Clog_e() fails! Oh well.
|
|
}
|
|
}
|
|
} finally {
|
|
// Try everything to make sure this process goes away.
|
|
Process.killProcess(Process.myPid());
|
|
System.exit(10);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensures that the logging handler has been triggered.
|
|
*
|
|
* See b/73380984. This reinstates the pre-O behavior of
|
|
*
|
|
* {@code thread.getUncaughtExceptionHandler().uncaughtException(thread, e);}
|
|
*
|
|
* logging the exception (in addition to killing the app). This behavior
|
|
* was never documented / guaranteed but helps in diagnostics of apps
|
|
* using the pattern.
|
|
*
|
|
* If this KillApplicationHandler is invoked the "regular" way (by
|
|
* {@link Thread#dispatchUncaughtException(Throwable)
|
|
* Thread.dispatchUncaughtException} in case of an uncaught exception)
|
|
* then the pre-handler (expected to be {@link #mLoggingHandler}) will already
|
|
* have run. Otherwise, we manually invoke it here.
|
|
*/
|
|
private void ensureLogging(Thread t, Throwable e) {
|
|
if (!mLoggingHandler.mTriggered) {
|
|
try {
|
|
mLoggingHandler.uncaughtException(t, e);
|
|
} catch (Throwable loggingThrowable) {
|
|
// Ignored.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Common initialization that (unlike {@link #commonInit()} should happen prior to
|
|
* the Zygote fork.
|
|
*/
|
|
public static void preForkInit() {
|
|
if (DEBUG) Slog.d(TAG, "Entered preForkInit.");
|
|
RuntimeInit.enableDdms();
|
|
// TODO(b/142019040#comment13): Decide whether to load the default instance eagerly, i.e.
|
|
// MimeMap.setDefault(DefaultMimeMapFactory.create());
|
|
/*
|
|
* Replace libcore's minimal default mapping between MIME types and file
|
|
* extensions with a mapping that's suitable for Android. Android's mapping
|
|
* contains many more entries that are derived from IANA registrations but
|
|
* with several customizations (extensions, overrides).
|
|
*/
|
|
MimeMap.setDefaultSupplier(DefaultMimeMapFactory::create);
|
|
}
|
|
|
|
@UnsupportedAppUsage
|
|
protected static final void commonInit() {
|
|
if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
|
|
|
|
/*
|
|
* set handlers; these apply to all threads in the VM. Apps can replace
|
|
* the default handler, but not the pre handler.
|
|
*/
|
|
LoggingHandler loggingHandler = new LoggingHandler();
|
|
RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
|
|
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
|
|
|
|
/*
|
|
* Install a time zone supplier that uses the Android persistent time zone system property.
|
|
*/
|
|
RuntimeHooks.setTimeZoneIdSupplier(() -> SystemProperties.get("persist.sys.timezone"));
|
|
|
|
/*
|
|
* 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();
|
|
|
|
/*
|
|
* Sets the default HTTP User-Agent used by HttpURLConnection.
|
|
*/
|
|
String userAgent = getDefaultUserAgent();
|
|
System.setProperty("http.agent", userAgent);
|
|
|
|
/*
|
|
* Wire socket tagging to traffic stats.
|
|
*/
|
|
TrafficStats.attachSocketTagger();
|
|
|
|
initialized = true;
|
|
}
|
|
|
|
/**
|
|
* Returns an HTTP user agent of the form
|
|
* "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MAIN)".
|
|
*/
|
|
private static String getDefaultUserAgent() {
|
|
StringBuilder result = new StringBuilder(64);
|
|
result.append("Dalvik/");
|
|
result.append(System.getProperty("java.vm.version")); // such as 1.1.0
|
|
result.append(" (Linux; U; Android ");
|
|
|
|
String version = Build.VERSION.RELEASE_OR_CODENAME; // "1.0" or "3.4b5"
|
|
result.append(version.length() > 0 ? version : "1.0");
|
|
|
|
// add the model for the release build
|
|
if ("REL".equals(Build.VERSION.CODENAME)) {
|
|
String model = Build.MODEL;
|
|
if (model.length() > 0) {
|
|
result.append("; ");
|
|
result.append(model);
|
|
}
|
|
}
|
|
String id = Build.ID; // "MAIN" or "M4-rc20"
|
|
if (id.length() > 0) {
|
|
result.append(" Build/");
|
|
result.append(id);
|
|
}
|
|
result.append(")");
|
|
return result.toString();
|
|
}
|
|
|
|
/**
|
|
* 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()
|
|
* @param classLoader the classLoader to load {@className} with
|
|
*/
|
|
protected static Runnable findStaticMain(String className, String[] argv,
|
|
ClassLoader classLoader) {
|
|
Class<?> cl;
|
|
|
|
try {
|
|
cl = Class.forName(className, true, classLoader);
|
|
} 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.
|
|
*/
|
|
return new MethodAndArgsCaller(m, argv);
|
|
}
|
|
|
|
@UnsupportedAppUsage
|
|
public static final void main(String[] argv) {
|
|
preForkInit();
|
|
if (argv.length == 2 && argv[1].equals("application")) {
|
|
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
|
|
redirectLogStreams();
|
|
} else {
|
|
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");
|
|
}
|
|
|
|
commonInit();
|
|
|
|
/*
|
|
* Now that we're running in interpreted code, call back into native code
|
|
* to run the system.
|
|
*/
|
|
nativeFinishInit();
|
|
|
|
if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
|
|
}
|
|
|
|
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
|
|
String[] argv, ClassLoader classLoader) {
|
|
// If the application calls System.exit(), terminate the process
|
|
// immediately without running any shutdown hooks. It is not possible to
|
|
// shutdown an Android application gracefully. Among other things, the
|
|
// Android runtime shutdown hooks close the Binder driver, which can cause
|
|
// leftover running threads to crash before the process actually exits.
|
|
nativeSetExitWithoutCleanup(true);
|
|
|
|
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
|
|
VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
|
|
|
|
final Arguments args = new Arguments(argv);
|
|
|
|
// The end of of the RuntimeInit event (see #zygoteInit).
|
|
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
|
|
|
// Remaining arguments are passed to the start class's static main
|
|
return findStaticMain(args.startClass, args.startArgs, classLoader);
|
|
}
|
|
|
|
/**
|
|
* Redirect System.out and System.err to the Android log.
|
|
*/
|
|
@android.ravenwood.annotation.RavenwoodReplace
|
|
public static void redirectLogStreams() {
|
|
System.out.close();
|
|
System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
|
|
System.err.close();
|
|
System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
|
|
}
|
|
|
|
public static void redirectLogStreams$ravenwood() {
|
|
if (sOut$ravenwood == null) {
|
|
sOut$ravenwood = System.out;
|
|
System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
|
|
}
|
|
if (sErr$ravenwood == null) {
|
|
sErr$ravenwood = System.err;
|
|
System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Report a serious error in the current process. May or may not cause
|
|
* the process to terminate (depends on system settings).
|
|
*
|
|
* @param tag to record with the error
|
|
* @param t exception describing the error site and conditions
|
|
*/
|
|
@android.ravenwood.annotation.RavenwoodReplace
|
|
public static void wtf(String tag, Throwable t, boolean system) {
|
|
try {
|
|
boolean exit = false;
|
|
final IActivityManager am = ActivityManager.getService();
|
|
if (am != null) {
|
|
exit = am.handleApplicationWtf(
|
|
mApplicationObject, tag, system,
|
|
new ApplicationErrorReport.ParcelableCrashInfo(t),
|
|
Process.myPid());
|
|
} else {
|
|
// Unlikely but possible in early system boot
|
|
final ApplicationWtfHandler handler = sDefaultApplicationWtfHandler;
|
|
if (handler != null) {
|
|
exit = handler.handleApplicationWtf(
|
|
mApplicationObject, tag, system,
|
|
new ApplicationErrorReport.ParcelableCrashInfo(t),
|
|
Process.myPid());
|
|
} else {
|
|
// Simply log the error
|
|
Slog.e(TAG, "Original WTF:", t);
|
|
}
|
|
}
|
|
if (exit) {
|
|
// The Activity Manager has already written us off -- now exit.
|
|
Process.killProcess(Process.myPid());
|
|
System.exit(10);
|
|
}
|
|
} catch (Throwable t2) {
|
|
if (t2 instanceof DeadObjectException) {
|
|
// System process is dead; ignore
|
|
} else {
|
|
Slog.e(TAG, "Error reporting WTF", t2);
|
|
Slog.e(TAG, "Original WTF:", t);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void wtf$ravenwood(String tag, Throwable t, boolean system) {
|
|
// We've already emitted to logs, so there's nothing more to do here,
|
|
// as we don't have a DropBox pipeline configured
|
|
}
|
|
|
|
/**
|
|
* Set the default {@link ApplicationWtfHandler}, in case the ActivityManager is not ready yet.
|
|
*/
|
|
public static void setDefaultApplicationWtfHandler(final ApplicationWtfHandler handler) {
|
|
sDefaultApplicationWtfHandler = handler;
|
|
}
|
|
|
|
/**
|
|
* The handler to deal with the serious application errors.
|
|
*/
|
|
public interface ApplicationWtfHandler {
|
|
/**
|
|
* @param app object of the crashing app, null for the system server
|
|
* @param tag reported by the caller
|
|
* @param system whether this wtf is coming from the system
|
|
* @param crashInfo describing the context of the error
|
|
* @param immediateCallerPid the caller Pid
|
|
* @return true if the process should exit immediately (WTF is fatal)
|
|
*/
|
|
boolean handleApplicationWtf(IBinder app, String tag, boolean system,
|
|
ApplicationErrorReport.ParcelableCrashInfo crashInfo, int immediateCallerPid);
|
|
}
|
|
|
|
/**
|
|
* Set the object identifying this application/process, for reporting VM
|
|
* errors.
|
|
*/
|
|
public static final void setApplicationObject(IBinder app) {
|
|
mApplicationObject = app;
|
|
}
|
|
|
|
@UnsupportedAppUsage
|
|
public static final IBinder getApplicationObject() {
|
|
return mApplicationObject;
|
|
}
|
|
|
|
/**
|
|
* Enable DDMS.
|
|
*/
|
|
private static void enableDdms() {
|
|
// Register handlers for DDM messages.
|
|
android.ddm.DdmRegister.registerHandlers();
|
|
}
|
|
|
|
/**
|
|
* Handles argument parsing for args related to the runtime.
|
|
*
|
|
* Current recognized args:
|
|
* <ul>
|
|
* <li> <code> [--] <start class name> <args>
|
|
* </ul>
|
|
*/
|
|
static class Arguments {
|
|
/** first non-option argument */
|
|
String startClass;
|
|
|
|
/** all following arguments */
|
|
String[] startArgs;
|
|
|
|
/**
|
|
* Constructs instance and parses args
|
|
* @param args runtime command-line args
|
|
* @throws IllegalArgumentException
|
|
*/
|
|
Arguments(String args[]) throws IllegalArgumentException {
|
|
parseArgs(args);
|
|
}
|
|
|
|
/**
|
|
* Parses the commandline arguments intended for the Runtime.
|
|
*/
|
|
private void parseArgs(String args[])
|
|
throws IllegalArgumentException {
|
|
int curArg = 0;
|
|
for (; curArg < args.length; curArg++) {
|
|
String arg = args[curArg];
|
|
|
|
if (arg.equals("--")) {
|
|
curArg++;
|
|
break;
|
|
} else if (!arg.startsWith("--")) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (curArg == args.length) {
|
|
throw new IllegalArgumentException("Missing classname argument to RuntimeInit!");
|
|
}
|
|
|
|
startClass = args[curArg++];
|
|
startArgs = new String[args.length - curArg];
|
|
System.arraycopy(args, curArg, startArgs, 0, startArgs.length);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper class which holds a method and arguments and can call them. This is used as part of
|
|
* a trampoline to get rid of the initial process setup stack frames.
|
|
*/
|
|
static class MethodAndArgsCaller implements Runnable {
|
|
/** method to call */
|
|
private final Method mMethod;
|
|
|
|
/** argument array */
|
|
private final String[] mArgs;
|
|
|
|
public MethodAndArgsCaller(Method method, String[] args) {
|
|
mMethod = method;
|
|
mArgs = args;
|
|
}
|
|
|
|
public void run() {
|
|
try {
|
|
mMethod.invoke(null, new Object[] { mArgs });
|
|
} catch (IllegalAccessException ex) {
|
|
throw new RuntimeException(ex);
|
|
} catch (InvocationTargetException ex) {
|
|
Throwable cause = ex.getCause();
|
|
if (cause instanceof RuntimeException) {
|
|
throw (RuntimeException) cause;
|
|
} else if (cause instanceof Error) {
|
|
throw (Error) cause;
|
|
}
|
|
throw new RuntimeException(ex);
|
|
}
|
|
}
|
|
}
|
|
}
|