diff options
| -rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 9 | ||||
| -rw-r--r-- | core/java/android/app/ApplicationErrorReport.java | 29 | ||||
| -rw-r--r-- | core/java/android/app/IActivityManager.java | 13 | ||||
| -rw-r--r-- | core/java/android/os/StrictMode.java | 287 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 31 |
5 files changed, 250 insertions, 119 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 43a08b5f56d5..a93184d5d403 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -37,6 +37,7 @@ import android.os.RemoteException; import android.os.IBinder; import android.os.Parcel; import android.os.ServiceManager; +import android.os.StrictMode; import android.text.TextUtils; import android.util.Config; import android.util.Log; @@ -1056,8 +1057,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); IBinder app = data.readStrongBinder(); int violationMask = data.readInt(); - ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data); - handleApplicationStrictModeViolation(app, violationMask, ci); + StrictMode.ViolationInfo info = new StrictMode.ViolationInfo(data); + handleApplicationStrictModeViolation(app, violationMask, info); reply.writeNoException(); return true; } @@ -2584,14 +2585,14 @@ class ActivityManagerProxy implements IActivityManager public void handleApplicationStrictModeViolation(IBinder app, int violationMask, - ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException + StrictMode.ViolationInfo info) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(app); data.writeInt(violationMask); - crashInfo.writeToParcel(data, 0); + info.writeToParcel(data, 0); mRemote.transact(HANDLE_APPLICATION_STRICT_MODE_VIOLATION_TRANSACTION, data, reply, 0); reply.readException(); reply.recycle(); diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java index ddc2b1f5222b..238259627e73 100644 --- a/core/java/android/app/ApplicationErrorReport.java +++ b/core/java/android/app/ApplicationErrorReport.java @@ -24,6 +24,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Parcel; import android.os.Parcelable; +import android.os.SystemClock; import android.os.SystemProperties; import android.provider.Settings; import android.util.Printer; @@ -74,18 +75,15 @@ public class ApplicationErrorReport implements Parcelable { public static final int TYPE_BATTERY = 3; /** - * An error report about a StrictMode violation. - */ - public static final int TYPE_STRICT_MODE_VIOLATION = 4; - - /** - * An error report about a StrictMode violation. + * A report from a user to a developer about a running service that the + * user doesn't think should be running. */ public static final int TYPE_RUNNING_SERVICE = 5; /** * Type of this report. Can be one of {@link #TYPE_NONE}, - * {@link #TYPE_CRASH}, {@link #TYPE_ANR}, or {@link #TYPE_BATTERY}. + * {@link #TYPE_CRASH}, {@link #TYPE_ANR}, {@link #TYPE_BATTERY}, + * or {@link #TYPE_RUNNING_SERVICE}. */ public int type; @@ -133,7 +131,7 @@ public class ApplicationErrorReport implements Parcelable { * of BatteryInfo; otherwise null. */ public BatteryInfo batteryInfo; - + /** * If this report is of type {@link #TYPE_RUNNING_SERVICE}, contains an instance * of RunningServiceInfo; otherwise null. @@ -278,10 +276,6 @@ 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 { /** @@ -320,12 +314,6 @@ 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() { @@ -368,7 +356,6 @@ public class ApplicationErrorReport implements Parcelable { throwMethodName = in.readString(); throwLineNumber = in.readInt(); stackTrace = in.readString(); - durationMillis = in.readLong(); } /** @@ -382,7 +369,6 @@ public class ApplicationErrorReport implements Parcelable { dest.writeString(throwMethodName); dest.writeInt(throwLineNumber); dest.writeString(stackTrace); - dest.writeLong(durationMillis); } /** @@ -396,9 +382,6 @@ 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/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 8ea59a700668..0d35ba4c7d23 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -19,10 +19,10 @@ package android.app; import android.content.ComponentName; import android.content.ContentProviderNative; import android.content.IContentProvider; +import android.content.IIntentReceiver; +import android.content.IIntentSender; import android.content.Intent; import android.content.IntentFilter; -import android.content.IIntentSender; -import android.content.IIntentReceiver; import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; @@ -31,14 +31,15 @@ import android.content.pm.ProviderInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.net.Uri; +import android.os.Bundle; import android.os.Debug; -import android.os.RemoteException; import android.os.IBinder; import android.os.IInterface; import android.os.Parcel; -import android.os.Parcelable; import android.os.ParcelFileDescriptor; -import android.os.Bundle; +import android.os.Parcelable; +import android.os.RemoteException; +import android.os.StrictMode; import java.util.List; @@ -260,7 +261,7 @@ public interface IActivityManager extends IInterface { // bit violated and penalty bits to be executed by the // ActivityManagerService remaining set. public void handleApplicationStrictModeViolation(IBinder app, int violationMask, - ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException; + StrictMode.ViolationInfo crashInfo) throws RemoteException; /* * This will deliver the specified signal to all the persistent processes. Currently only diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index d4b0500a5ac1..ac12e1083227 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -18,6 +18,7 @@ package android.os; import android.app.ActivityManagerNative; import android.app.ApplicationErrorReport; import android.util.Log; +import android.util.Printer; import com.android.internal.os.RuntimeInit; @@ -97,9 +98,9 @@ public final class StrictMode { * via Parcel.writeNoException() (amusingly) where the caller can * choose how to react. */ - private static final ThreadLocal<ArrayList<ApplicationErrorReport.CrashInfo>> gatheredViolations = - new ThreadLocal<ArrayList<ApplicationErrorReport.CrashInfo>>() { - @Override protected ArrayList<ApplicationErrorReport.CrashInfo> initialValue() { + private static final ThreadLocal<ArrayList<ViolationInfo>> gatheredViolations = + new ThreadLocal<ArrayList<ViolationInfo>>() { + @Override protected ArrayList<ViolationInfo> initialValue() { // Starts null to avoid unnecessary allocations when // checking whether there are any violations or not in // hasGatheredViolations() below. @@ -240,7 +241,9 @@ public final class StrictMode { if ((mPolicyMask & DISALLOW_DISK_WRITE) == 0) { return; } - startHandlingViolationException(new StrictModeDiskWriteViolation(mPolicyMask)); + BlockGuard.BlockGuardPolicyException e = new StrictModeDiskWriteViolation(mPolicyMask); + e.fillInStackTrace(); + startHandlingViolationException(e); } // Part of BlockGuard.Policy interface: @@ -248,7 +251,9 @@ public final class StrictMode { if ((mPolicyMask & DISALLOW_DISK_READ) == 0) { return; } - startHandlingViolationException(new StrictModeDiskReadViolation(mPolicyMask)); + BlockGuard.BlockGuardPolicyException e = new StrictModeDiskReadViolation(mPolicyMask); + e.fillInStackTrace(); + startHandlingViolationException(e); } // Part of BlockGuard.Policy interface: @@ -256,7 +261,9 @@ public final class StrictMode { if ((mPolicyMask & DISALLOW_NETWORK) == 0) { return; } - startHandlingViolationException(new StrictModeNetworkViolation(mPolicyMask)); + BlockGuard.BlockGuardPolicyException e = new StrictModeNetworkViolation(mPolicyMask); + e.fillInStackTrace(); + startHandlingViolationException(e); } public void setPolicyMask(int policyMask) { @@ -269,31 +276,70 @@ public final class StrictMode { // thread and, if so, uses it to roughly measure how long the // violation took. void startHandlingViolationException(BlockGuard.BlockGuardPolicyException e) { - e.fillInStackTrace(); - final ApplicationErrorReport.CrashInfo crashInfo = new ApplicationErrorReport.CrashInfo(e); - crashInfo.durationMillis = -1; // unknown - final int savedPolicy = mPolicyMask; + final ViolationInfo info = new ViolationInfo(e, e.getPolicy()); + info.violationUptimeMillis = SystemClock.uptimeMillis(); + handleViolationWithTimingAttempt(info); + } + private static final ThreadLocal<ArrayList<ViolationInfo>> violationsBeingTimed = + new ThreadLocal<ArrayList<ViolationInfo>>() { + @Override protected ArrayList<ViolationInfo> initialValue() { + return new ArrayList<ViolationInfo>(); + } + }; + + // Attempts to fill in the provided ViolationInfo's + // durationMillis field if this thread has a Looper we can use + // to measure with. We measure from the time of violation + // until the time the looper is idle again (right before + // the next epoll_wait) + void handleViolationWithTimingAttempt(final ViolationInfo info) { Looper looper = Looper.myLooper(); + + // Without a Looper, we're unable to time how long the + // violation takes place. This case should be rare, as + // most users will care about timing violations that + // happen on their main UI thread. Note that this case is + // also hit when a violation takes place in a Binder + // thread, in "gather" mode. In this case, the duration + // of the violation is computed by the ultimate caller and + // its Looper, if any. + // TODO: if in gather mode, ignore Looper.myLooper() and always + // go into this immediate mode? if (looper == null) { - // Without a Looper, we're unable to time how long the - // violation takes place. This case should be rare, - // as most users will care about timing violations - // that happen on their main UI thread. - handleViolation(crashInfo, savedPolicy); - } else { - MessageQueue queue = Looper.myQueue(); - final long violationTime = SystemClock.uptimeMillis(); - queue.addIdleHandler(new MessageQueue.IdleHandler() { - public boolean queueIdle() { - long afterViolationTime = SystemClock.uptimeMillis(); - crashInfo.durationMillis = afterViolationTime - violationTime; - handleViolation(crashInfo, savedPolicy); - return false; // remove this idle handler from the array - } - }); + info.durationMillis = -1; // unknown (redundant, already set) + handleViolation(info); + return; } + MessageQueue queue = Looper.myQueue(); + final ArrayList<ViolationInfo> records = violationsBeingTimed.get(); + if (records.size() >= 10) { + // Not worth measuring. Too many offenses in one loop. + return; + } + records.add(info); + if (records.size() > 1) { + // There's already been a violation this loop, so we've already + // registered an idle handler to process the list of violations + // at the end of this Looper's loop. + return; + } + + queue.addIdleHandler(new MessageQueue.IdleHandler() { + public boolean queueIdle() { + long loopFinishTime = SystemClock.uptimeMillis(); + for (int n = 0; n < records.size(); ++n) { + ViolationInfo v = records.get(n); + v.violationNumThisLoop = n + 1; + v.durationMillis = + (int) (loopFinishTime - v.violationUptimeMillis); + handleViolation(v); + } + records.clear(); + return false; // remove this idle handler from the array + } + }); } // Note: It's possible (even quite likely) that the @@ -301,37 +347,35 @@ public final class StrictMode { // violation fired and now (after the violating code ran) due // to people who push/pop temporary policy in regions of code, // hence the policy being passed around. - void handleViolation( - final ApplicationErrorReport.CrashInfo crashInfo, - int policy) { - if (crashInfo.stackTrace == null) { - Log.d(TAG, "unexpected null stacktrace"); + void handleViolation(final ViolationInfo info) { + if (info == null || info.crashInfo == null || info.crashInfo.stackTrace == null) { + Log.wtf(TAG, "unexpected null stacktrace"); return; } - if (LOG_V) Log.d(TAG, "handleViolation; policy=" + policy); + if (LOG_V) Log.d(TAG, "handleViolation; policy=" + info.policy); - if ((policy & PENALTY_GATHER) != 0) { - ArrayList<ApplicationErrorReport.CrashInfo> violations = gatheredViolations.get(); + if ((info.policy & PENALTY_GATHER) != 0) { + ArrayList<ViolationInfo> violations = gatheredViolations.get(); if (violations == null) { - violations = new ArrayList<ApplicationErrorReport.CrashInfo>(1); + violations = new ArrayList<ViolationInfo>(1); gatheredViolations.set(violations); } else if (violations.size() >= 5) { // Too many. In a loop or something? Don't gather them all. return; } - for (ApplicationErrorReport.CrashInfo previous : violations) { - if (crashInfo.stackTrace.equals(previous.stackTrace)) { + for (ViolationInfo previous : violations) { + if (info.crashInfo.stackTrace.equals(previous.crashInfo.stackTrace)) { // Duplicate. Don't log. return; } } - violations.add(crashInfo); + violations.add(info); return; } // Not perfect, but fast and good enough for dup suppression. - Integer crashFingerprint = crashInfo.stackTrace.hashCode(); + Integer crashFingerprint = info.crashInfo.stackTrace.hashCode(); long lastViolationTime = 0; if (mLastViolationTime.containsKey(crashFingerprint)) { lastViolationTime = mLastViolationTime.get(crashFingerprint); @@ -341,13 +385,13 @@ public final class StrictMode { long timeSinceLastViolationMillis = lastViolationTime == 0 ? Long.MAX_VALUE : (now - lastViolationTime); - if ((policy & PENALTY_LOG) != 0 && + if ((info.policy & PENALTY_LOG) != 0 && timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) { - if (crashInfo.durationMillis != -1) { + if (info.durationMillis != -1) { Log.d(TAG, "StrictMode policy violation; ~duration=" + - crashInfo.durationMillis + " ms: " + crashInfo.stackTrace); + info.durationMillis + " ms: " + info.crashInfo.stackTrace); } else { - Log.d(TAG, "StrictMode policy violation: " + crashInfo.stackTrace); + Log.d(TAG, "StrictMode policy violation: " + info.crashInfo.stackTrace); } } @@ -355,20 +399,20 @@ public final class StrictMode { // subset of the original StrictMode policy bitmask, with // only the bit violated and penalty bits to be executed // by the ActivityManagerService remaining set. - int violationMask = 0; + int violationMaskSubset = 0; - if ((policy & PENALTY_DIALOG) != 0 && + if ((info.policy & PENALTY_DIALOG) != 0 && timeSinceLastViolationMillis > MIN_DIALOG_INTERVAL_MS) { - violationMask |= PENALTY_DIALOG; + violationMaskSubset |= PENALTY_DIALOG; } - if ((policy & PENALTY_DROPBOX) != 0 && lastViolationTime == 0) { - violationMask |= PENALTY_DROPBOX; + if ((info.policy & PENALTY_DROPBOX) != 0 && lastViolationTime == 0) { + violationMaskSubset |= PENALTY_DROPBOX; } - if (violationMask != 0) { - int violationBit = parseViolationFromMessage(crashInfo.exceptionMessage); - violationMask |= violationBit; + if (violationMaskSubset != 0) { + int violationBit = parseViolationFromMessage(info.crashInfo.exceptionMessage); + violationMaskSubset |= violationBit; final int savedPolicy = getThreadBlockingPolicy(); try { // First, remove any policy before we call into the Activity Manager, @@ -379,8 +423,8 @@ public final class StrictMode { ActivityManagerNative.getDefault().handleApplicationStrictModeViolation( RuntimeInit.getApplicationObject(), - violationMask, - crashInfo); + violationMaskSubset, + info); } catch (RemoteException e) { Log.e(TAG, "RemoteException trying to handle StrictMode violation", e); } finally { @@ -389,7 +433,7 @@ public final class StrictMode { } } - if ((policy & PENALTY_DEATH) != 0) { + if ((info.policy & PENALTY_DEATH) != 0) { System.err.println("StrictMode policy violation with POLICY_DEATH; shutting down."); Process.killProcess(Process.myPid()); System.exit(10); @@ -417,7 +461,7 @@ public final class StrictMode { * Called from Parcel.writeNoException() */ /* package */ static void writeGatheredViolationsToParcel(Parcel p) { - ArrayList<ApplicationErrorReport.CrashInfo> violations = gatheredViolations.get(); + ArrayList<ViolationInfo> violations = gatheredViolations.get(); if (violations == null) { p.writeInt(0); } else { @@ -439,35 +483,21 @@ public final class StrictMode { */ /* package */ static void readAndHandleBinderCallViolations(Parcel p) { // Our own stack trace to append - Exception e = new LogStackTrace(); StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw)); + new LogStackTrace().printStackTrace(new PrintWriter(sw)); String ourStack = sw.toString(); int policyMask = getThreadBlockingPolicy(); + boolean currentlyGathering = (policyMask & PENALTY_GATHER) != 0; int numViolations = p.readInt(); for (int i = 0; i < numViolations; ++i) { if (LOG_V) Log.d(TAG, "strict mode violation stacks read from binder call. i=" + i); - ApplicationErrorReport.CrashInfo crashInfo = new ApplicationErrorReport.CrashInfo(p); - crashInfo.stackTrace += "# via Binder call with stack:\n" + ourStack; - - // Unlike the in-process violations in which case we - // trigger an error _before_ the thing occurs, in this - // case the violating thing has already occurred, so we - // can't use our heuristic of waiting for the next event - // loop idle cycle to measure the approximate violation - // duration. Instead, just skip that step and use -1 - // (unknown duration) for now. - // TODO: keep a thread-local on remote process of first - // violation time's uptimeMillis, and when writing that - // back out in Parcel reply, include in the header the - // violation time and use it here. - crashInfo.durationMillis = -1; - + ViolationInfo info = new ViolationInfo(p, !currentlyGathering); + info.crashInfo.stackTrace += "# via Binder call with stack:\n" + ourStack; BlockGuard.Policy policy = BlockGuard.getThreadPolicy(); if (policy instanceof AndroidBlockGuardPolicy) { - ((AndroidBlockGuardPolicy) policy).handleViolation(crashInfo, policyMask); + ((AndroidBlockGuardPolicy) policy).handleViolationWithTimingAttempt(info); } } } @@ -483,4 +513,113 @@ public final class StrictMode { private static void onBinderStrictModePolicyChange(int newPolicy) { setBlockGuardPolicy(newPolicy); } + + /** + * Parcelable that gets sent in Binder call headers back to callers + * to report violations that happened during a cross-process call. + * + * @hide + */ + public static class ViolationInfo { + /** + * Stack and other stuff info. + */ + public final ApplicationErrorReport.CrashInfo crashInfo; + + /** + * The strict mode policy mask at the time of violation. + */ + public final int policy; + + /** + * The wall time duration of the violation, when known. -1 when + * not known. + */ + public int durationMillis = -1; + + /** + * Which violation number this was (1-based) since the last Looper loop, + * from the perspective of the root caller (if it crossed any processes + * via Binder calls). The value is 0 if the root caller wasn't on a Looper + * thread. + */ + public int violationNumThisLoop; + + /** + * The time (in terms of SystemClock.uptimeMillis()) that the + * violation occurred. + */ + public long violationUptimeMillis; + + /** + * Create an uninitialized instance of ViolationInfo + */ + public ViolationInfo() { + crashInfo = null; + policy = 0; + } + + /** + * Create an instance of ViolationInfo initialized from an exception. + */ + public ViolationInfo(Throwable tr, int policy) { + crashInfo = new ApplicationErrorReport.CrashInfo(tr); + violationUptimeMillis = SystemClock.uptimeMillis(); + this.policy = policy; + } + + /** + * Create an instance of ViolationInfo initialized from a Parcel. + */ + public ViolationInfo(Parcel in) { + this(in, false); + } + + /** + * Create an instance of ViolationInfo initialized from a Parcel. + * + * @param unsetGatheringBit if true, the caller is the root caller + * and the gathering penalty should be removed. + */ + public ViolationInfo(Parcel in, boolean unsetGatheringBit) { + crashInfo = new ApplicationErrorReport.CrashInfo(in); + int rawPolicy = in.readInt(); + if (unsetGatheringBit) { + policy = rawPolicy & ~PENALTY_GATHER; + } else { + policy = rawPolicy; + } + durationMillis = in.readInt(); + violationNumThisLoop = in.readInt(); + violationUptimeMillis = in.readLong(); + } + + /** + * Save a ViolationInfo instance to a parcel. + */ + public void writeToParcel(Parcel dest, int flags) { + crashInfo.writeToParcel(dest, flags); + dest.writeInt(policy); + dest.writeInt(durationMillis); + dest.writeInt(violationNumThisLoop); + dest.writeLong(violationUptimeMillis); + } + + + /** + * Dump a ViolationInfo instance to a Printer. + */ + public void dump(Printer pw, String prefix) { + crashInfo.dump(pw, prefix); + pw.println(prefix + "policy: " + policy); + if (durationMillis != -1) { + pw.println(prefix + "durationMillis: " + durationMillis); + } + if (violationNumThisLoop != 0) { + pw.println(prefix + "violationNumThisLoop: " + violationNumThisLoop); + } + pw.println(prefix + "violationUptimeMillis: " + violationUptimeMillis); + } + + } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index cb2ae735b9f5..415073e60cbb 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -6125,17 +6125,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } public void handleApplicationStrictModeViolation( - IBinder app, int violationMask, ApplicationErrorReport.CrashInfo crashInfo) { + IBinder app, + int violationMask, + StrictMode.ViolationInfo info) { ProcessRecord r = findAppProcess(app); if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) { - Integer stackFingerprint = crashInfo.stackTrace.hashCode(); + Integer stackFingerprint = info.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 info.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 @@ -6148,7 +6150,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } if (logIt) { - logStrictModeViolationToDropBox(r, crashInfo); + logStrictModeViolationToDropBox(r, info); } } @@ -6163,7 +6165,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen data.put("result", result); data.put("app", r); data.put("violationMask", violationMask); - data.put("crashInfo", crashInfo); + data.put("info", info); msg.obj = data; mHandler.sendMessage(msg); @@ -6178,9 +6180,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // these in quick succession so we try to batch these together to // minimize disk writes, number of dropbox entries, and maximize // compression, by having more fewer, larger records. - private void logStrictModeViolationToDropBox(ProcessRecord process, - ApplicationErrorReport.CrashInfo crashInfo) { - if (crashInfo == null) { + private void logStrictModeViolationToDropBox( + ProcessRecord process, + StrictMode.ViolationInfo info) { + if (info == null) { return; } final boolean isSystemApp = process == null || @@ -6201,12 +6204,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen appendDropBoxProcessHeaders(process, sb); sb.append("Build: ").append(Build.FINGERPRINT).append("\n"); sb.append("System-App: ").append(isSystemApp).append("\n"); - if (crashInfo != null && crashInfo.durationMillis != -1) { - sb.append("Duration-Millis: ").append(crashInfo.durationMillis).append("\n"); + sb.append("Uptime-Millis: ").append(info.violationUptimeMillis).append("\n"); + if (info.violationNumThisLoop != 0) { + sb.append("Loop-Violation-Number: ").append(info.violationNumThisLoop).append("\n"); + } + if (info != null && info.durationMillis != -1) { + sb.append("Duration-Millis: ").append(info.durationMillis).append("\n"); } sb.append("\n"); - if (crashInfo != null && crashInfo.stackTrace != null) { - sb.append(crashInfo.stackTrace); + if (info.crashInfo != null && info.crashInfo.stackTrace != null) { + sb.append(info.crashInfo.stackTrace); } sb.append("\n"); |