DropBox logging of app & system server crashes.
The crashes are also reported to the event log (and of course the
main logcat, like they always have been). Ordinary Log.e(t,m,e) isn't dropboxed
but there's a new Log.wtf() which always is. (Still @pending in this change.)
Add a hook to IPowerManager to crash the system server on demand
(only for apps with REBOOT permission, since it's basically a restart).
This is not exposed in PowerManager, must be invoked directly -- mostly
this is there so "Bad Behavior" in dev tools can do it.
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 676d6d56..7d07604 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -606,7 +606,7 @@
public int uid;
/**
- * The tag that was provided when the process crashed.
+ * The activity name associated with the error, if known. May be null.
*/
public String tag;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a0498aa..90f46dd 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -979,13 +979,23 @@
return true;
}
- case HANDLE_APPLICATION_ERROR_TRANSACTION: {
+ case HANDLE_APPLICATION_CRASH_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder app = data.readStrongBinder();
+ ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data);
+ handleApplicationCrash(app, ci);
+ reply.writeNoException();
+ return true;
+ }
+
+ case HANDLE_APPLICATION_WTF_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder app = data.readStrongBinder();
String tag = data.readString();
ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data);
- handleApplicationError(app, tag, ci);
+ boolean res = handleApplicationWtf(app, tag, ci);
reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
return true;
}
@@ -2337,7 +2347,20 @@
/* this base class version is never called */
return true;
}
- public void handleApplicationError(IBinder app, String tag,
+ public void handleApplicationCrash(IBinder app,
+ ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(app);
+ crashInfo.writeToParcel(data, 0);
+ mRemote.transact(HANDLE_APPLICATION_CRASH_TRANSACTION, data, reply, 0);
+ reply.readException();
+ reply.recycle();
+ data.recycle();
+ }
+ public boolean handleApplicationWtf(IBinder app, String tag,
ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException
{
Parcel data = Parcel.obtain();
@@ -2346,10 +2369,12 @@
data.writeStrongBinder(app);
data.writeString(tag);
crashInfo.writeToParcel(data, 0);
- mRemote.transact(HANDLE_APPLICATION_ERROR_TRANSACTION, data, reply, 0);
+ mRemote.transact(HANDLE_APPLICATION_WTF_TRANSACTION, data, reply, 0);
reply.readException();
+ boolean res = reply.readInt() != 0;
reply.recycle();
data.recycle();
+ return res;
}
public void signalPersistentProcesses(int sig) throws RemoteException {
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index e89b3ad0..a4b692f 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -195,6 +195,7 @@
StringWriter sw = new StringWriter();
tr.printStackTrace(new PrintWriter(sw));
stackTrace = sw.toString();
+ exceptionMessage = tr.getMessage();
// Populate fields with the "root cause" exception
while (tr.getCause() != null) {
diff --git a/core/java/android/app/IActivityController.aidl b/core/java/android/app/IActivityController.aidl
index 804dd61..c76a517 100644
--- a/core/java/android/app/IActivityController.aidl
+++ b/core/java/android/app/IActivityController.aidl
@@ -44,7 +44,7 @@
* it immediately.
*/
boolean appCrashed(String processName, int pid,
- String tag, String shortMsg, String longMsg,
+ String shortMsg, String longMsg,
long timeMillis, String stackTrace);
/**
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c890c4c..ca6bfa7 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -242,8 +242,9 @@
// Special low-level communication with activity manager.
public void startRunning(String pkg, String cls, String action,
String data) throws RemoteException;
-
- public void handleApplicationError(IBinder app, String tag,
+ public void handleApplicationCrash(IBinder app,
+ ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException;
+ public boolean handleApplicationWtf(IBinder app, String tag,
ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException;
/*
@@ -349,7 +350,7 @@
// Please keep these transaction codes the same -- they are also
// sent by C++ code.
int START_RUNNING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
- int HANDLE_APPLICATION_ERROR_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1;
+ int HANDLE_APPLICATION_CRASH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1;
int START_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2;
int UNHANDLED_BACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3;
int OPEN_CONTENT_URI_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4;
@@ -446,4 +447,5 @@
int KILL_APPLICATION_PROCESS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+98;
int START_ACTIVITY_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+99;
int OVERRIDE_PENDING_TRANSITION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+100;
+ int HANDLE_APPLICATION_WTF_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+101;
}
diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java
index 38d252e..3457815 100644
--- a/core/java/android/os/FileObserver.java
+++ b/core/java/android/os/FileObserver.java
@@ -103,9 +103,7 @@
try {
observer.onEvent(mask, path);
} catch (Throwable throwable) {
- Log.e(LOG_TAG, "Unhandled throwable " + throwable.toString() +
- " (returned by observer " + observer + ")", throwable);
- RuntimeInit.crash("FileObserver", throwable);
+ Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable);
}
}
}
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 0afc537..23762ca 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -31,6 +31,7 @@
void preventScreenOn(boolean prevent);
boolean isScreenOn();
void reboot(String reason);
+ void crash(String message);
// sets the brightness of the backlights (screen, keyboard, button) 0-255
void setBacklightBrightness(int brightness);
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index caf0923..bc653d6 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -115,9 +115,7 @@
didIdle = true;
keep = ((IdleHandler)idler).queueIdle();
} catch (Throwable t) {
- Log.e("MessageQueue",
- "IdleHandler threw exception", t);
- RuntimeInit.crash("MessageQueue", t);
+ Log.wtf("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 293dabc..e4eaf45b 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -326,12 +326,11 @@
{
synchronized (mToken) {
if (mHeld) {
+ Log.wtf(TAG, "WakeLock finalized while still held: " + mTag);
try {
mService.releaseWakeLock(mToken, 0);
} catch (RemoteException e) {
}
- RuntimeInit.crash(TAG, new Exception(
- "WakeLock finalized while still held: "+mTag));
}
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 947ab19..09651f1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3710,6 +3710,11 @@
"dropbox:";
/**
+ * Nonzero causes Log.wtf() to crash.
+ */
+ public static final String WTF_IS_FATAL = "wtf_is_fatal";
+
+ /**
* The length of time in milli-seconds that automatic small adjustments to
* SystemClock are ignored if NITZ_UPDATE_DIFF is not exceeded.
*/
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index e95d0be..7a959a6 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -81,6 +81,13 @@
*/
public static final int ASSERT = 7;
+ /**
+ * Exception class used to capture a stack trace in {@link #wtf()}.
+ */
+ private static class TerribleFailure extends Exception {
+ TerribleFailure(String msg, Throwable cause) { super(msg, cause); }
+ }
+
private Log() {
}
@@ -170,24 +177,24 @@
/**
* Checks to see whether or not a log for the specified tag is loggable at the specified level.
- *
+ *
* The default level of any tag is set to INFO. This means that any level above and including
* INFO will be logged. Before you make any calls to a logging method you should check to see
* if your tag should be logged. You can change the default level by setting a system property:
* 'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>'
- * Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPRESS will
+ * Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will
* turn off all logging for your tag. You can also create a local.prop file that with the
* following in it:
* 'log.tag.<YOUR_LOG_TAG>=<LEVEL>'
* and place that in /data/local.prop.
- *
+ *
* @param tag The tag to check.
* @param level The level to check.
* @return Whether or not that this is allowed to be logged.
* @throws IllegalArgumentException is thrown if the tag.length() > 23.
*/
public static native boolean isLoggable(String tag, int level);
-
+
/*
* Send a {@link #WARN} log message and log the exception.
* @param tag Used to identify the source of a log message. It usually identifies
@@ -220,6 +227,46 @@
}
/**
+ * What a Terrible Failure: Report a condition that should never happen.
+ * The error will always be logged at level ASSERT with the call stack.
+ * Depending on system configuration, a report may be added to the
+ * {@link android.os.DropBoxManager} and/or the process may be terminated
+ * immediately with an error dialog.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ * @pending
+ */
+ public static int wtf(String tag, String msg) {
+ return wtf(tag, msg, null);
+ }
+
+ /**
+ * What a Terrible Failure: Report an exception that should never happen.
+ * Similar to {@link #wtf(String, String)}, with an exception to log.
+ * @param tag Used to identify the source of a log message.
+ * @param tr An exception to log.
+ * @pending
+ */
+ public static int wtf(String tag, Throwable tr) {
+ return wtf(tag, tr.getMessage(), tr);
+ }
+
+ /**
+ * What a Terrible Failure: Report an exception that should never happen.
+ * Similar to {@link #wtf(String, Throwable)}, with a message as well.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ * @param tr An exception to log. May be null.
+ * @pending
+ */
+ public static int wtf(String tag, String msg, Throwable tr) {
+ tr = new TerribleFailure(msg, tr);
+ int bytes = println(ASSERT, tag, getStackTraceString(tr));
+ RuntimeInit.wtf(tag, tr);
+ return bytes;
+ }
+
+ /**
* Handy function to get a loggable stack trace from a Throwable
* @param tr An exception to log
*/