/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 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; import dalvik.system.BlockGuard; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; /** *
StrictMode is a developer tool which lets you impose stricter * rules under which your application runs. * *
StrictMode is most commonly used to catch accidental disk or * network access on the application's main thread, where UI * operations are received and animations take place. Keeping disk * and network operations off the main thread makes for much smoother, * more responsive applications. * *
Note that even though an Android device's disk is * often on flash memory, many devices run a filesystem on top of that * memory with very limited concurrency. It's often the case that * almost all disk accesses are fast, but may in individual cases be * dramatically slower when certain I/O is happening in the background * from other processes. If possible, it's best to assume that such * things are not fast.
* *Example code to enable from early in your * {@link android.app.Application}, {@link android.app.Activity}, or * other application component's * {@link android.app.Application#onCreate} method: * *
* public void onCreate() { * if (DEVELOPER_MODE) { * StrictMode.setThreadPolicy(StrictMode.DISALLOW_DISK_WRITE | * StrictMode.DISALLOW_DISK_READ | * StrictMode.DISALLOW_NETWORK | * StrictMode.PENALTY_LOG); * } * super.onCreate(); * } ** *
Then you can watch the output of adb logcat
while you
* use your application.
*
*
If you find violations that you feel are problematic, there are * a variety of tools to help solve them: threads, {@link android.os.Handler}, * {@link android.os.AsyncTask}, {@link android.app.IntentService}, etc. * But don't feel compelled to fix everything that StrictMode finds. In particular, * a lot of disk accesses are often necessary during the normal activity lifecycle. Use * StrictMode to find things you did on accident. Network requests on the UI thread * are almost always a problem, though. * *
StrictMode is not a security mechanism and is not
* guaranteed to find all disk or network accesses. While it does
* propagate its state across process boundaries when doing
* {@link android.os.Binder} calls, it's still ultimately a best
* effort mechanism. Notably, disk or network access from JNI calls
* won't necessarily trigger it. Future versions of Android may catch
* more (or fewer) operations, so you should never leave StrictMode
* enabled in shipping applications on the Android Market.
*/
public final class StrictMode {
private static final String TAG = "StrictMode";
private static final boolean LOG_V = false;
// Only log a duplicate stack trace to the logs every second.
private static final long MIN_LOG_INTERVAL_MS = 1000;
// Only show an annoying dialog at most every 30 seconds
private static final long MIN_DIALOG_INTERVAL_MS = 30000;
private StrictMode() {}
/**
* Flag for {@link #setThreadPolicy} to signal that you don't intend for this
* thread to write to disk.
*/
public static final int DISALLOW_DISK_WRITE = 0x01;
/**
* Flag for {@link #setThreadPolicy} to signal that you don't intend for this
* thread to read from disk.
*/
public static final int DISALLOW_DISK_READ = 0x02;
/**
* Flag for {@link #setThreadPolicy} to signal that you don't intend for this
* thread to access the network.
*/
public static final int DISALLOW_NETWORK = 0x04;
/** @hide */
public static final int DISALLOW_MASK =
DISALLOW_DISK_WRITE | DISALLOW_DISK_READ | DISALLOW_NETWORK;
/**
* Penalty flag for {@link #setThreadPolicy} to log violations to
* the system log, visible with Internally this sets a thread-local integer which is
* propagated across cross-process IPC calls, meaning you can
* catch violations when a system service or another process
* accesses the disk or network on your behalf.
*
* @param policyMask a bitmask of DISALLOW_* and PENALTY_* values,
* e.g. {@link #DISALLOW_DISK_READ}, {@link #DISALLOW_DISK_WRITE},
* {@link #DISALLOW_NETWORK}, {@link #PENALTY_LOG}.
*/
public static void setThreadPolicy(final int policyMask) {
// In addition to the Java-level thread-local in Dalvik's
// BlockGuard, we also need to keep a native thread-local in
// Binder in order to propagate the value across Binder calls,
// even across native-only processes. The two are kept in
// sync via the callback to onStrictModePolicyChange, below.
setBlockGuardPolicy(policyMask);
// And set the Android native version...
Binder.setThreadStrictModePolicy(policyMask);
}
// Sets the policy in Dalvik/libcore (BlockGuard)
private static void setBlockGuardPolicy(final int policyMask) {
if (policyMask == 0) {
BlockGuard.setThreadPolicy(BlockGuard.LAX_POLICY);
return;
}
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (!(policy instanceof AndroidBlockGuardPolicy)) {
BlockGuard.setThreadPolicy(new AndroidBlockGuardPolicy(policyMask));
} else {
AndroidBlockGuardPolicy androidPolicy = (AndroidBlockGuardPolicy) policy;
androidPolicy.setPolicyMask(policyMask);
}
}
private static class StrictModeNetworkViolation extends BlockGuard.BlockGuardPolicyException {
public StrictModeNetworkViolation(int policyMask) {
super(policyMask, DISALLOW_NETWORK);
}
}
private static class StrictModeDiskReadViolation extends BlockGuard.BlockGuardPolicyException {
public StrictModeDiskReadViolation(int policyMask) {
super(policyMask, DISALLOW_DISK_READ);
}
}
private static class StrictModeDiskWriteViolation extends BlockGuard.BlockGuardPolicyException {
public StrictModeDiskWriteViolation(int policyMask) {
super(policyMask, DISALLOW_DISK_WRITE);
}
}
/**
* Returns the bitmask of the current thread's policy.
*
* @return the bitmask of all the DISALLOW_* and PENALTY_* bits currently enabled
*/
public static int getThreadPolicy() {
return BlockGuard.getThreadPolicy().getPolicyMask();
}
/**
* A convenience wrapper around {@link #getThreadPolicy} and
* {@link #setThreadPolicy}. Updates the current thread's policy
* mask to allow both reading & writing to disk, returning the
* old policy so you can restore it at the end of a block.
*
* @return the old policy mask, to be passed to setThreadPolicy to
* restore the policy.
*/
public static int allowThreadDiskWrites() {
int oldPolicy = getThreadPolicy();
int newPolicy = oldPolicy & ~(DISALLOW_DISK_WRITE | DISALLOW_DISK_READ);
if (newPolicy != oldPolicy) {
setThreadPolicy(newPolicy);
}
return oldPolicy;
}
/**
* A convenience wrapper around {@link #getThreadPolicy} and
* {@link #setThreadPolicy}. Updates the current thread's policy
* mask to allow reading from disk, returning the old
* policy so you can restore it at the end of a block.
*
* @return the old policy mask, to be passed to setThreadPolicy to
* restore the policy.
*/
public static int allowThreadDiskReads() {
int oldPolicy = getThreadPolicy();
int newPolicy = oldPolicy & ~(DISALLOW_DISK_READ);
if (newPolicy != oldPolicy) {
setThreadPolicy(newPolicy);
}
return oldPolicy;
}
/**
* Enable DropBox logging for debug phone builds.
*
* @hide
*/
public static boolean conditionallyEnableDebugLogging() {
// For debug builds, log event loop stalls to dropbox for analysis.
// Similar logic also appears in ActivityThread.java for system apps.
if ("user".equals(Build.TYPE)) {
return false;
}
StrictMode.setThreadPolicy(
StrictMode.DISALLOW_DISK_WRITE |
StrictMode.DISALLOW_DISK_READ |
StrictMode.DISALLOW_NETWORK |
StrictMode.PENALTY_DROPBOX);
return true;
}
/**
* Parses the BlockGuard policy mask out from the Exception's
* getMessage() String value. Kinda gross, but least
* invasive. :/
*
* Input is of form "policy=137 violation=64"
*
* Returns 0 on failure, which is a valid policy, but not a
* valid policy during a violation (else there must've been
* some policy in effect to violate).
*/
private static int parsePolicyFromMessage(String message) {
if (message == null || !message.startsWith("policy=")) {
return 0;
}
int spaceIndex = message.indexOf(' ');
if (spaceIndex == -1) {
return 0;
}
String policyString = message.substring(7, spaceIndex);
try {
return Integer.valueOf(policyString).intValue();
} catch (NumberFormatException e) {
return 0;
}
}
/**
* Like parsePolicyFromMessage(), but returns the violation.
*/
private static int parseViolationFromMessage(String message) {
if (message == null) {
return 0;
}
int violationIndex = message.indexOf("violation=");
if (violationIndex == -1) {
return 0;
}
String violationString = message.substring(violationIndex + 10);
try {
return Integer.valueOf(violationString).intValue();
} catch (NumberFormatException e) {
return 0;
}
}
private static class AndroidBlockGuardPolicy implements BlockGuard.Policy {
private int mPolicyMask;
// Map from violation stacktrace hashcode -> uptimeMillis of
// last violation. No locking needed, as this is only
// accessed by the same thread.
private final HashMapadb logcat
.
*/
public static final int PENALTY_LOG = 0x10; // normal android.util.Log
/**
* Penalty flag for {@link #setThreadPolicy} to show an annoying
* dialog to the developer, rate-limited to be only a little
* annoying.
*/
public static final int PENALTY_DIALOG = 0x20;
/**
* Penalty flag for {@link #setThreadPolicy} to crash hard if
* policy is violated.
*/
public static final int PENALTY_DEATH = 0x40;
/**
* Penalty flag for {@link #setThreadPolicy} to log a stacktrace
* and timing data to the
* {@link android.os.DropBoxManager DropBox} on policy violation.
* Intended mostly for platform integrators doing beta user field
* data collection.
*/
public static final int PENALTY_DROPBOX = 0x80;
/**
* Non-public penalty mode which overrides all the other penalty
* bits and signals that we're in a Binder call and we should
* ignore the other penalty bits and instead serialize back all
* our offending stack traces to the caller to ultimately handle
* in the originating process.
*
* This must be kept in sync with the constant in libs/binder/Parcel.cpp
*
* @hide
*/
public static final int PENALTY_GATHER = 0x100;
/** @hide */
public static final int PENALTY_MASK =
PENALTY_LOG | PENALTY_DIALOG |
PENALTY_DROPBOX | PENALTY_DEATH;
/**
* Log of strict mode violation stack traces that have occurred
* during a Binder call, to be serialized back later to the caller
* via Parcel.writeNoException() (amusingly) where the caller can
* choose how to react.
*/
private static final ThreadLocal