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 676d6d5..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
*/
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index b7bb72d..57a28e6 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -58,6 +58,10 @@
/** true if commonInit() has been called */
private static boolean initialized;
+ private static IBinder mApplicationObject;
+
+ private static volatile boolean mCrashing = false;
+
/**
* Use this to log a message when a thread exits due to an uncaught
* exception. The framework catches these for the main threads, so
@@ -66,14 +70,30 @@
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.
+ // Don't re-enter -- avoid infinite loops if crash-reporting crashes.
+ if (mCrashing) return;
+ mCrashing = true;
+
+ if (mApplicationObject == null) {
+ Log.e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
+ } else {
+ Log.e(TAG, "FATAL EXCEPTION: " + t.getName(), e);
+ }
+
+ // Bring up crash dialog, wait for it to be dismissed
+ ActivityManagerNative.getDefault().handleApplicationCrash(
+ mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
+ } catch (Throwable t2) {
+ try {
+ Log.e(TAG, "Error reporting crash", t2);
+ } catch (Throwable t3) {
+ // Even Log.e() fails! Oh well.
+ }
+ } finally {
+ // Try everything to make sure this process goes away.
+ Process.killProcess(Process.myPid());
+ System.exit(10);
}
- crash(TAG, e);
}
}
@@ -300,46 +320,22 @@
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.
+ * Report a serious error in the current process. May or may not cause
+ * the process to terminate (depends on system settings).
*
- * @param tag to use when logging the error
- * @param t exception that was generated by the error
+ * @param tag to record with the error
+ * @param t exception describing the error site and conditions
*/
- public static void crash(String tag, Throwable t) {
- if (mApplicationObject != null) {
- try {
- // Log exception.
- Log.e(TAG, Log.getStackTraceString(t));
-
- // Show a message to the user.
- IActivityManager am = ActivityManagerNative.getDefault();
- am.handleApplicationError(mApplicationObject, tag,
- new ApplicationErrorReport.CrashInfo(t));
- } 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.
+ public static void wtf(String tag, Throwable t) {
+ try {
+ if (ActivityManagerNative.getDefault().handleApplicationWtf(
+ mApplicationObject, tag, new ApplicationErrorReport.CrashInfo(t))) {
+ // The Activity Manager has already written us off -- now exit.
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));
- } 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);
- }
+ } catch (Throwable t2) {
+ Log.e(TAG, "Error reporting WTF", t2);
}
}
@@ -361,6 +357,4 @@
// Register handlers for DDM messages.
android.ddm.DdmRegister.registerHandlers();
}
-
- private static IBinder mApplicationObject;
}
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index e11c312..d740ce1 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -2137,6 +2137,24 @@
}
}
+ /**
+ * Crash the runtime (causing a complete restart of the Android framework).
+ * Requires REBOOT permission. Mostly for testing. Should not return.
+ */
+ public void crash(final String message)
+ {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
+ Thread t = new Thread("PowerManagerService.crash()") {
+ public void run() { throw new RuntimeException(message); }
+ };
+ try {
+ t.start();
+ t.join();
+ } catch (InterruptedException e) {
+ Log.wtf(TAG, e);
+ }
+ }
+
private void goToSleepLocked(long time, int reason) {
if (mLastEventTime <= time) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 266f213..31edaf2 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -71,7 +71,9 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Build;
import android.os.Debug;
+import android.os.DropBoxManager;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
@@ -8580,11 +8582,10 @@
}
private boolean makeAppCrashingLocked(ProcessRecord app,
- String tag, String shortMsg, String longMsg, String stackTrace) {
+ String shortMsg, String longMsg, String stackTrace) {
app.crashing = true;
app.crashingReport = generateProcessError(app,
- ActivityManager.ProcessErrorStateInfo.CRASHED, tag, shortMsg, longMsg,
- stackTrace);
+ ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
startAppProblemLocked(app);
app.stopFreezingAllLocked();
return handleAppCrashLocked(app);
@@ -8658,10 +8659,11 @@
}
private void makeAppNotRespondingLocked(ProcessRecord app,
- String tag, String shortMsg, String longMsg) {
+ String activity, String shortMsg, String longMsg) {
app.notResponding = true;
app.notRespondingReport = generateProcessError(app,
- ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, tag, shortMsg, longMsg, null);
+ ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
+ activity, shortMsg, longMsg, null);
startAppProblemLocked(app);
app.stopFreezingAllLocked();
}
@@ -8672,7 +8674,7 @@
* @param app The ProcessRecord in which the error occurred.
* @param condition Crashing, Application Not Responding, etc. Values are defined in
* ActivityManager.AppErrorStateInfo
- * @param tag The tag that was passed into handleApplicationError(). Typically the classname.
+ * @param activity The activity associated with the crash, if known.
* @param shortMsg Short message describing the crash.
* @param longMsg Long message describing the crash.
* @param stackTrace Full crash stack trace, may be null.
@@ -8680,14 +8682,14 @@
* @return Returns a fully-formed AppErrorStateInfo record.
*/
private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
- int condition, String tag, String shortMsg, String longMsg, String stackTrace) {
+ int condition, String activity, String shortMsg, String longMsg, String stackTrace) {
ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo();
report.condition = condition;
report.processName = app.processName;
report.pid = app.pid;
report.uid = app.info.uid;
- report.tag = tag;
+ report.tag = activity;
report.shortMsg = shortMsg;
report.longMsg = longMsg;
report.stackTrace = stackTrace;
@@ -8804,10 +8806,126 @@
}
}
- public void handleApplicationError(IBinder app, String tag,
+ /**
+ * Used by {@link com.android.internal.os.RuntimeInit} to report when an application crashes.
+ * The application process will exit immediately after this call returns.
+ * @param app object of the crashing app, null for the system server
+ * @param crashInfo describing the exception
+ */
+ public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {
+ ProcessRecord r = findAppProcess(app);
+
+ EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),
+ app == null ? "system" : (r == null ? "unknown" : r.processName),
+ crashInfo.exceptionClassName,
+ crashInfo.exceptionMessage,
+ crashInfo.throwFileName,
+ crashInfo.throwLineNumber);
+
+ addExceptionToDropBox("crash", r, null, crashInfo);
+
+ crashApplication(r, crashInfo);
+ }
+
+ /**
+ * Used by {@link Log} via {@link com.android.internal.os.RuntimeInit} to report serious errors.
+ * @param app object of the crashing app, null for the system server
+ * @param tag reported by the caller
+ * @param crashInfo describing the context of the error
+ * @return true if the process should exit immediately (WTF is fatal)
+ */
+ public boolean handleApplicationWtf(IBinder app, String tag,
ApplicationErrorReport.CrashInfo crashInfo) {
- AppErrorResult result = new AppErrorResult();
- ProcessRecord r = null;
+ ProcessRecord r = findAppProcess(app);
+
+ EventLog.writeEvent(EventLogTags.AM_WTF, Binder.getCallingPid(),
+ app == null ? "system" : (r == null ? "unknown" : r.processName),
+ tag, crashInfo.exceptionMessage);
+
+ addExceptionToDropBox("wtf", r, tag, crashInfo);
+
+ if (Settings.Gservices.getInt(mContext.getContentResolver(),
+ Settings.Gservices.WTF_IS_FATAL, 0) != 0) {
+ crashApplication(r, crashInfo);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @param app object of some object (as stored in {@link com.android.internal.os.RuntimeInit})
+ * @return the corresponding {@link ProcessRecord} object, or null if none could be found
+ */
+ private ProcessRecord findAppProcess(IBinder app) {
+ if (app == null) {
+ return null;
+ }
+
+ synchronized (this) {
+ for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
+ final int NA = apps.size();
+ for (int ia=0; ia<NA; ia++) {
+ ProcessRecord p = apps.valueAt(ia);
+ if (p.thread != null && p.thread.asBinder() == app) {
+ return p;
+ }
+ }
+ }
+
+ Log.w(TAG, "Can't find mystery application: " + app);
+ return null;
+ }
+ }
+
+ /**
+ * Write a description of an exception (from a crash or WTF report) to the drop box.
+ * @param eventType to include in the drop box tag ("crash", "wtf", etc.)
+ * @param r the process which crashed, null for the system server
+ * @param tag supplied by the application (in the case of WTF), or null
+ * @param crashInfo describing the exception
+ */
+ private void addExceptionToDropBox(String eventType, ProcessRecord r, String tag,
+ ApplicationErrorReport.CrashInfo crashInfo) {
+ String dropboxTag, processName;
+ if (r == null) {
+ dropboxTag = "system_server_" + eventType;
+ } else if ((r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ dropboxTag = "system_app_" + eventType;
+ } else {
+ dropboxTag = "data_app_" + eventType;
+ }
+
+ DropBoxManager dbox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
+ if (dbox != null && dbox.isTagEnabled(dropboxTag)) {
+ StringBuilder sb = new StringBuilder(1024);
+ sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
+ if (r == null) {
+ sb.append("Process: system_server\n");
+ } else {
+ sb.append("Package: ").append(r.info.packageName).append("\n");
+ if (!r.processName.equals(r.info.packageName)) {
+ sb.append("Process: ").append(r.processName).append("\n");
+ }
+ }
+ if (tag != null) {
+ sb.append("Tag: ").append(tag).append("\n");
+ }
+ if (crashInfo != null && crashInfo.stackTrace != null) {
+ sb.append("\n").append(crashInfo.stackTrace);
+ }
+ dbox.addText(dropboxTag, sb.toString());
+ }
+ }
+
+ /**
+ * Bring up the "unexpected error" dialog box for a crashing app.
+ * Deal with edge cases (intercepts from instrumented applications,
+ * ActivityController, error intent receivers, that sort of thing).
+ * @param r the application crashing
+ * @param crashInfo describing the failure
+ */
+ private void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
long timeMillis = System.currentTimeMillis();
String shortMsg = crashInfo.exceptionClassName;
String longMsg = crashInfo.exceptionMessage;
@@ -8818,20 +8936,8 @@
longMsg = shortMsg;
}
+ AppErrorResult result = new AppErrorResult();
synchronized (this) {
- if (app != null) {
- for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
- final int NA = apps.size();
- for (int ia=0; ia<NA; ia++) {
- ProcessRecord p = apps.valueAt(ia);
- if (p.thread != null && p.thread.asBinder() == app) {
- r = p;
- break;
- }
- }
- }
- }
-
if (r != null) {
// The application has crashed. Send the SIGQUIT to the process so
// that it can dump its state.
@@ -8844,7 +8950,7 @@
try {
String name = r != null ? r.processName : null;
int pid = r != null ? r.pid : Binder.getCallingPid();
- if (!mController.appCrashed(name, pid, tag,
+ if (!mController.appCrashed(name, pid,
shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
Log.w(TAG, "Force-killing crashed app " + name
+ " at watcher's request");
@@ -8872,15 +8978,9 @@
return;
}
- if (r != null) {
- if (!makeAppCrashingLocked(r, tag, shortMsg, longMsg, stackTrace)) {
- return;
- }
- } else {
- Log.w(TAG, "Some application object " + app + " tag " + tag
- + " has crashed, but I don't know who it is.");
- Log.w(TAG, "ShortMsg:" + shortMsg);
- Log.w(TAG, "LongMsg:" + longMsg);
+ // If we can't identify the process or it's already exceeded its crash quota,
+ // quit right away without showing a crash dialog.
+ if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace)) {
Binder.restoreCallingIdentity(origId);
return;
}
diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags
index 7e4ea35..952555b 100644
--- a/services/java/com/android/server/am/EventLogTags.logtags
+++ b/services/java/com/android/server/am/EventLogTags.logtags
@@ -79,4 +79,7 @@
# The activity manager gave up on a new process taking too long to start
30037 am_process_start_timeout (PID|1|5),(UID|1|5),(Process Name|3)
-
+# Unhandled exception
+30039 am_crash (PID|1|5),(Process Name|3),(Exception|3),(Message|3),(File|3),(Line|1|5)
+# Log.wtf() called
+30040 am_wtf (PID|1|5),(Process Name|3),(Tag|3),(Message|3)