summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ApplicationErrorReport.java15
-rw-r--r--core/java/android/os/StrictMode.java1
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java39
3 files changed, 51 insertions, 4 deletions
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index 57a23aeb8914..9834c4c0f542 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -254,6 +254,10 @@ public class ApplicationErrorReport implements Parcelable {
/**
* Describes an application crash.
+ *
+ * <p>This is also used to marshal around stack traces of ANRs and
+ * StrictMode violations which aren't necessarily crashes, but have
+ * a lot in common.
*/
public static class CrashInfo {
/**
@@ -292,6 +296,12 @@ public class ApplicationErrorReport implements Parcelable {
public String stackTrace;
/**
+ * For StrictMode violations, the wall time duration of the
+ * violation, when known.
+ */
+ public long durationMillis = -1;
+
+ /**
* Create an uninitialized instance of CrashInfo.
*/
public CrashInfo() {
@@ -334,6 +344,7 @@ public class ApplicationErrorReport implements Parcelable {
throwMethodName = in.readString();
throwLineNumber = in.readInt();
stackTrace = in.readString();
+ durationMillis = in.readLong();
}
/**
@@ -347,6 +358,7 @@ public class ApplicationErrorReport implements Parcelable {
dest.writeString(throwMethodName);
dest.writeInt(throwLineNumber);
dest.writeString(stackTrace);
+ dest.writeLong(durationMillis);
}
/**
@@ -360,6 +372,9 @@ public class ApplicationErrorReport implements Parcelable {
pw.println(prefix + "throwMethodName: " + throwMethodName);
pw.println(prefix + "throwLineNumber: " + throwLineNumber);
pw.println(prefix + "stackTrace: " + stackTrace);
+ if (durationMillis != -1) {
+ pw.println(prefix + "durationMillis: " + durationMillis);
+ }
}
}
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index c0ae263d7c2c..9b18719670fb 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -187,6 +187,7 @@ public final class StrictMode {
// Not _really_ a Crash, but we use the same data structure...
ApplicationErrorReport.CrashInfo crashInfo =
new ApplicationErrorReport.CrashInfo(violation);
+ crashInfo.durationMillis = durationMillis;
// Not perfect, but fast and good enough for dup suppression.
Integer crashFingerprint = crashInfo.stackTrace.hashCode();
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index e93f7ff2c669..ec209eda3178 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -648,6 +648,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
= new HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>>();
/**
+ * Fingerprints (String.hashCode()) of stack traces that we've
+ * already logged DropBox entries for. Guarded by itself. If
+ * something (rogue user app) forces this over
+ * MAX_DUP_SUPPRESSED_STACKS entries, the contents are cleared.
+ */
+ private final HashSet<Integer> mAlreadyLoggedViolatedStacks = new HashSet<Integer>();
+ private static final int MAX_DUP_SUPPRESSED_STACKS = 5000;
+
+ /**
* Intent broadcast that we have tried to start, but are
* waiting for its application's process to be created. We only
* need one (instead of a list) because we always process broadcasts
@@ -9357,12 +9366,29 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public void handleApplicationStrictModeViolation(
IBinder app, int violationMask, ApplicationErrorReport.CrashInfo crashInfo) {
ProcessRecord r = findAppProcess(app);
- // TODO: implement
- Log.w(TAG, "handleApplicationStrictModeViolation.");
if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) {
- Integer crashFingerprint = crashInfo.stackTrace.hashCode();
- Log.d(TAG, "supposed to drop box for fingerprint " + crashFingerprint);
+ Integer stackFingerprint = crashInfo.stackTrace.hashCode();
+ boolean logIt = true;
+ synchronized (mAlreadyLoggedViolatedStacks) {
+ if (mAlreadyLoggedViolatedStacks.contains(stackFingerprint)) {
+ logIt = false;
+ // TODO: sub-sample into EventLog for these, with
+ // the crashInfo.durationMillis? Then we'd get
+ // the relative pain numbers, without logging all
+ // the stack traces repeatedly. We'd want to do
+ // likewise in the client code, which also does
+ // dup suppression, before the Binder call.
+ } else {
+ if (mAlreadyLoggedViolatedStacks.size() >= MAX_DUP_SUPPRESSED_STACKS) {
+ mAlreadyLoggedViolatedStacks.clear();
+ }
+ mAlreadyLoggedViolatedStacks.add(stackFingerprint);
+ }
+ }
+ if (logIt) {
+ addErrorToDropBox("strictmode", r, null, null, null, null, null, crashInfo);
+ }
}
if ((violationMask & StrictMode.PENALTY_DIALOG) != 0) {
@@ -9375,6 +9401,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
HashMap<String, Object> data = new HashMap<String, Object>();
data.put("result", result);
data.put("app", r);
+ data.put("violationMask", violationMask);
+ data.put("crashInfo", crashInfo);
msg.obj = data;
mHandler.sendMessage(msg);
@@ -9510,6 +9538,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
sb.append("Subject: ").append(subject).append("\n");
}
sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
+ if (crashInfo.durationMillis != -1) {
+ sb.append("Duration-Millis: ").append(crashInfo.durationMillis).append("\n");
+ }
sb.append("\n");
// Do the rest in a worker thread to avoid blocking the caller on I/O