Loading core/java/android/app/ActivityManagerNative.java +5 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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(); Loading core/java/android/app/ApplicationErrorReport.java +6 −23 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 { /** Loading Loading @@ -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. */ Loading Loading @@ -368,7 +356,6 @@ public class ApplicationErrorReport implements Parcelable { throwMethodName = in.readString(); throwLineNumber = in.readInt(); stackTrace = in.readString(); durationMillis = in.readLong(); } /** Loading @@ -382,7 +369,6 @@ public class ApplicationErrorReport implements Parcelable { dest.writeString(throwMethodName); dest.writeInt(throwLineNumber); dest.writeString(stackTrace); dest.writeLong(durationMillis); } /** Loading @@ -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); } } } Loading core/java/android/app/IActivityManager.java +7 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 Loading core/java/android/os/StrictMode.java +213 −74 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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: Loading @@ -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: Loading @@ -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) { Loading @@ -269,69 +276,106 @@ 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(); 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 { // 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) { info.durationMillis = -1; // unknown (redundant, already set) handleViolation(info); return; } MessageQueue queue = Looper.myQueue(); final long violationTime = SystemClock.uptimeMillis(); 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 afterViolationTime = SystemClock.uptimeMillis(); crashInfo.durationMillis = afterViolationTime - violationTime; handleViolation(crashInfo, savedPolicy); 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 // thread-local policy mask has changed from the time the // 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); Loading @@ -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); } } Loading @@ -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, Loading @@ -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 { Loading @@ -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); Loading Loading @@ -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 { Loading @@ -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); } } } Loading @@ -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); } } } services/java/com/android/server/am/ActivityManagerService.java +19 −12 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/app/ActivityManagerNative.java +5 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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(); Loading
core/java/android/app/ApplicationErrorReport.java +6 −23 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 { /** Loading Loading @@ -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. */ Loading Loading @@ -368,7 +356,6 @@ public class ApplicationErrorReport implements Parcelable { throwMethodName = in.readString(); throwLineNumber = in.readInt(); stackTrace = in.readString(); durationMillis = in.readLong(); } /** Loading @@ -382,7 +369,6 @@ public class ApplicationErrorReport implements Parcelable { dest.writeString(throwMethodName); dest.writeInt(throwLineNumber); dest.writeString(stackTrace); dest.writeLong(durationMillis); } /** Loading @@ -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); } } } Loading
core/java/android/app/IActivityManager.java +7 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 Loading
core/java/android/os/StrictMode.java +213 −74 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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: Loading @@ -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: Loading @@ -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) { Loading @@ -269,69 +276,106 @@ 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(); 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 { // 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) { info.durationMillis = -1; // unknown (redundant, already set) handleViolation(info); return; } MessageQueue queue = Looper.myQueue(); final long violationTime = SystemClock.uptimeMillis(); 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 afterViolationTime = SystemClock.uptimeMillis(); crashInfo.durationMillis = afterViolationTime - violationTime; handleViolation(crashInfo, savedPolicy); 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 // thread-local policy mask has changed from the time the // 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); Loading @@ -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); } } Loading @@ -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, Loading @@ -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 { Loading @@ -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); Loading Loading @@ -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 { Loading @@ -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); } } } Loading @@ -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); } } }
services/java/com/android/server/am/ActivityManagerService.java +19 −12 File changed.Preview size limit exceeded, changes collapsed. Show changes