summaryrefslogtreecommitdiffstats
path: root/core/java/android/os
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@android.com>2010-06-10 12:19:19 -0700
committerBrad Fitzpatrick <bradfitz@android.com>2010-06-10 14:38:58 -0700
commit438d0595121a7a2cdf19741e76e3c0e21a5c173d (patch)
treed4a76171b2eb52c472c2900eb6947ff073bed81a /core/java/android/os
parent8f1bfb001b522a370a65c8e4545183b7611f672b (diff)
downloadframeworks_base-438d0595121a7a2cdf19741e76e3c0e21a5c173d.zip
frameworks_base-438d0595121a7a2cdf19741e76e3c0e21a5c173d.tar.gz
frameworks_base-438d0595121a7a2cdf19741e76e3c0e21a5c173d.tar.bz2
Introduce "StrictMode"
This is a new public API for developers to opt-in to strict rules about what they're allowed to do on certain threads. (this is the public face of the @hide dalvik.system.BlockGuard, added recently...) In practice this will be used for developers to opt-in to declaring that they don't want to be allowed to do various operations (such as disk I/O or network operations) on their main UI threads. (these operations are often accidental, or even when they are fast come with a good chance of being slow or very slow in some cases....) Implementation wise, this is just a thread-local integer that has a bitmask of the things that aren't allowed, and more bits for saying what the violation penalty is. The penalties, of which multiple can be chosen, include: * logging * dropbox uploading for analysis/reporting * annoying dialog * full-on crashing These are all only very roughly implemented at this point, but all parts now minimally work end-to-end now, so this is a good checkpoint commit before this gets too large. Future CLs will polish all the above 4 penalties, including checksumming of stacktraces and minimizing penalties for duplicate violations. Change-Id: Icbe61a2e950119519e7364030b10c3c28d243abe
Diffstat (limited to 'core/java/android/os')
-rw-r--r--core/java/android/os/StrictMode.java219
1 files changed, 219 insertions, 0 deletions
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
new file mode 100644
index 0000000..876ec39
--- /dev/null
+++ b/core/java/android/os/StrictMode.java
@@ -0,0 +1,219 @@
+/*
+ * 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 com.android.internal.os.RuntimeInit;
+
+import dalvik.system.BlockGuard;
+
+/**
+ * <p>StrictMode lets you impose stricter rules under which your
+ * application runs.</p>
+ */
+public final class StrictMode {
+ private static final String TAG = "StrictMode";
+
+ private StrictMode() {}
+
+ public static final int DISALLOW_DISK_WRITE = 0x01;
+ public static final int DISALLOW_DISK_READ = 0x02;
+ public static final int DISALLOW_NETWORK = 0x04;
+
+ /** @hide */
+ public static final int DISALLOW_MASK =
+ DISALLOW_DISK_WRITE | DISALLOW_DISK_READ | DISALLOW_NETWORK;
+
+ /**
+ * Flag to log to the system log.
+ */
+ public static final int PENALTY_LOG = 0x10; // normal android.util.Log
+
+ /**
+ * Show an annoying dialog to the user. Will be rate-limited to be only
+ * a little annoying.
+ */
+ public static final int PENALTY_DIALOG = 0x20;
+
+ /**
+ * Crash hard if policy is violated.
+ */
+ public static final int PENALTY_DEATH = 0x40;
+
+ /**
+ * Log a stacktrace to the DropBox on policy violation.
+ */
+ public static final int PENALTY_DROPBOX = 0x80;
+
+ /** @hide */
+ public static final int PENALTY_MASK =
+ PENALTY_LOG | PENALTY_DIALOG |
+ PENALTY_DROPBOX | PENALTY_DEATH;
+
+ /**
+ * Sets the policy for what actions the current thread is denied,
+ * as well as the penalty for violating the policy.
+ *
+ * @param policyMask a bitmask of DISALLOW_* and PENALTY_* values.
+ */
+ public static void setThreadBlockingPolicy(final int policyMask) {
+ BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+ if (!(policy instanceof AndroidBlockGuardPolicy)) {
+ BlockGuard.setThreadPolicy(new AndroidBlockGuardPolicy(policyMask));
+ } else {
+ AndroidBlockGuardPolicy androidPolicy = (AndroidBlockGuardPolicy) policy;
+ androidPolicy.setPolicyMask(policyMask);
+ }
+ }
+
+ /**
+ * Returns the bitmask of the current thread's blocking policy.
+ *
+ * @return the bitmask of all the DISALLOW_* and PENALTY_* bits currently enabled
+ */
+ public static int getThreadBlockingPolicy() {
+ return BlockGuard.getThreadPolicy().getPolicyMask();
+ }
+
+ /** @hide */
+ public static void setDropBoxManager(DropBoxManager dropBoxManager) {
+ BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+ if (!(policy instanceof AndroidBlockGuardPolicy)) {
+ policy = new AndroidBlockGuardPolicy(0);
+ BlockGuard.setThreadPolicy(policy);
+ }
+ ((AndroidBlockGuardPolicy) policy).setDropBoxManager(dropBoxManager);
+ }
+
+ private static class AndroidBlockGuardPolicy implements BlockGuard.Policy {
+ private int mPolicyMask;
+ private DropBoxManager mDropBoxManager = null;
+
+ public AndroidBlockGuardPolicy(final int policyMask) {
+ mPolicyMask = policyMask;
+ }
+
+ // Part of BlockGuard.Policy interface:
+ public int getPolicyMask() {
+ return mPolicyMask;
+ }
+
+ // Part of BlockGuard.Policy interface:
+ public void onWriteToDisk() {
+ if ((mPolicyMask & DISALLOW_DISK_WRITE) == 0) {
+ return;
+ }
+ handleViolation(DISALLOW_DISK_WRITE);
+ }
+
+ // Part of BlockGuard.Policy interface:
+ public void onReadFromDisk() {
+ if ((mPolicyMask & DISALLOW_DISK_READ) == 0) {
+ return;
+ }
+ handleViolation(DISALLOW_DISK_READ);
+ }
+
+ // Part of BlockGuard.Policy interface:
+ public void onNetwork() {
+ if ((mPolicyMask & DISALLOW_NETWORK) == 0) {
+ return;
+ }
+ handleViolation(DISALLOW_NETWORK);
+ }
+
+ public void setPolicyMask(int policyMask) {
+ mPolicyMask = policyMask;
+ }
+
+ public void setDropBoxManager(DropBoxManager dropBoxManager) {
+ mDropBoxManager = dropBoxManager;
+ }
+
+ private void handleViolation(int violationBit) {
+ final BlockGuard.BlockGuardPolicyException violation =
+ new BlockGuard.BlockGuardPolicyException(mPolicyMask, violationBit);
+ violation.fillInStackTrace();
+
+ 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.
+ handleViolationWithTime(violation, -1L /* no time */);
+ } else {
+ MessageQueue queue = Looper.myQueue();
+ final long violationTime = SystemClock.uptimeMillis();
+ queue.addIdleHandler(new MessageQueue.IdleHandler() {
+ public boolean queueIdle() {
+ long afterViolationTime = SystemClock.uptimeMillis();
+ handleViolationWithTime(violation, afterViolationTime - violationTime);
+ return false; // remove this idle handler from the array
+ }
+ });
+ }
+ }
+
+ private void handleViolationWithTime(
+ BlockGuard.BlockGuardPolicyException violation,
+ long durationMillis) {
+
+ // It's possible (even quite likely) that mPolicyMask 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. So use
+ // the old policy here.
+ int policy = violation.getPolicy();
+
+ if ((policy & PENALTY_LOG) != 0) {
+ if (durationMillis != -1) {
+ Log.d(TAG, "StrictMode policy violation; ~duration=" + durationMillis + " ms",
+ violation);
+ } else {
+ Log.d(TAG, "StrictMode policy violation.", violation);
+ }
+ }
+
+ if ((policy & PENALTY_DIALOG) != 0) {
+ // Currently this is just used for the dialog.
+ try {
+ ActivityManagerNative.getDefault().handleApplicationStrictModeViolation(
+ RuntimeInit.getApplicationObject(),
+ new ApplicationErrorReport.CrashInfo(violation));
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException trying to open strict mode dialog", e);
+ }
+ }
+
+ if ((policy & PENALTY_DROPBOX) != 0) {
+ // TODO: call into ActivityManagerNative to do the dropboxing.
+ // But do the first-layer signature dup-checking first client-side.
+ // This conditional should be combined with the above, too, along
+ // with PENALTY_DEATH below.
+ }
+
+ if ((policy & PENALTY_DEATH) != 0) {
+ System.err.println("StrictMode policy violation with POLICY_DEATH; shutting down.");
+ Process.killProcess(Process.myPid());
+ System.exit(10);
+ }
+ }
+ }
+}