diff options
author | Brad Fitzpatrick <bradfitz@android.com> | 2010-06-10 12:19:19 -0700 |
---|---|---|
committer | Brad Fitzpatrick <bradfitz@android.com> | 2010-06-10 14:38:58 -0700 |
commit | 438d0595121a7a2cdf19741e76e3c0e21a5c173d (patch) | |
tree | d4a76171b2eb52c472c2900eb6947ff073bed81a /core/java/android/os | |
parent | 8f1bfb001b522a370a65c8e4545183b7611f672b (diff) | |
download | frameworks_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.java | 219 |
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); + } + } + } +} |