/* * 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.animation.ValueAnimator; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.IActivityManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.net.TrafficStats; import android.net.Uri; import android.os.storage.IStorageManager; import android.os.strictmode.CleartextNetworkViolation; import android.os.strictmode.ContentUriWithoutPermissionViolation; import android.os.strictmode.CredentialProtectedWhileLockedViolation; import android.os.strictmode.CustomViolation; import android.os.strictmode.DiskReadViolation; import android.os.strictmode.DiskWriteViolation; import android.os.strictmode.ExplicitGcViolation; import android.os.strictmode.FileUriExposedViolation; import android.os.strictmode.ImplicitDirectBootViolation; import android.os.strictmode.IncorrectContextUseViolation; import android.os.strictmode.InstanceCountViolation; import android.os.strictmode.IntentReceiverLeakedViolation; import android.os.strictmode.LeakedClosableViolation; import android.os.strictmode.NetworkViolation; import android.os.strictmode.NonSdkApiUsedViolation; import android.os.strictmode.ResourceMismatchViolation; import android.os.strictmode.ServiceConnectionLeakedViolation; import android.os.strictmode.SqliteObjectLeakedViolation; import android.os.strictmode.UnbufferedIoViolation; import android.os.strictmode.UntaggedSocketViolation; import android.os.strictmode.Violation; import android.os.strictmode.WebViewMethodCalledOnWrongThreadViolation; import android.util.ArrayMap; import android.util.Log; import android.util.Printer; import android.util.Singleton; import android.util.Slog; import android.view.IWindowManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; import com.android.internal.os.RuntimeInit; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.HexDump; import dalvik.system.BlockGuard; import dalvik.system.CloseGuard; import dalvik.system.VMDebug; import dalvik.system.VMRuntime; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Deque; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; /** * StrictMode is a developer tool which detects things you might be doing by accident and brings * them to your attention so you can fix them. * *
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. By keeping your application's main thread responsive, you also prevent ANR dialogs from being shown to * users. * *
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(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}() * .detectDiskReads() * .detectDiskWrites() * .detectNetwork() // or .detectAll() for all detectable problems * .penaltyLog() * .build()); * StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}() * .detectLeakedSqlLiteObjects() * .detectLeakedClosableObjects() * .penaltyLog() * .penaltyDeath() * .build()); * } * super.onCreate(); * } ** *
You can decide what should happen when a violation is detected. For example, using {@link
* ThreadPolicy.Builder#penaltyLog} you can watch the output of adb logcat
while you
* use your application to see the violations as they happen.
*
*
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, many cases of disk access are often necessary during the normal activity * lifecycle. Use StrictMode to find things you did by 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 applications distributed on * Google Play. */ public final class StrictMode { private static final String TAG = "StrictMode"; private static final boolean LOG_V = Log.isLoggable(TAG, Log.VERBOSE); /** * Boolean system property to disable strict mode checks outright. Set this to 'true' to force * disable; 'false' has no effect on other enable/disable policy. * * @hide */ public static final String DISABLE_PROPERTY = "persist.sys.strictmode.disable"; /** * The boolean system property to control screen flashes on violations. * * @hide */ public static final String VISUAL_PROPERTY = "persist.sys.strictmode.visual"; /** * Temporary property used to include {@link #DETECT_VM_CLEARTEXT_NETWORK} in {@link * VmPolicy.Builder#detectAll()}. Apps can still always opt-into detection using {@link * VmPolicy.Builder#detectCleartextNetwork()}. */ private static final String CLEARTEXT_PROPERTY = "persist.sys.strictmode.clear"; /** * Quick feature-flag that can be used to disable the defaults provided by {@link * #initThreadDefaults(ApplicationInfo)} and {@link #initVmDefaults(ApplicationInfo)}. */ private static final boolean DISABLE = false; // Only apply VM penalties for the same violation at this interval. private static final long MIN_VM_INTERVAL_MS = 1000; // 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; // Only log a dropbox entry at most every 30 seconds private static final long MIN_DROPBOX_INTERVAL_MS = 3000; // How many Span tags (e.g. animations) to report. private static final int MAX_SPAN_TAGS = 20; // How many offending stacks to keep track of (and time) per loop // of the Looper. private static final int MAX_OFFENSES_PER_LOOP = 10; /** @hide */ @IntDef(flag = true, prefix = { "DETECT_THREAD_", "PENALTY_" }, value = { DETECT_THREAD_DISK_WRITE, DETECT_THREAD_DISK_READ, DETECT_THREAD_NETWORK, DETECT_THREAD_CUSTOM, DETECT_THREAD_RESOURCE_MISMATCH, DETECT_THREAD_UNBUFFERED_IO, DETECT_THREAD_EXPLICIT_GC, PENALTY_GATHER, PENALTY_LOG, PENALTY_DIALOG, PENALTY_DEATH, PENALTY_FLASH, PENALTY_DROPBOX, PENALTY_DEATH_ON_NETWORK, PENALTY_DEATH_ON_CLEARTEXT_NETWORK, PENALTY_DEATH_ON_FILE_URI_EXPOSURE, }) @Retention(RetentionPolicy.SOURCE) public @interface ThreadPolicyMask {} // Thread policy: bits 0-15 /** @hide */ private static final int DETECT_THREAD_DISK_WRITE = 1 << 0; /** @hide */ private static final int DETECT_THREAD_DISK_READ = 1 << 1; /** @hide */ private static final int DETECT_THREAD_NETWORK = 1 << 2; /** @hide */ private static final int DETECT_THREAD_CUSTOM = 1 << 3; /** @hide */ private static final int DETECT_THREAD_RESOURCE_MISMATCH = 1 << 4; /** @hide */ private static final int DETECT_THREAD_UNBUFFERED_IO = 1 << 5; /** @hide */ private static final int DETECT_THREAD_EXPLICIT_GC = 1 << 6; /** @hide */ private static final int DETECT_THREAD_ALL = 0x0000ffff; /** @hide */ @IntDef(flag = true, prefix = { "DETECT_THREAD_", "PENALTY_" }, value = { DETECT_VM_CURSOR_LEAKS, DETECT_VM_CLOSABLE_LEAKS, DETECT_VM_ACTIVITY_LEAKS, DETECT_VM_INSTANCE_LEAKS, DETECT_VM_REGISTRATION_LEAKS, DETECT_VM_FILE_URI_EXPOSURE, DETECT_VM_CLEARTEXT_NETWORK, DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION, DETECT_VM_UNTAGGED_SOCKET, DETECT_VM_NON_SDK_API_USAGE, DETECT_VM_IMPLICIT_DIRECT_BOOT, DETECT_VM_INCORRECT_CONTEXT_USE, PENALTY_GATHER, PENALTY_LOG, PENALTY_DIALOG, PENALTY_DEATH, PENALTY_FLASH, PENALTY_DROPBOX, PENALTY_DEATH_ON_NETWORK, PENALTY_DEATH_ON_CLEARTEXT_NETWORK, PENALTY_DEATH_ON_FILE_URI_EXPOSURE, }) @Retention(RetentionPolicy.SOURCE) public @interface VmPolicyMask {} // VM policy: bits 0-15 /** @hide */ private static final int DETECT_VM_CURSOR_LEAKS = 1 << 0; /** @hide */ private static final int DETECT_VM_CLOSABLE_LEAKS = 1 << 1; /** @hide */ private static final int DETECT_VM_ACTIVITY_LEAKS = 1 << 2; /** @hide */ private static final int DETECT_VM_INSTANCE_LEAKS = 1 << 3; /** @hide */ private static final int DETECT_VM_REGISTRATION_LEAKS = 1 << 4; /** @hide */ private static final int DETECT_VM_FILE_URI_EXPOSURE = 1 << 5; /** @hide */ private static final int DETECT_VM_CLEARTEXT_NETWORK = 1 << 6; /** @hide */ private static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 1 << 7; /** @hide */ private static final int DETECT_VM_UNTAGGED_SOCKET = 1 << 8; /** @hide */ private static final int DETECT_VM_NON_SDK_API_USAGE = 1 << 9; /** @hide */ private static final int DETECT_VM_IMPLICIT_DIRECT_BOOT = 1 << 10; /** @hide */ private static final int DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED = 1 << 11; /** @hide */ private static final int DETECT_VM_INCORRECT_CONTEXT_USE = 1 << 12; /** @hide */ private static final int DETECT_VM_ALL = 0x0000ffff; // Penalty policy: bits 16-31 /** * 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 = 1 << 31;
/** {@hide} */
public static final int PENALTY_LOG = 1 << 30;
/** {@hide} */
public static final int PENALTY_DIALOG = 1 << 29;
/** {@hide} */
public static final int PENALTY_DEATH = 1 << 28;
/** {@hide} */
public static final int PENALTY_FLASH = 1 << 27;
/** {@hide} */
public static final int PENALTY_DROPBOX = 1 << 26;
/** {@hide} */
public static final int PENALTY_DEATH_ON_NETWORK = 1 << 25;
/** {@hide} */
public static final int PENALTY_DEATH_ON_CLEARTEXT_NETWORK = 1 << 24;
/** {@hide} */
public static final int PENALTY_DEATH_ON_FILE_URI_EXPOSURE = 1 << 23;
/** @hide */
public static final int PENALTY_ALL = 0xffff0000;
/** {@hide} */
public static final int NETWORK_POLICY_ACCEPT = 0;
/** {@hide} */
public static final int NETWORK_POLICY_LOG = 1;
/** {@hide} */
public static final int NETWORK_POLICY_REJECT = 2;
// TODO: wrap in some ImmutableHashMap thing.
// Note: must be before static initialization of sVmPolicy.
private static final HashMap The policy is enabled by {@link #setThreadPolicy}. The current policy can be retrieved
* with {@link #getThreadPolicy}.
*
* Note that multiple penalties may be provided and they're run in order from least to most
* severe (logging before process death, for example). There's currently no mechanism to choose
* different penalties for different detected actions.
*/
public static final class ThreadPolicy {
/** The default, lax policy which doesn't catch anything. */
public static final ThreadPolicy LAX = new ThreadPolicy(0, null, null);
@UnsupportedAppUsage
final @ThreadPolicyMask int mask;
final OnThreadViolationListener mListener;
final Executor mCallbackExecutor;
private ThreadPolicy(@ThreadPolicyMask int mask, OnThreadViolationListener listener,
Executor executor) {
this.mask = mask;
mListener = listener;
mCallbackExecutor = executor;
}
@Override
public String toString() {
return "[StrictMode.ThreadPolicy; mask=" + mask + "]";
}
/**
* Creates {@link ThreadPolicy} instances. Methods whose names start with {@code detect}
* specify what problems we should look for. Methods whose names start with {@code penalty}
* specify what we should do when we detect a problem.
*
* You can call as many {@code detect} and {@code penalty} methods as you like. Currently
* order is insignificant: all penalties apply to all detected problems.
*
* For example, detect everything and log anything that's found:
*
* As of the Gingerbread release this includes network and disk operations but will
* likely expand in future releases.
*/
public @NonNull Builder detectAll() {
detectDiskReads();
detectDiskWrites();
detectNetwork();
final int targetSdk = VMRuntime.getRuntime().getTargetSdkVersion();
if (targetSdk >= Build.VERSION_CODES.HONEYCOMB) {
detectCustomSlowCalls();
}
if (targetSdk >= Build.VERSION_CODES.M) {
detectResourceMismatches();
}
if (targetSdk >= Build.VERSION_CODES.O) {
detectUnbufferedIo();
}
return this;
}
/** Disable the detection of everything. */
public @NonNull Builder permitAll() {
return disable(DETECT_THREAD_ALL);
}
/** Enable detection of network operations. */
public @NonNull Builder detectNetwork() {
return enable(DETECT_THREAD_NETWORK);
}
/** Disable detection of network operations. */
public @NonNull Builder permitNetwork() {
return disable(DETECT_THREAD_NETWORK);
}
/** Enable detection of disk reads. */
public @NonNull Builder detectDiskReads() {
return enable(DETECT_THREAD_DISK_READ);
}
/** Disable detection of disk reads. */
public @NonNull Builder permitDiskReads() {
return disable(DETECT_THREAD_DISK_READ);
}
/** Enable detection of slow calls. */
public @NonNull Builder detectCustomSlowCalls() {
return enable(DETECT_THREAD_CUSTOM);
}
/** Disable detection of slow calls. */
public @NonNull Builder permitCustomSlowCalls() {
return disable(DETECT_THREAD_CUSTOM);
}
/** Disable detection of mismatches between defined resource types and getter calls. */
public @NonNull Builder permitResourceMismatches() {
return disable(DETECT_THREAD_RESOURCE_MISMATCH);
}
/** Detect unbuffered input/output operations. */
public @NonNull Builder detectUnbufferedIo() {
return enable(DETECT_THREAD_UNBUFFERED_IO);
}
/** Disable detection of unbuffered input/output operations. */
public @NonNull Builder permitUnbufferedIo() {
return disable(DETECT_THREAD_UNBUFFERED_IO);
}
/**
* Enables detection of mismatches between defined resource types and getter calls.
*
* This helps detect accidental type mismatches and potentially expensive type
* conversions when obtaining typed resources.
*
* For example, a strict mode violation would be thrown when calling {@link
* android.content.res.TypedArray#getInt(int, int)} on an index that contains a
* String-type resource. If the string value can be parsed as an integer, this method
* call will return a value without crashing; however, the developer should format the
* resource as an integer to avoid unnecessary type conversion.
*/
public @NonNull Builder detectResourceMismatches() {
return enable(DETECT_THREAD_RESOURCE_MISMATCH);
}
/** Enable detection of disk writes. */
public @NonNull Builder detectDiskWrites() {
return enable(DETECT_THREAD_DISK_WRITE);
}
/** Disable detection of disk writes. */
public @NonNull Builder permitDiskWrites() {
return disable(DETECT_THREAD_DISK_WRITE);
}
/**
* Detect explicit GC requests, i.e. calls to Runtime.gc().
*
* @hide
*/
@TestApi
public @NonNull Builder detectExplicitGc() {
// TODO(b/3400644): Un-hide this for next API update
// TODO(b/3400644): Un-hide ExplicitGcViolation for next API update
// TODO(b/3400644): Make DETECT_EXPLICIT_GC a @TestApi for next API update
// TODO(b/3400644): Call this from detectAll in next API update
return enable(DETECT_THREAD_EXPLICIT_GC);
}
/**
* Disable detection of explicit GC requests, i.e. calls to Runtime.gc().
*
* @hide
*/
public @NonNull Builder permitExplicitGc() {
// TODO(b/3400644): Un-hide this for next API update
return disable(DETECT_THREAD_EXPLICIT_GC);
}
/**
* Show an annoying dialog to the developer on detected violations, rate-limited to be
* only a little annoying.
*/
public @NonNull Builder penaltyDialog() {
return enable(PENALTY_DIALOG);
}
/**
* Crash the whole process on violation. This penalty runs at the end of all enabled
* penalties so you'll still get see logging or other violations before the process
* dies.
*
* Unlike {@link #penaltyDeathOnNetwork}, this applies to disk reads, disk writes,
* and network usage if their corresponding detect flags are set.
*/
public @NonNull Builder penaltyDeath() {
return enable(PENALTY_DEATH);
}
/**
* Crash the whole process on any network usage. Unlike {@link #penaltyDeath}, this
* penalty runs before anything else. You must still have called {@link
* #detectNetwork} to enable this.
*
* In the Honeycomb or later SDKs, this is on by default.
*/
public @NonNull Builder penaltyDeathOnNetwork() {
return enable(PENALTY_DEATH_ON_NETWORK);
}
/** Flash the screen during a violation. */
public @NonNull Builder penaltyFlashScreen() {
return enable(PENALTY_FLASH);
}
/** Log detected violations to the system log. */
public @NonNull Builder penaltyLog() {
return enable(PENALTY_LOG);
}
/**
* Enable detected violations 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 @NonNull Builder penaltyDropBox() {
return enable(PENALTY_DROPBOX);
}
/**
* Call #{@link OnThreadViolationListener#onThreadViolation(Violation)} on specified
* executor every violation.
*/
public @NonNull Builder penaltyListener(
@NonNull Executor executor, @NonNull OnThreadViolationListener listener) {
if (executor == null) {
throw new NullPointerException("executor must not be null");
}
mListener = listener;
mExecutor = executor;
return this;
}
/** @removed */
public @NonNull Builder penaltyListener(
@NonNull OnThreadViolationListener listener, @NonNull Executor executor) {
return penaltyListener(executor, listener);
}
private Builder enable(@ThreadPolicyMask int mask) {
mMask |= mask;
return this;
}
private Builder disable(@ThreadPolicyMask int mask) {
mMask &= ~mask;
return this;
}
/**
* Construct the ThreadPolicy instance.
*
* Note: if no penalties are enabled before calling The policy is enabled by {@link #setVmPolicy}.
*/
public static final class VmPolicy {
/** The default, lax policy which doesn't catch anything. */
public static final VmPolicy LAX = new VmPolicy(0, EMPTY_CLASS_LIMIT_MAP, null, null);
@UnsupportedAppUsage
final @VmPolicyMask int mask;
final OnVmViolationListener mListener;
final Executor mCallbackExecutor;
// Map from class to max number of allowed instances in memory.
final HashMap You can call as many {@code detect} and {@code penalty} methods as you like. Currently
* order is insignificant: all penalties apply to all detected problems.
*
* For example, detect everything and log anything that's found:
*
* Note that any non-SDK APIs that this processes accesses before this detection is
* enabled may not be detected. To ensure that all such API accesses are detected,
* you should apply this policy as early as possible after process creation.
*/
public @NonNull Builder detectNonSdkApiUsage() {
return enable(DETECT_VM_NON_SDK_API_USAGE);
}
/**
* Permit reflective usage of APIs that are not part of the public Android SDK. Note
* that this only affects {@code StrictMode}, the underlying runtime may
* continue to restrict or warn on access to methods that are not part of the
* public SDK.
*/
public @NonNull Builder permitNonSdkApiUsage() {
return disable(DETECT_VM_NON_SDK_API_USAGE);
}
/**
* Detect everything that's potentially suspect.
*
* In the Honeycomb release this includes leaks of SQLite cursors, Activities, and
* other closable objects but will likely expand in future releases.
*/
public @NonNull Builder detectAll() {
detectLeakedSqlLiteObjects();
final int targetSdk = VMRuntime.getRuntime().getTargetSdkVersion();
if (targetSdk >= Build.VERSION_CODES.HONEYCOMB) {
detectActivityLeaks();
detectLeakedClosableObjects();
}
if (targetSdk >= Build.VERSION_CODES.JELLY_BEAN) {
detectLeakedRegistrationObjects();
}
if (targetSdk >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
detectFileUriExposure();
}
if (targetSdk >= Build.VERSION_CODES.M) {
// TODO: always add DETECT_VM_CLEARTEXT_NETWORK once we have
// facility for apps to mark sockets that should be ignored
if (SystemProperties.getBoolean(CLEARTEXT_PROPERTY, false)) {
detectCleartextNetwork();
}
}
if (targetSdk >= Build.VERSION_CODES.O) {
detectContentUriWithoutPermission();
detectUntaggedSockets();
}
if (targetSdk >= Build.VERSION_CODES.Q) {
detectCredentialProtectedWhileLocked();
}
if (targetSdk >= Build.VERSION_CODES.R) {
detectIncorrectContextUse();
}
// TODO: Decide whether to detect non SDK API usage beyond a certain API level.
// TODO: enable detectImplicitDirectBoot() once system is less noisy
return this;
}
/**
* Detect when an {@link android.database.sqlite.SQLiteCursor} or other SQLite object is
* finalized without having been closed.
*
* You always want to explicitly close your SQLite cursors to avoid unnecessary
* database contention and temporary memory leaks.
*/
public @NonNull Builder detectLeakedSqlLiteObjects() {
return enable(DETECT_VM_CURSOR_LEAKS);
}
/**
* Detect when an {@link java.io.Closeable} or other object with an explicit termination
* method is finalized without having been closed.
*
* You always want to explicitly close such objects to avoid unnecessary resources
* leaks.
*/
public @NonNull Builder detectLeakedClosableObjects() {
return enable(DETECT_VM_CLOSABLE_LEAKS);
}
/**
* Detect when a {@link BroadcastReceiver} or {@link ServiceConnection} is leaked during
* {@link Context} teardown.
*/
public @NonNull Builder detectLeakedRegistrationObjects() {
return enable(DETECT_VM_REGISTRATION_LEAKS);
}
/**
* Detect when the calling application exposes a {@code file://} {@link android.net.Uri}
* to another app.
*
* This exposure is discouraged since the receiving app may not have access to the
* shared path. For example, the receiving app may not have requested the {@link
* android.Manifest.permission#READ_EXTERNAL_STORAGE} runtime permission, or the
* platform may be sharing the {@link android.net.Uri} across user profile boundaries.
*
* Instead, apps should use {@code content://} Uris so the platform can extend
* temporary permission for the receiving app to access the resource.
*
* @see android.support.v4.content.FileProvider
* @see Intent#FLAG_GRANT_READ_URI_PERMISSION
*/
public @NonNull Builder detectFileUriExposure() {
return enable(DETECT_VM_FILE_URI_EXPOSURE);
}
/**
* Detect any network traffic from the calling app which is not wrapped in SSL/TLS. This
* can help you detect places that your app is inadvertently sending cleartext data
* across the network.
*
* Using {@link #penaltyDeath()} or {@link #penaltyDeathOnCleartextNetwork()} will
* block further traffic on that socket to prevent accidental data leakage, in addition
* to crashing your process.
*
* Using {@link #penaltyDropBox()} will log the raw contents of the packet that
* triggered the violation.
*
* This inspects both IPv4/IPv6 and TCP/UDP network traffic, but it may be subject to
* false positives, such as when STARTTLS protocols or HTTP proxies are used.
*/
public @NonNull Builder detectCleartextNetwork() {
return enable(DETECT_VM_CLEARTEXT_NETWORK);
}
/**
* Detect when the calling application sends a {@code content://} {@link
* android.net.Uri} to another app without setting {@link
* Intent#FLAG_GRANT_READ_URI_PERMISSION} or {@link
* Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
*
* Forgetting to include one or more of these flags when sending an intent is
* typically an app bug.
*
* @see Intent#FLAG_GRANT_READ_URI_PERMISSION
* @see Intent#FLAG_GRANT_WRITE_URI_PERMISSION
*/
public @NonNull Builder detectContentUriWithoutPermission() {
return enable(DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION);
}
/**
* Detect any sockets in the calling app which have not been tagged using {@link
* TrafficStats}. Tagging sockets can help you investigate network usage inside your
* app, such as a narrowing down heavy usage to a specific library or component.
*
* This currently does not detect sockets created in native code.
*
* @see TrafficStats#setThreadStatsTag(int)
* @see TrafficStats#tagSocket(java.net.Socket)
* @see TrafficStats#tagDatagramSocket(java.net.DatagramSocket)
*/
public @NonNull Builder detectUntaggedSockets() {
return enable(DETECT_VM_UNTAGGED_SOCKET);
}
/** @hide */
public @NonNull Builder permitUntaggedSockets() {
return disable(DETECT_VM_UNTAGGED_SOCKET);
}
/**
* Detect any implicit reliance on Direct Boot automatic filtering
* of {@link PackageManager} values. Violations are only triggered
* when implicit calls are made while the user is locked.
*
* Apps becoming Direct Boot aware need to carefully inspect each
* query site and explicitly decide which combination of flags they
* want to use:
*
* When a user is locked, credential protected storage is
* unavailable, and files stored in these locations appear to not
* exist, which can result in subtle app bugs if they assume default
* behaviors or empty states. Instead, apps should store data needed
* while a user is locked under device protected storage areas.
*
* @see Context#createDeviceProtectedStorageContext()
*/
public @NonNull Builder detectCredentialProtectedWhileLocked() {
return enable(DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED);
}
/** @hide */
public @NonNull Builder permitCredentialProtectedWhileLocked() {
return disable(DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED);
}
/**
* Detect attempts to invoke a method on a {@link Context} that is not suited for such
* operation.
* An example of this is trying to obtain an instance of visual service (e.g.
* {@link android.view.WindowManager}) from a non-visual {@link Context}. This is not
* allowed, since a non-visual {@link Context} is not adjusted to any visual area, and
* therefore can report incorrect metrics or resources.
* @see Context#getDisplay()
* @see Context#getSystemService(String)
* @hide
*/
@TestApi
public @NonNull Builder detectIncorrectContextUse() {
return enable(DETECT_VM_INCORRECT_CONTEXT_USE);
}
/**
* Disable detection of incorrect context use.
* TODO(b/149790106): Fix usages and remove.
* @hide
*/
@TestApi
public @NonNull Builder permitIncorrectContextUse() {
return disable(DETECT_VM_INCORRECT_CONTEXT_USE);
}
/**
* Crashes the whole process on violation. This penalty runs at the end of all enabled
* penalties so you'll still get your logging or other violations before the process
* dies.
*/
public @NonNull Builder penaltyDeath() {
return enable(PENALTY_DEATH);
}
/**
* Crashes the whole process when cleartext network traffic is detected.
*
* @see #detectCleartextNetwork()
*/
public @NonNull Builder penaltyDeathOnCleartextNetwork() {
return enable(PENALTY_DEATH_ON_CLEARTEXT_NETWORK);
}
/**
* Crashes the whole process when a {@code file://} {@link android.net.Uri} is exposed
* beyond this app.
*
* @see #detectFileUriExposure()
*/
public @NonNull Builder penaltyDeathOnFileUriExposure() {
return enable(PENALTY_DEATH_ON_FILE_URI_EXPOSURE);
}
/** Log detected violations to the system log. */
public @NonNull Builder penaltyLog() {
return enable(PENALTY_LOG);
}
/**
* Enable detected violations 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 @NonNull Builder penaltyDropBox() {
return enable(PENALTY_DROPBOX);
}
/**
* Call #{@link OnVmViolationListener#onVmViolation(Violation)} on every violation.
*/
public @NonNull Builder penaltyListener(
@NonNull Executor executor, @NonNull OnVmViolationListener listener) {
if (executor == null) {
throw new NullPointerException("executor must not be null");
}
mListener = listener;
mExecutor = executor;
return this;
}
/** @removed */
public @NonNull Builder penaltyListener(
@NonNull OnVmViolationListener listener, @NonNull Executor executor) {
return penaltyListener(executor, listener);
}
private Builder enable(@VmPolicyMask int mask) {
mMask |= mask;
return this;
}
Builder disable(@VmPolicyMask int mask) {
mMask &= ~mask;
return this;
}
/**
* Construct the VmPolicy instance.
*
* Note: if no penalties are enabled before calling Internally this sets a thread-local variable 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 policy the policy to put into place
*/
public static void setThreadPolicy(final ThreadPolicy policy) {
setThreadPolicyMask(policy.mask);
sThreadViolationListener.set(policy.mListener);
sThreadViolationExecutor.set(policy.mCallbackExecutor);
}
/** @hide */
public static void setThreadPolicyMask(@ThreadPolicyMask int threadPolicyMask) {
// 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(threadPolicyMask);
// And set the Android native version...
Binder.setThreadStrictModePolicy(threadPolicyMask);
}
// Sets the policy in Dalvik/libcore (BlockGuard)
private static void setBlockGuardPolicy(@ThreadPolicyMask int threadPolicyMask) {
if (threadPolicyMask == 0) {
BlockGuard.setThreadPolicy(BlockGuard.LAX_POLICY);
return;
}
final BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
final AndroidBlockGuardPolicy androidPolicy;
if (policy instanceof AndroidBlockGuardPolicy) {
androidPolicy = (AndroidBlockGuardPolicy) policy;
} else {
androidPolicy = THREAD_ANDROID_POLICY.get();
BlockGuard.setThreadPolicy(androidPolicy);
}
androidPolicy.setThreadPolicyMask(threadPolicyMask);
}
private static void setBlockGuardVmPolicy(@VmPolicyMask int vmPolicyMask) {
// We only need to install BlockGuard for a small subset of VM policies
vmPolicyMask &= DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED;
if (vmPolicyMask != 0) {
BlockGuard.setVmPolicy(VM_ANDROID_POLICY);
} else {
BlockGuard.setVmPolicy(BlockGuard.LAX_VM_POLICY);
}
}
// Sets up CloseGuard in Dalvik/libcore
private static void setCloseGuardEnabled(boolean enabled) {
if (!(CloseGuard.getReporter() instanceof AndroidCloseGuardReporter)) {
CloseGuard.setReporter(new AndroidCloseGuardReporter());
}
CloseGuard.setEnabled(enabled);
}
/**
* Returns the bitmask of the current thread's policy.
*
* @return the bitmask of all the DETECT_* and PENALTY_* bits currently enabled
* @hide
*/
@UnsupportedAppUsage
public static @ThreadPolicyMask int getThreadPolicyMask() {
final BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (policy instanceof AndroidBlockGuardPolicy) {
return ((AndroidBlockGuardPolicy) policy).getThreadPolicyMask();
} else {
return 0;
}
}
/** Returns the current thread's policy. */
public static ThreadPolicy getThreadPolicy() {
// TODO: this was a last minute Gingerbread API change (to
// introduce VmPolicy cleanly) but this isn't particularly
// optimal for users who might call this method often. This
// should be in a thread-local and not allocate on each call.
return new ThreadPolicy(
getThreadPolicyMask(),
sThreadViolationListener.get(),
sThreadViolationExecutor.get());
}
/**
* A convenience wrapper that takes the current {@link ThreadPolicy} from {@link
* #getThreadPolicy}, modifies it to permit both disk reads & writes, and sets the new
* policy with {@link #setThreadPolicy}, returning the old policy so you can restore it at the
* end of a block.
*
* @return the old policy, to be passed to {@link #setThreadPolicy} to restore the policy at the
* end of a block
*/
public static ThreadPolicy allowThreadDiskWrites() {
return new ThreadPolicy(
allowThreadDiskWritesMask(),
sThreadViolationListener.get(),
sThreadViolationExecutor.get());
}
/** @hide */
public static @ThreadPolicyMask int allowThreadDiskWritesMask() {
int oldPolicyMask = getThreadPolicyMask();
int newPolicyMask = oldPolicyMask & ~(DETECT_THREAD_DISK_WRITE | DETECT_THREAD_DISK_READ);
if (newPolicyMask != oldPolicyMask) {
setThreadPolicyMask(newPolicyMask);
}
return oldPolicyMask;
}
/**
* A convenience wrapper that takes the current {@link ThreadPolicy} from {@link
* #getThreadPolicy}, modifies it to permit disk reads, and sets the new policy with {@link
* #setThreadPolicy}, returning the old policy so you can restore it at the end of a block.
*
* @return the old policy, to be passed to setThreadPolicy to restore the policy.
*/
public static ThreadPolicy allowThreadDiskReads() {
return new ThreadPolicy(
allowThreadDiskReadsMask(),
sThreadViolationListener.get(),
sThreadViolationExecutor.get());
}
/** @hide */
public static @ThreadPolicyMask int allowThreadDiskReadsMask() {
int oldPolicyMask = getThreadPolicyMask();
int newPolicyMask = oldPolicyMask & ~(DETECT_THREAD_DISK_READ);
if (newPolicyMask != oldPolicyMask) {
setThreadPolicyMask(newPolicyMask);
}
return oldPolicyMask;
}
/** @hide */
public static ThreadPolicy allowThreadViolations() {
ThreadPolicy oldPolicy = getThreadPolicy();
setThreadPolicyMask(0);
return oldPolicy;
}
/** @hide */
public static VmPolicy allowVmViolations() {
VmPolicy oldPolicy = getVmPolicy();
sVmPolicy = VmPolicy.LAX;
return oldPolicy;
}
/**
* Determine if the given app is "bundled" as part of the system image. These bundled apps are
* developed in lock-step with the OS, and they aren't updated outside of an OTA, so we want to
* chase any {@link StrictMode} regressions by enabling detection when running on {@link
* Build#IS_USERDEBUG} or {@link Build#IS_ENG} builds.
*
* Unbundled apps included in the system image are expected to detect and triage their own
* {@link StrictMode} issues separate from the OS release process, which is why we don't enable
* them here.
*
* @hide
*/
public static boolean isBundledSystemApp(ApplicationInfo ai) {
if (ai == null || ai.packageName == null) {
// Probably system server
return true;
} else if (ai.isSystemApp()) {
// Ignore unbundled apps living in the wrong namespace
if (ai.packageName.equals("com.android.vending")
|| ai.packageName.equals("com.android.chrome")) {
return false;
}
// Ignore bundled apps that are way too spammy
// STOPSHIP: burn this list down to zero
if (ai.packageName.equals("com.android.phone")) {
return false;
}
if (ai.packageName.equals("android")
|| ai.packageName.startsWith("android.")
|| ai.packageName.startsWith("com.android.")) {
return true;
}
}
return false;
}
/**
* Initialize default {@link ThreadPolicy} for the current thread.
*
* @hide
*/
public static void initThreadDefaults(ApplicationInfo ai) {
final ThreadPolicy.Builder builder = new ThreadPolicy.Builder();
final int targetSdkVersion =
(ai != null) ? ai.targetSdkVersion : Build.VERSION_CODES.CUR_DEVELOPMENT;
// Starting in HC, we don't allow network usage on the main thread
if (targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
builder.detectNetwork();
builder.penaltyDeathOnNetwork();
}
if (Build.IS_USER || DISABLE || SystemProperties.getBoolean(DISABLE_PROPERTY, false)) {
// Detect nothing extra
} else if (Build.IS_USERDEBUG) {
// Detect everything in bundled apps
if (isBundledSystemApp(ai)) {
builder.detectAll();
builder.penaltyDropBox();
if (SystemProperties.getBoolean(VISUAL_PROPERTY, false)) {
builder.penaltyFlashScreen();
}
}
} else if (Build.IS_ENG) {
// Detect everything in bundled apps
if (isBundledSystemApp(ai)) {
builder.detectAll();
builder.penaltyDropBox();
builder.penaltyLog();
builder.penaltyFlashScreen();
}
}
setThreadPolicy(builder.build());
}
/**
* Initialize default {@link VmPolicy} for the current VM.
*
* @hide
*/
public static void initVmDefaults(ApplicationInfo ai) {
final VmPolicy.Builder builder = new VmPolicy.Builder();
final int targetSdkVersion =
(ai != null) ? ai.targetSdkVersion : Build.VERSION_CODES.CUR_DEVELOPMENT;
// Starting in N, we don't allow file:// Uri exposure
if (targetSdkVersion >= Build.VERSION_CODES.N) {
builder.detectFileUriExposure();
builder.penaltyDeathOnFileUriExposure();
}
if (Build.IS_USER || DISABLE || SystemProperties.getBoolean(DISABLE_PROPERTY, false)) {
// Detect nothing extra
} else if (Build.IS_USERDEBUG) {
// Detect everything in bundled apps (except activity leaks, which
// are expensive to track)
if (isBundledSystemApp(ai)) {
builder.detectAll();
builder.permitActivityLeaks();
builder.penaltyDropBox();
}
} else if (Build.IS_ENG) {
// Detect everything in bundled apps
if (isBundledSystemApp(ai)) {
builder.detectAll();
builder.penaltyDropBox();
builder.penaltyLog();
}
}
setVmPolicy(builder.build());
}
/**
* Used by the framework to make file usage a fatal error.
*
* @hide
*/
@UnsupportedAppUsage
public static void enableDeathOnFileUriExposure() {
sVmPolicy =
new VmPolicy(
sVmPolicy.mask
| DETECT_VM_FILE_URI_EXPOSURE
| PENALTY_DEATH_ON_FILE_URI_EXPOSURE,
sVmPolicy.classInstanceLimit,
sVmPolicy.mListener,
sVmPolicy.mCallbackExecutor);
}
/**
* Used by lame internal apps that haven't done the hard work to get themselves off file:// Uris
* yet.
*
* @hide
*/
@UnsupportedAppUsage
public static void disableDeathOnFileUriExposure() {
sVmPolicy =
new VmPolicy(
sVmPolicy.mask
& ~(DETECT_VM_FILE_URI_EXPOSURE
| PENALTY_DEATH_ON_FILE_URI_EXPOSURE),
sVmPolicy.classInstanceLimit,
sVmPolicy.mListener,
sVmPolicy.mCallbackExecutor);
}
@UnsupportedAppUsage
private static final ThreadLocal This catches disk and network access on the main thread, as well as leaked SQLite cursors
* and unclosed resources. This is simply a wrapper around {@link #setVmPolicy} and {@link
* #setThreadPolicy}.
*/
public static void enableDefaults() {
setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
}
/** @hide */
public static boolean vmSqliteObjectLeaksEnabled() {
return (sVmPolicy.mask & DETECT_VM_CURSOR_LEAKS) != 0;
}
/** @hide */
public static boolean vmClosableObjectLeaksEnabled() {
return (sVmPolicy.mask & DETECT_VM_CLOSABLE_LEAKS) != 0;
}
/** @hide */
public static boolean vmRegistrationLeaksEnabled() {
return (sVmPolicy.mask & DETECT_VM_REGISTRATION_LEAKS) != 0;
}
/** @hide */
public static boolean vmFileUriExposureEnabled() {
return (sVmPolicy.mask & DETECT_VM_FILE_URI_EXPOSURE) != 0;
}
/** @hide */
public static boolean vmCleartextNetworkEnabled() {
return (sVmPolicy.mask & DETECT_VM_CLEARTEXT_NETWORK) != 0;
}
/** @hide */
public static boolean vmContentUriWithoutPermissionEnabled() {
return (sVmPolicy.mask & DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION) != 0;
}
/** @hide */
public static boolean vmUntaggedSocketEnabled() {
return (sVmPolicy.mask & DETECT_VM_UNTAGGED_SOCKET) != 0;
}
/** @hide */
public static boolean vmImplicitDirectBootEnabled() {
return (sVmPolicy.mask & DETECT_VM_IMPLICIT_DIRECT_BOOT) != 0;
}
/** @hide */
public static boolean vmCredentialProtectedWhileLockedEnabled() {
return (sVmPolicy.mask & DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED) != 0;
}
/** @hide */
public static boolean vmIncorrectContextUseEnabled() {
return (sVmPolicy.mask & DETECT_VM_INCORRECT_CONTEXT_USE) != 0;
}
/** @hide */
public static void onSqliteObjectLeaked(String message, Throwable originStack) {
onVmPolicyViolation(new SqliteObjectLeakedViolation(message, originStack));
}
/** @hide */
@UnsupportedAppUsage
public static void onWebViewMethodCalledOnWrongThread(Throwable originStack) {
onVmPolicyViolation(new WebViewMethodCalledOnWrongThreadViolation(originStack));
}
/** @hide */
public static void onIntentReceiverLeaked(Throwable originStack) {
onVmPolicyViolation(new IntentReceiverLeakedViolation(originStack));
}
/** @hide */
public static void onServiceConnectionLeaked(Throwable originStack) {
onVmPolicyViolation(new ServiceConnectionLeakedViolation(originStack));
}
/** @hide */
public static void onFileUriExposed(Uri uri, String location) {
final String message = uri + " exposed beyond app through " + location;
if ((sVmPolicy.mask & PENALTY_DEATH_ON_FILE_URI_EXPOSURE) != 0) {
throw new FileUriExposedException(message);
} else {
onVmPolicyViolation(new FileUriExposedViolation(message));
}
}
/** @hide */
public static void onContentUriWithoutPermission(Uri uri, String location) {
onVmPolicyViolation(new ContentUriWithoutPermissionViolation(uri, location));
}
/** @hide */
public static void onCleartextNetworkDetected(byte[] firstPacket) {
byte[] rawAddr = null;
if (firstPacket != null) {
if (firstPacket.length >= 20 && (firstPacket[0] & 0xf0) == 0x40) {
// IPv4
rawAddr = new byte[4];
System.arraycopy(firstPacket, 16, rawAddr, 0, 4);
} else if (firstPacket.length >= 40 && (firstPacket[0] & 0xf0) == 0x60) {
// IPv6
rawAddr = new byte[16];
System.arraycopy(firstPacket, 24, rawAddr, 0, 16);
}
}
final int uid = android.os.Process.myUid();
String msg = "Detected cleartext network traffic from UID " + uid;
if (rawAddr != null) {
try {
msg += " to " + InetAddress.getByAddress(rawAddr);
} catch (UnknownHostException ignored) {
}
}
msg += HexDump.dumpHexString(firstPacket).trim() + " ";
final boolean forceDeath = (sVmPolicy.mask & PENALTY_DEATH_ON_CLEARTEXT_NETWORK) != 0;
onVmPolicyViolation(new CleartextNetworkViolation(msg), forceDeath);
}
/** @hide */
public static void onUntaggedSocket() {
onVmPolicyViolation(new UntaggedSocketViolation());
}
/** @hide */
public static void onImplicitDirectBoot() {
onVmPolicyViolation(new ImplicitDirectBootViolation());
}
/** @hide */
public static void onIncorrectContextUsed(String message, Throwable originStack) {
onVmPolicyViolation(new IncorrectContextUseViolation(message, originStack));
}
/** Assume locked until we hear otherwise */
private static volatile boolean sUserKeyUnlocked = false;
private static boolean isUserKeyUnlocked(int userId) {
final IStorageManager storage = IStorageManager.Stub
.asInterface(ServiceManager.getService("mount"));
if (storage != null) {
try {
return storage.isUserKeyUnlocked(userId);
} catch (RemoteException ignored) {
}
}
return false;
}
/** @hide */
private static void onCredentialProtectedPathAccess(String path, int userId) {
// We can cache the unlocked state for the userId we're running as,
// since any relocking of that user will always result in our
// process being killed to release any CE FDs we're holding onto.
if (userId == UserHandle.myUserId()) {
if (sUserKeyUnlocked) {
return;
} else if (isUserKeyUnlocked(userId)) {
sUserKeyUnlocked = true;
return;
}
} else if (isUserKeyUnlocked(userId)) {
return;
}
onVmPolicyViolation(new CredentialProtectedWhileLockedViolation(
"Accessed credential protected path " + path + " while user " + userId
+ " was locked"));
}
// Map from VM violation fingerprint to uptime millis.
@UnsupportedAppUsage
private static final HashMap The object itself is a linked list node, to avoid any allocations during rapid span
* entries and exits.
*
* @hide
*/
public static class Span {
private String mName;
private long mCreateMillis;
private Span mNext;
private Span mPrev; // not used when in freeList, only active
private final ThreadSpanState mContainerState;
Span(ThreadSpanState threadState) {
mContainerState = threadState;
}
// Empty constructor for the NO_OP_SPAN
protected Span() {
mContainerState = null;
}
/**
* To be called when the critical span is complete (i.e. the animation is done animating).
* This can be called on any thread (even a different one from where the animation was
* taking place), but that's only a defensive implementation measure. It really makes no
* sense for you to call this on thread other than that where you created it.
*
* @hide
*/
@UnsupportedAppUsage
public void finish() {
ThreadSpanState state = mContainerState;
synchronized (state) {
if (mName == null) {
// Duplicate finish call. Ignore.
return;
}
// Remove ourselves from the active list.
if (mPrev != null) {
mPrev.mNext = mNext;
}
if (mNext != null) {
mNext.mPrev = mPrev;
}
if (state.mActiveHead == this) {
state.mActiveHead = mNext;
}
state.mActiveSize--;
if (LOG_V) Log.d(TAG, "Span finished=" + mName + "; size=" + state.mActiveSize);
this.mCreateMillis = -1;
this.mName = null;
this.mPrev = null;
this.mNext = null;
// Add ourselves to the freeList, if it's not already
// too big.
if (state.mFreeListSize < 5) {
this.mNext = state.mFreeListHead;
state.mFreeListHead = this;
state.mFreeListSize++;
}
}
}
}
// The no-op span that's used in user builds.
private static final Span NO_OP_SPAN =
new Span() {
public void finish() {
// Do nothing.
}
};
/**
* Linked lists of active spans and a freelist.
*
* Locking notes: there's one of these structures per thread and all members of this
* structure (as well as the Span nodes under it) are guarded by the ThreadSpanState object
* instance. While in theory there'd be no locking required because it's all local per-thread,
* the finish() method above is defensive against people calling it on a different thread from
* where they created the Span, hence the locking.
*/
private static class ThreadSpanState {
public Span mActiveHead; // doubly-linked list.
public int mActiveSize;
public Span mFreeListHead; // singly-linked list. only changes at head.
public int mFreeListSize;
}
private static final ThreadLocal The name is an arbitary label (or tag) that will be applied to any strictmode violation
* that happens while this span is active. You must call finish() on the span when done.
*
* This will never return null, but on devices without debugging enabled, this may return a
* dummy object on which the finish() method is a no-op.
*
* TODO: add CloseGuard to this, verifying callers call finish.
*
* @hide
*/
@UnsupportedAppUsage
public static Span enterCriticalSpan(String name) {
if (Build.IS_USER) {
return NO_OP_SPAN;
}
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("name must be non-null and non-empty");
}
ThreadSpanState state = sThisThreadSpanState.get();
Span span = null;
synchronized (state) {
if (state.mFreeListHead != null) {
span = state.mFreeListHead;
state.mFreeListHead = span.mNext;
state.mFreeListSize--;
} else {
// Shouldn't have to do this often.
span = new Span(state);
}
span.mName = name;
span.mCreateMillis = SystemClock.uptimeMillis();
span.mNext = state.mActiveHead;
span.mPrev = null;
state.mActiveHead = span;
state.mActiveSize++;
if (span.mNext != null) {
span.mNext.mPrev = span;
}
if (LOG_V) Log.d(TAG, "Span enter=" + name + "; size=" + state.mActiveSize);
}
return span;
}
/**
* For code to note that it's slow. This is a no-op unless the current thread's {@link
* android.os.StrictMode.ThreadPolicy} has {@link
* android.os.StrictMode.ThreadPolicy.Builder#detectCustomSlowCalls} enabled.
*
* @param name a short string for the exception stack trace that's built if when this fires.
*/
public static void noteSlowCall(String name) {
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (!(policy instanceof AndroidBlockGuardPolicy)) {
// StrictMode not enabled.
return;
}
((AndroidBlockGuardPolicy) policy).onCustomSlowCall(name);
}
/**
* For code to note that a resource was obtained using a type other than its defined type. This
* is a no-op unless the current thread's {@link android.os.StrictMode.ThreadPolicy} has {@link
* android.os.StrictMode.ThreadPolicy.Builder#detectResourceMismatches()} enabled.
*
* @param tag an object for the exception stack trace that's built if when this fires.
* @hide
*/
public static void noteResourceMismatch(Object tag) {
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (!(policy instanceof AndroidBlockGuardPolicy)) {
// StrictMode not enabled.
return;
}
((AndroidBlockGuardPolicy) policy).onResourceMismatch(tag);
}
/** @hide */
public static void noteUnbufferedIO() {
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (!(policy instanceof AndroidBlockGuardPolicy)) {
// StrictMode not enabled.
return;
}
policy.onUnbufferedIO();
}
/** @hide */
public static void noteDiskRead() {
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (!(policy instanceof AndroidBlockGuardPolicy)) {
// StrictMode not enabled.
return;
}
policy.onReadFromDisk();
}
/** @hide */
public static void noteDiskWrite() {
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (!(policy instanceof AndroidBlockGuardPolicy)) {
// StrictMode not enabled.
return;
}
policy.onWriteToDisk();
}
@GuardedBy("StrictMode.class")
private static final HashMap
* StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
* .detectAll()
* .penaltyLog()
* .build();
* StrictMode.setThreadPolicy(policy);
*
*/
public static final class Builder {
private @ThreadPolicyMask int mMask = 0;
private OnThreadViolationListener mListener;
private Executor mExecutor;
/**
* Create a Builder that detects nothing and has no violations. (but note that {@link
* #build} will default to enabling {@link #penaltyLog} if no other penalties are
* specified)
*/
public Builder() {
mMask = 0;
}
/** Initialize a Builder from an existing ThreadPolicy. */
public Builder(ThreadPolicy policy) {
mMask = policy.mask;
mListener = policy.mListener;
mExecutor = policy.mCallbackExecutor;
}
/**
* Detect everything that's potentially suspect.
*
* build
, {@link
* #penaltyLog} is implicitly set.
*/
public ThreadPolicy build() {
// If there are detection bits set but no violation bits
// set, enable simple logging.
if (mListener == null
&& mMask != 0
&& (mMask
& (PENALTY_DEATH
| PENALTY_LOG
| PENALTY_DROPBOX
| PENALTY_DIALOG))
== 0) {
penaltyLog();
}
return new ThreadPolicy(mMask, mListener, mExecutor);
}
}
}
/**
* {@link StrictMode} policy applied to all threads in the virtual machine's process.
*
*
* StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder()
* .detectAll()
* .penaltyLog()
* .build();
* StrictMode.setVmPolicy(policy);
*
*/
public static final class Builder {
@UnsupportedAppUsage
private @VmPolicyMask int mMask;
private OnVmViolationListener mListener;
private Executor mExecutor;
private HashMap
*
*/
public @NonNull Builder detectImplicitDirectBoot() {
return enable(DETECT_VM_IMPLICIT_DIRECT_BOOT);
}
/** @hide */
public @NonNull Builder permitImplicitDirectBoot() {
return disable(DETECT_VM_IMPLICIT_DIRECT_BOOT);
}
/**
* Detect access to filesystem paths stored in credential protected
* storage areas while the user is locked.
* build
, {@link
* #penaltyLog} is implicitly set.
*/
public VmPolicy build() {
// If there are detection bits set but no violation bits
// set, enable simple logging.
if (mListener == null
&& mMask != 0
&& (mMask
& (PENALTY_DEATH
| PENALTY_LOG
| PENALTY_DROPBOX
| PENALTY_DIALOG))
== 0) {
penaltyLog();
}
return new VmPolicy(
mMask,
mClassInstanceLimit != null ? mClassInstanceLimit : EMPTY_CLASS_LIMIT_MAP,
mListener,
mExecutor);
}
}
}
/**
* 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