am d2165cfc: Merge "StrictMode: time violations in Binder calls" into gingerbread

Merge commit 'd2165cfce7911dac66d1195ed9123e79b086d22b' into gingerbread-plus-aosp

* commit 'd2165cfce7911dac66d1195ed9123e79b086d22b':
  StrictMode: time violations in Binder calls
This commit is contained in:
Brad Fitzpatrick
2010-07-30 15:23:19 -07:00
committed by Android Git Automerger
5 changed files with 250 additions and 119 deletions

View File

@ -37,6 +37,7 @@ import android.os.RemoteException;
import android.os.IBinder; import android.os.IBinder;
import android.os.Parcel; import android.os.Parcel;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.StrictMode;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Config; import android.util.Config;
import android.util.Log; import android.util.Log;
@ -1056,8 +1057,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
data.enforceInterface(IActivityManager.descriptor); data.enforceInterface(IActivityManager.descriptor);
IBinder app = data.readStrongBinder(); IBinder app = data.readStrongBinder();
int violationMask = data.readInt(); int violationMask = data.readInt();
ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data); StrictMode.ViolationInfo info = new StrictMode.ViolationInfo(data);
handleApplicationStrictModeViolation(app, violationMask, ci); handleApplicationStrictModeViolation(app, violationMask, info);
reply.writeNoException(); reply.writeNoException();
return true; return true;
} }
@ -2571,14 +2572,14 @@ class ActivityManagerProxy implements IActivityManager
public void handleApplicationStrictModeViolation(IBinder app, public void handleApplicationStrictModeViolation(IBinder app,
int violationMask, int violationMask,
ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException StrictMode.ViolationInfo info) throws RemoteException
{ {
Parcel data = Parcel.obtain(); Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain(); Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor); data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(app); data.writeStrongBinder(app);
data.writeInt(violationMask); data.writeInt(violationMask);
crashInfo.writeToParcel(data, 0); info.writeToParcel(data, 0);
mRemote.transact(HANDLE_APPLICATION_STRICT_MODE_VIOLATION_TRANSACTION, data, reply, 0); mRemote.transact(HANDLE_APPLICATION_STRICT_MODE_VIOLATION_TRANSACTION, data, reply, 0);
reply.readException(); reply.readException();
reply.recycle(); reply.recycle();

View File

@ -24,6 +24,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.os.SystemClock;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.provider.Settings; import android.provider.Settings;
import android.util.Printer; import android.util.Printer;
@ -74,18 +75,15 @@ public class ApplicationErrorReport implements Parcelable {
public static final int TYPE_BATTERY = 3; public static final int TYPE_BATTERY = 3;
/** /**
* 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_STRICT_MODE_VIOLATION = 4;
/**
* An error report about a StrictMode violation.
*/ */
public static final int TYPE_RUNNING_SERVICE = 5; public static final int TYPE_RUNNING_SERVICE = 5;
/** /**
* Type of this report. Can be one of {@link #TYPE_NONE}, * 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; public int type;
@ -133,7 +131,7 @@ public class ApplicationErrorReport implements Parcelable {
* of BatteryInfo; otherwise null. * of BatteryInfo; otherwise null.
*/ */
public BatteryInfo batteryInfo; public BatteryInfo batteryInfo;
/** /**
* If this report is of type {@link #TYPE_RUNNING_SERVICE}, contains an instance * If this report is of type {@link #TYPE_RUNNING_SERVICE}, contains an instance
* of RunningServiceInfo; otherwise null. * of RunningServiceInfo; otherwise null.
@ -278,10 +276,6 @@ public class ApplicationErrorReport implements Parcelable {
/** /**
* Describes an application crash. * 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 { public static class CrashInfo {
/** /**
@ -319,12 +313,6 @@ public class ApplicationErrorReport implements Parcelable {
*/ */
public String stackTrace; 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. * Create an uninitialized instance of CrashInfo.
*/ */
@ -368,7 +356,6 @@ public class ApplicationErrorReport implements Parcelable {
throwMethodName = in.readString(); throwMethodName = in.readString();
throwLineNumber = in.readInt(); throwLineNumber = in.readInt();
stackTrace = in.readString(); stackTrace = in.readString();
durationMillis = in.readLong();
} }
/** /**
@ -382,7 +369,6 @@ public class ApplicationErrorReport implements Parcelable {
dest.writeString(throwMethodName); dest.writeString(throwMethodName);
dest.writeInt(throwLineNumber); dest.writeInt(throwLineNumber);
dest.writeString(stackTrace); dest.writeString(stackTrace);
dest.writeLong(durationMillis);
} }
/** /**
@ -396,9 +382,6 @@ public class ApplicationErrorReport implements Parcelable {
pw.println(prefix + "throwMethodName: " + throwMethodName); pw.println(prefix + "throwMethodName: " + throwMethodName);
pw.println(prefix + "throwLineNumber: " + throwLineNumber); pw.println(prefix + "throwLineNumber: " + throwLineNumber);
pw.println(prefix + "stackTrace: " + stackTrace); pw.println(prefix + "stackTrace: " + stackTrace);
if (durationMillis != -1) {
pw.println(prefix + "durationMillis: " + durationMillis);
}
} }
} }

View File

@ -19,10 +19,10 @@ package android.app;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.ContentProviderNative; import android.content.ContentProviderNative;
import android.content.IContentProvider; import android.content.IContentProvider;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.IIntentSender;
import android.content.IIntentReceiver;
import android.content.IntentSender; import android.content.IntentSender;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo; import android.content.pm.ConfigurationInfo;
@ -31,14 +31,15 @@ import android.content.pm.ProviderInfo;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle;
import android.os.Debug; import android.os.Debug;
import android.os.RemoteException;
import android.os.IBinder; import android.os.IBinder;
import android.os.IInterface; import android.os.IInterface;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.Bundle; import android.os.Parcelable;
import android.os.RemoteException;
import android.os.StrictMode;
import java.util.List; import java.util.List;
@ -260,7 +261,7 @@ public interface IActivityManager extends IInterface {
// bit violated and penalty bits to be executed by the // bit violated and penalty bits to be executed by the
// ActivityManagerService remaining set. // ActivityManagerService remaining set.
public void handleApplicationStrictModeViolation(IBinder app, int violationMask, 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 * This will deliver the specified signal to all the persistent processes. Currently only

View File

@ -18,6 +18,7 @@ package android.os;
import android.app.ActivityManagerNative; import android.app.ActivityManagerNative;
import android.app.ApplicationErrorReport; import android.app.ApplicationErrorReport;
import android.util.Log; import android.util.Log;
import android.util.Printer;
import com.android.internal.os.RuntimeInit; import com.android.internal.os.RuntimeInit;
@ -97,9 +98,9 @@ public final class StrictMode {
* via Parcel.writeNoException() (amusingly) where the caller can * via Parcel.writeNoException() (amusingly) where the caller can
* choose how to react. * choose how to react.
*/ */
private static final ThreadLocal<ArrayList<ApplicationErrorReport.CrashInfo>> gatheredViolations = private static final ThreadLocal<ArrayList<ViolationInfo>> gatheredViolations =
new ThreadLocal<ArrayList<ApplicationErrorReport.CrashInfo>>() { new ThreadLocal<ArrayList<ViolationInfo>>() {
@Override protected ArrayList<ApplicationErrorReport.CrashInfo> initialValue() { @Override protected ArrayList<ViolationInfo> initialValue() {
// Starts null to avoid unnecessary allocations when // Starts null to avoid unnecessary allocations when
// checking whether there are any violations or not in // checking whether there are any violations or not in
// hasGatheredViolations() below. // hasGatheredViolations() below.
@ -240,7 +241,9 @@ public final class StrictMode {
if ((mPolicyMask & DISALLOW_DISK_WRITE) == 0) { if ((mPolicyMask & DISALLOW_DISK_WRITE) == 0) {
return; return;
} }
startHandlingViolationException(new StrictModeDiskWriteViolation(mPolicyMask)); BlockGuard.BlockGuardPolicyException e = new StrictModeDiskWriteViolation(mPolicyMask);
e.fillInStackTrace();
startHandlingViolationException(e);
} }
// Part of BlockGuard.Policy interface: // Part of BlockGuard.Policy interface:
@ -248,7 +251,9 @@ public final class StrictMode {
if ((mPolicyMask & DISALLOW_DISK_READ) == 0) { if ((mPolicyMask & DISALLOW_DISK_READ) == 0) {
return; return;
} }
startHandlingViolationException(new StrictModeDiskReadViolation(mPolicyMask)); BlockGuard.BlockGuardPolicyException e = new StrictModeDiskReadViolation(mPolicyMask);
e.fillInStackTrace();
startHandlingViolationException(e);
} }
// Part of BlockGuard.Policy interface: // Part of BlockGuard.Policy interface:
@ -256,7 +261,9 @@ public final class StrictMode {
if ((mPolicyMask & DISALLOW_NETWORK) == 0) { if ((mPolicyMask & DISALLOW_NETWORK) == 0) {
return; return;
} }
startHandlingViolationException(new StrictModeNetworkViolation(mPolicyMask)); BlockGuard.BlockGuardPolicyException e = new StrictModeNetworkViolation(mPolicyMask);
e.fillInStackTrace();
startHandlingViolationException(e);
} }
public void setPolicyMask(int policyMask) { 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 // thread and, if so, uses it to roughly measure how long the
// violation took. // violation took.
void startHandlingViolationException(BlockGuard.BlockGuardPolicyException e) { void startHandlingViolationException(BlockGuard.BlockGuardPolicyException e) {
e.fillInStackTrace(); final ViolationInfo info = new ViolationInfo(e, e.getPolicy());
final ApplicationErrorReport.CrashInfo crashInfo = new ApplicationErrorReport.CrashInfo(e); info.violationUptimeMillis = SystemClock.uptimeMillis();
crashInfo.durationMillis = -1; // unknown handleViolationWithTimingAttempt(info);
final int savedPolicy = mPolicyMask; }
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(); 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) { if (looper == null) {
// Without a Looper, we're unable to time how long the info.durationMillis = -1; // unknown (redundant, already set)
// violation takes place. This case should be rare, handleViolation(info);
// as most users will care about timing violations return;
// 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
}
});
} }
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 // 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 // violation fired and now (after the violating code ran) due
// to people who push/pop temporary policy in regions of code, // to people who push/pop temporary policy in regions of code,
// hence the policy being passed around. // hence the policy being passed around.
void handleViolation( void handleViolation(final ViolationInfo info) {
final ApplicationErrorReport.CrashInfo crashInfo, if (info == null || info.crashInfo == null || info.crashInfo.stackTrace == null) {
int policy) { Log.wtf(TAG, "unexpected null stacktrace");
if (crashInfo.stackTrace == null) {
Log.d(TAG, "unexpected null stacktrace");
return; 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) { if ((info.policy & PENALTY_GATHER) != 0) {
ArrayList<ApplicationErrorReport.CrashInfo> violations = gatheredViolations.get(); ArrayList<ViolationInfo> violations = gatheredViolations.get();
if (violations == null) { if (violations == null) {
violations = new ArrayList<ApplicationErrorReport.CrashInfo>(1); violations = new ArrayList<ViolationInfo>(1);
gatheredViolations.set(violations); gatheredViolations.set(violations);
} else if (violations.size() >= 5) { } else if (violations.size() >= 5) {
// Too many. In a loop or something? Don't gather them all. // Too many. In a loop or something? Don't gather them all.
return; return;
} }
for (ApplicationErrorReport.CrashInfo previous : violations) { for (ViolationInfo previous : violations) {
if (crashInfo.stackTrace.equals(previous.stackTrace)) { if (info.crashInfo.stackTrace.equals(previous.crashInfo.stackTrace)) {
// Duplicate. Don't log. // Duplicate. Don't log.
return; return;
} }
} }
violations.add(crashInfo); violations.add(info);
return; return;
} }
// Not perfect, but fast and good enough for dup suppression. // Not perfect, but fast and good enough for dup suppression.
Integer crashFingerprint = crashInfo.stackTrace.hashCode(); Integer crashFingerprint = info.crashInfo.stackTrace.hashCode();
long lastViolationTime = 0; long lastViolationTime = 0;
if (mLastViolationTime.containsKey(crashFingerprint)) { if (mLastViolationTime.containsKey(crashFingerprint)) {
lastViolationTime = mLastViolationTime.get(crashFingerprint); lastViolationTime = mLastViolationTime.get(crashFingerprint);
@ -341,13 +385,13 @@ public final class StrictMode {
long timeSinceLastViolationMillis = lastViolationTime == 0 ? long timeSinceLastViolationMillis = lastViolationTime == 0 ?
Long.MAX_VALUE : (now - lastViolationTime); Long.MAX_VALUE : (now - lastViolationTime);
if ((policy & PENALTY_LOG) != 0 && if ((info.policy & PENALTY_LOG) != 0 &&
timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) { timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) {
if (crashInfo.durationMillis != -1) { if (info.durationMillis != -1) {
Log.d(TAG, "StrictMode policy violation; ~duration=" + Log.d(TAG, "StrictMode policy violation; ~duration=" +
crashInfo.durationMillis + " ms: " + crashInfo.stackTrace); info.durationMillis + " ms: " + info.crashInfo.stackTrace);
} else { } 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 // subset of the original StrictMode policy bitmask, with
// only the bit violated and penalty bits to be executed // only the bit violated and penalty bits to be executed
// by the ActivityManagerService remaining set. // 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) { timeSinceLastViolationMillis > MIN_DIALOG_INTERVAL_MS) {
violationMask |= PENALTY_DIALOG; violationMaskSubset |= PENALTY_DIALOG;
} }
if ((policy & PENALTY_DROPBOX) != 0 && lastViolationTime == 0) { if ((info.policy & PENALTY_DROPBOX) != 0 && lastViolationTime == 0) {
violationMask |= PENALTY_DROPBOX; violationMaskSubset |= PENALTY_DROPBOX;
} }
if (violationMask != 0) { if (violationMaskSubset != 0) {
int violationBit = parseViolationFromMessage(crashInfo.exceptionMessage); int violationBit = parseViolationFromMessage(info.crashInfo.exceptionMessage);
violationMask |= violationBit; violationMaskSubset |= violationBit;
final int savedPolicy = getThreadBlockingPolicy(); final int savedPolicy = getThreadBlockingPolicy();
try { try {
// First, remove any policy before we call into the Activity Manager, // First, remove any policy before we call into the Activity Manager,
@ -379,8 +423,8 @@ public final class StrictMode {
ActivityManagerNative.getDefault().handleApplicationStrictModeViolation( ActivityManagerNative.getDefault().handleApplicationStrictModeViolation(
RuntimeInit.getApplicationObject(), RuntimeInit.getApplicationObject(),
violationMask, violationMaskSubset,
crashInfo); info);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "RemoteException trying to handle StrictMode violation", e); Log.e(TAG, "RemoteException trying to handle StrictMode violation", e);
} finally { } 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."); System.err.println("StrictMode policy violation with POLICY_DEATH; shutting down.");
Process.killProcess(Process.myPid()); Process.killProcess(Process.myPid());
System.exit(10); System.exit(10);
@ -417,7 +461,7 @@ public final class StrictMode {
* Called from Parcel.writeNoException() * Called from Parcel.writeNoException()
*/ */
/* package */ static void writeGatheredViolationsToParcel(Parcel p) { /* package */ static void writeGatheredViolationsToParcel(Parcel p) {
ArrayList<ApplicationErrorReport.CrashInfo> violations = gatheredViolations.get(); ArrayList<ViolationInfo> violations = gatheredViolations.get();
if (violations == null) { if (violations == null) {
p.writeInt(0); p.writeInt(0);
} else { } else {
@ -439,35 +483,21 @@ public final class StrictMode {
*/ */
/* package */ static void readAndHandleBinderCallViolations(Parcel p) { /* package */ static void readAndHandleBinderCallViolations(Parcel p) {
// Our own stack trace to append // Our own stack trace to append
Exception e = new LogStackTrace();
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw)); new LogStackTrace().printStackTrace(new PrintWriter(sw));
String ourStack = sw.toString(); String ourStack = sw.toString();
int policyMask = getThreadBlockingPolicy(); int policyMask = getThreadBlockingPolicy();
boolean currentlyGathering = (policyMask & PENALTY_GATHER) != 0;
int numViolations = p.readInt(); int numViolations = p.readInt();
for (int i = 0; i < numViolations; ++i) { for (int i = 0; i < numViolations; ++i) {
if (LOG_V) Log.d(TAG, "strict mode violation stacks read from binder call. i=" + i); if (LOG_V) Log.d(TAG, "strict mode violation stacks read from binder call. i=" + i);
ApplicationErrorReport.CrashInfo crashInfo = new ApplicationErrorReport.CrashInfo(p); ViolationInfo info = new ViolationInfo(p, !currentlyGathering);
crashInfo.stackTrace += "# via Binder call with stack:\n" + ourStack; info.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;
BlockGuard.Policy policy = BlockGuard.getThreadPolicy(); BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (policy instanceof AndroidBlockGuardPolicy) { 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) { private static void onBinderStrictModePolicyChange(int newPolicy) {
setBlockGuardPolicy(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);
}
}
} }

View File

@ -6125,17 +6125,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} }
public void handleApplicationStrictModeViolation( public void handleApplicationStrictModeViolation(
IBinder app, int violationMask, ApplicationErrorReport.CrashInfo crashInfo) { IBinder app,
int violationMask,
StrictMode.ViolationInfo info) {
ProcessRecord r = findAppProcess(app); ProcessRecord r = findAppProcess(app);
if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) { if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) {
Integer stackFingerprint = crashInfo.stackTrace.hashCode(); Integer stackFingerprint = info.crashInfo.stackTrace.hashCode();
boolean logIt = true; boolean logIt = true;
synchronized (mAlreadyLoggedViolatedStacks) { synchronized (mAlreadyLoggedViolatedStacks) {
if (mAlreadyLoggedViolatedStacks.contains(stackFingerprint)) { if (mAlreadyLoggedViolatedStacks.contains(stackFingerprint)) {
logIt = false; logIt = false;
// TODO: sub-sample into EventLog for these, with // 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 relative pain numbers, without logging all
// the stack traces repeatedly. We'd want to do // the stack traces repeatedly. We'd want to do
// likewise in the client code, which also does // likewise in the client code, which also does
@ -6148,7 +6150,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} }
} }
if (logIt) { 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("result", result);
data.put("app", r); data.put("app", r);
data.put("violationMask", violationMask); data.put("violationMask", violationMask);
data.put("crashInfo", crashInfo); data.put("info", info);
msg.obj = data; msg.obj = data;
mHandler.sendMessage(msg); 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 // these in quick succession so we try to batch these together to
// minimize disk writes, number of dropbox entries, and maximize // minimize disk writes, number of dropbox entries, and maximize
// compression, by having more fewer, larger records. // compression, by having more fewer, larger records.
private void logStrictModeViolationToDropBox(ProcessRecord process, private void logStrictModeViolationToDropBox(
ApplicationErrorReport.CrashInfo crashInfo) { ProcessRecord process,
if (crashInfo == null) { StrictMode.ViolationInfo info) {
if (info == null) {
return; return;
} }
final boolean isSystemApp = process == null || final boolean isSystemApp = process == null ||
@ -6201,12 +6204,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
appendDropBoxProcessHeaders(process, sb); appendDropBoxProcessHeaders(process, sb);
sb.append("Build: ").append(Build.FINGERPRINT).append("\n"); sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
sb.append("System-App: ").append(isSystemApp).append("\n"); sb.append("System-App: ").append(isSystemApp).append("\n");
if (crashInfo != null && crashInfo.durationMillis != -1) { sb.append("Uptime-Millis: ").append(info.violationUptimeMillis).append("\n");
sb.append("Duration-Millis: ").append(crashInfo.durationMillis).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"); sb.append("\n");
if (crashInfo != null && crashInfo.stackTrace != null) { if (info.crashInfo != null && info.crashInfo.stackTrace != null) {
sb.append(crashInfo.stackTrace); sb.append(info.crashInfo.stackTrace);
} }
sb.append("\n"); sb.append("\n");