StrictMode.java revision 438d0595121a7a2cdf19741e76e3c0e21a5c173d
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package android.os;
17
18import android.app.ActivityManagerNative;
19import android.app.ApplicationErrorReport;
20import android.util.Log;
21
22import com.android.internal.os.RuntimeInit;
23
24import dalvik.system.BlockGuard;
25
26/**
27 * <p>StrictMode lets you impose stricter rules under which your
28 * application runs.</p>
29 */
30public final class StrictMode {
31    private static final String TAG = "StrictMode";
32
33    private StrictMode() {}
34
35    public static final int DISALLOW_DISK_WRITE = 0x01;
36    public static final int DISALLOW_DISK_READ = 0x02;
37    public static final int DISALLOW_NETWORK = 0x04;
38
39    /** @hide */
40    public static final int DISALLOW_MASK =
41            DISALLOW_DISK_WRITE | DISALLOW_DISK_READ | DISALLOW_NETWORK;
42
43    /**
44     * Flag to log to the system log.
45     */
46    public static final int PENALTY_LOG = 0x10;  // normal android.util.Log
47
48    /**
49     * Show an annoying dialog to the user.  Will be rate-limited to be only
50     * a little annoying.
51     */
52    public static final int PENALTY_DIALOG = 0x20;
53
54    /**
55     * Crash hard if policy is violated.
56     */
57    public static final int PENALTY_DEATH = 0x40;
58
59    /**
60     * Log a stacktrace to the DropBox on policy violation.
61     */
62    public static final int PENALTY_DROPBOX = 0x80;
63
64    /** @hide */
65    public static final int PENALTY_MASK =
66            PENALTY_LOG | PENALTY_DIALOG |
67            PENALTY_DROPBOX | PENALTY_DEATH;
68
69    /**
70     * Sets the policy for what actions the current thread is denied,
71     * as well as the penalty for violating the policy.
72     *
73     * @param policyMask a bitmask of DISALLOW_* and PENALTY_* values.
74     */
75    public static void setThreadBlockingPolicy(final int policyMask) {
76        BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
77        if (!(policy instanceof AndroidBlockGuardPolicy)) {
78            BlockGuard.setThreadPolicy(new AndroidBlockGuardPolicy(policyMask));
79        } else {
80            AndroidBlockGuardPolicy androidPolicy = (AndroidBlockGuardPolicy) policy;
81            androidPolicy.setPolicyMask(policyMask);
82        }
83    }
84
85    /**
86     * Returns the bitmask of the current thread's blocking policy.
87     *
88     * @return the bitmask of all the DISALLOW_* and PENALTY_* bits currently enabled
89     */
90    public static int getThreadBlockingPolicy() {
91        return BlockGuard.getThreadPolicy().getPolicyMask();
92    }
93
94    /** @hide */
95    public static void setDropBoxManager(DropBoxManager dropBoxManager) {
96        BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
97        if (!(policy instanceof AndroidBlockGuardPolicy)) {
98            policy = new AndroidBlockGuardPolicy(0);
99            BlockGuard.setThreadPolicy(policy);
100        }
101        ((AndroidBlockGuardPolicy) policy).setDropBoxManager(dropBoxManager);
102    }
103
104    private static class AndroidBlockGuardPolicy implements BlockGuard.Policy {
105        private int mPolicyMask;
106        private DropBoxManager mDropBoxManager = null;
107
108        public AndroidBlockGuardPolicy(final int policyMask) {
109            mPolicyMask = policyMask;
110        }
111
112        // Part of BlockGuard.Policy interface:
113        public int getPolicyMask() {
114            return mPolicyMask;
115        }
116
117        // Part of BlockGuard.Policy interface:
118        public void onWriteToDisk() {
119            if ((mPolicyMask & DISALLOW_DISK_WRITE) == 0) {
120                return;
121            }
122            handleViolation(DISALLOW_DISK_WRITE);
123        }
124
125        // Part of BlockGuard.Policy interface:
126        public void onReadFromDisk() {
127            if ((mPolicyMask & DISALLOW_DISK_READ) == 0) {
128                return;
129            }
130            handleViolation(DISALLOW_DISK_READ);
131        }
132
133        // Part of BlockGuard.Policy interface:
134        public void onNetwork() {
135            if ((mPolicyMask & DISALLOW_NETWORK) == 0) {
136                return;
137            }
138            handleViolation(DISALLOW_NETWORK);
139        }
140
141        public void setPolicyMask(int policyMask) {
142            mPolicyMask = policyMask;
143        }
144
145        public void setDropBoxManager(DropBoxManager dropBoxManager) {
146            mDropBoxManager = dropBoxManager;
147        }
148
149        private void handleViolation(int violationBit) {
150            final BlockGuard.BlockGuardPolicyException violation =
151                    new BlockGuard.BlockGuardPolicyException(mPolicyMask, violationBit);
152            violation.fillInStackTrace();
153
154            Looper looper = Looper.myLooper();
155            if (looper == null) {
156                // Without a Looper, we're unable to time how long the
157                // violation takes place.  This case should be rare,
158                // as most users will care about timing violations
159                // that happen on their main UI thread.
160                handleViolationWithTime(violation, -1L /* no time */);
161            } else {
162                MessageQueue queue = Looper.myQueue();
163                final long violationTime = SystemClock.uptimeMillis();
164                queue.addIdleHandler(new MessageQueue.IdleHandler() {
165                        public boolean queueIdle() {
166                            long afterViolationTime = SystemClock.uptimeMillis();
167                            handleViolationWithTime(violation, afterViolationTime - violationTime);
168                            return false;  // remove this idle handler from the array
169                        }
170                    });
171            }
172        }
173
174        private void handleViolationWithTime(
175            BlockGuard.BlockGuardPolicyException violation,
176            long durationMillis) {
177
178            // It's possible (even quite likely) that mPolicyMask has
179            // changed from the time the violation fired and now
180            // (after the violating code ran) due to people who
181            // push/pop temporary policy in regions of code.  So use
182            // the old policy here.
183            int policy = violation.getPolicy();
184
185            if ((policy & PENALTY_LOG) != 0) {
186                if (durationMillis != -1) {
187                    Log.d(TAG, "StrictMode policy violation; ~duration=" + durationMillis + " ms",
188                          violation);
189                } else {
190                    Log.d(TAG, "StrictMode policy violation.", violation);
191                }
192            }
193
194            if ((policy & PENALTY_DIALOG) != 0) {
195                // Currently this is just used for the dialog.
196                try {
197                    ActivityManagerNative.getDefault().handleApplicationStrictModeViolation(
198                        RuntimeInit.getApplicationObject(),
199                        new ApplicationErrorReport.CrashInfo(violation));
200                } catch (RemoteException e) {
201                    Log.e(TAG, "RemoteException trying to open strict mode dialog", e);
202                }
203            }
204
205            if ((policy & PENALTY_DROPBOX) != 0) {
206                // TODO: call into ActivityManagerNative to do the dropboxing.
207                // But do the first-layer signature dup-checking first client-side.
208                // This conditional should be combined with the above, too, along
209                // with PENALTY_DEATH below.
210            }
211
212            if ((policy & PENALTY_DEATH) != 0) {
213                System.err.println("StrictMode policy violation with POLICY_DEATH; shutting down.");
214                Process.killProcess(Process.myPid());
215                System.exit(10);
216            }
217        }
218    }
219}
220