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/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;
}