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:
@ -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;
|
||||
}
|
||||
@ -2571,14 +2572,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();
|
||||
|
@ -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;
|
||||
|
||||
@ -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 {
|
||||
/**
|
||||
@ -319,12 +313,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.
|
||||
*/
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
||||
|
Reference in New Issue
Block a user