BlockGuard.java revision 8b15dcc5890963edad4dfcf558cc16027c7985e5
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 */ 16 17package dalvik.system; 18 19import java.io.FileDescriptor; 20import java.io.FileNotFoundException; 21import java.io.IOException; 22import java.net.DatagramPacket; 23import java.net.InetAddress; 24import java.net.SocketException; 25import java.net.SocketImpl; 26import java.net.SocketOptions; 27import org.apache.harmony.luni.platform.INetworkSystem; 28 29/** 30 * Mechanism to let threads set restrictions on what code is allowed 31 * to do in their thread. 32 * 33 * <p>This is meant for applications to prevent certain blocking 34 * operations from running on their main event loop (or "UI") threads. 35 * 36 * <p>Note that this is all best-effort to catch most accidental mistakes 37 * and isn't intended to be a perfect mechanism, nor provide any sort of 38 * security. 39 * 40 * @hide 41 */ 42public final class BlockGuard { 43 44 public static final int DISALLOW_DISK_WRITE = 0x01; 45 public static final int DISALLOW_DISK_READ = 0x02; 46 public static final int DISALLOW_NETWORK = 0x04; 47 public static final int PASS_RESTRICTIONS_VIA_RPC = 0x08; 48 public static final int PENALTY_LOG = 0x10; 49 public static final int PENALTY_DIALOG = 0x20; 50 public static final int PENALTY_DEATH = 0x40; 51 52 public interface Policy { 53 /** 54 * Called on disk writes. 55 */ 56 void onWriteToDisk(); 57 58 /** 59 * Called on disk writes. 60 */ 61 void onReadFromDisk(); 62 63 /** 64 * Called on network operations. 65 */ 66 void onNetwork(); 67 68 /** 69 * Returns the policy bitmask, for shipping over Binder calls 70 * to remote threads/processes and reinstantiating the policy 71 * there. The bits in the mask are from the DISALLOW_* and 72 * PENALTY_* constants. 73 */ 74 int getPolicyMask(); 75 } 76 77 public static class BlockGuardPolicyException extends RuntimeException { 78 // bitmask of DISALLOW_*, PENALTY_*, etc flags 79 private final int mPolicyState; 80 private final int mPolicyViolated; 81 private final String mMessage; // may be null 82 83 public BlockGuardPolicyException(int policyState, int policyViolated) { 84 this(policyState, policyViolated, null); 85 } 86 87 public BlockGuardPolicyException(int policyState, int policyViolated, String message) { 88 mPolicyState = policyState; 89 mPolicyViolated = policyViolated; 90 mMessage = message; 91 fillInStackTrace(); 92 } 93 94 public int getPolicy() { 95 return mPolicyState; 96 } 97 98 public int getPolicyViolation() { 99 return mPolicyViolated; 100 } 101 102 public String getMessage() { 103 // Note: do not change this format casually. It's 104 // somewhat unfortunately Parceled and passed around 105 // Binder calls and parsed back into an Exception by 106 // Android's StrictMode. This was the least invasive 107 // option and avoided a gross mix of Java Serialization 108 // combined with Parcels. 109 return "policy=" + mPolicyState + " violation=" + mPolicyViolated + 110 (mMessage == null ? "" : (" msg=" + mMessage)); 111 } 112 } 113 114 /** 115 * The default, permissive policy that doesn't prevent any operations. 116 */ 117 public static final Policy LAX_POLICY = new Policy() { 118 public void onWriteToDisk() {} 119 public void onReadFromDisk() {} 120 public void onNetwork() {} 121 public int getPolicyMask() { 122 return 0; 123 } 124 }; 125 126 private static ThreadLocal<Policy> threadPolicy = new ThreadLocal<Policy>() { 127 @Override protected Policy initialValue() { 128 return LAX_POLICY; 129 } 130 }; 131 132 /** 133 * Get the current thread's policy. 134 * 135 * @return the current thread's policy. Never returns null. 136 * Will return the LAX_POLICY instance if nothing else is set. 137 */ 138 public static Policy getThreadPolicy() { 139 return threadPolicy.get(); 140 } 141 142 /** 143 * Sets the current thread's block guard policy. 144 * 145 * @param policy policy to set. May not be null. Use the public LAX_POLICY 146 * if you want to unset the active policy. 147 */ 148 public static void setThreadPolicy(Policy policy) { 149 if (policy == null) { 150 throw new NullPointerException("policy == null"); 151 } 152 threadPolicy.set(policy); 153 } 154 155 private BlockGuard() {} 156 157 /** 158 * A network wrapper that calls the policy check functions. 159 */ 160 public static class WrappedNetworkSystem implements INetworkSystem { 161 private final INetworkSystem mNetwork; 162 163 public WrappedNetworkSystem(INetworkSystem network) { 164 mNetwork = network; 165 } 166 167 public void accept(FileDescriptor serverFd, SocketImpl newSocket, 168 FileDescriptor clientFd) throws IOException { 169 BlockGuard.getThreadPolicy().onNetwork(); 170 mNetwork.accept(serverFd, newSocket, clientFd); 171 } 172 173 174 public void bind(FileDescriptor aFD, InetAddress inetAddress, int port) 175 throws SocketException { 176 mNetwork.bind(aFD, inetAddress, port); 177 } 178 179 public int read(FileDescriptor aFD, byte[] data, int offset, int count) throws IOException { 180 BlockGuard.getThreadPolicy().onNetwork(); 181 return mNetwork.read(aFD, data, offset, count); 182 } 183 184 public int readDirect(FileDescriptor aFD, int address, int count) throws IOException { 185 BlockGuard.getThreadPolicy().onNetwork(); 186 return mNetwork.readDirect(aFD, address, count); 187 } 188 189 public int write(FileDescriptor fd, byte[] data, int offset, int count) 190 throws IOException { 191 BlockGuard.getThreadPolicy().onNetwork(); 192 return mNetwork.write(fd, data, offset, count); 193 } 194 195 public int writeDirect(FileDescriptor fd, int address, int offset, int count) 196 throws IOException { 197 BlockGuard.getThreadPolicy().onNetwork(); 198 return mNetwork.writeDirect(fd, address, offset, count); 199 } 200 201 public boolean connectNonBlocking(FileDescriptor fd, InetAddress inetAddress, int port) 202 throws IOException { 203 BlockGuard.getThreadPolicy().onNetwork(); 204 return mNetwork.connectNonBlocking(fd, inetAddress, port); 205 } 206 207 public boolean isConnected(FileDescriptor fd, int timeout) throws IOException { 208 if (timeout != 0) { 209 // Less than 0 is blocking forever. 210 // Greater than 0 is a timeout. 211 // Zero is okay. 212 BlockGuard.getThreadPolicy().onNetwork(); 213 } 214 return mNetwork.isConnected(fd, timeout); 215 } 216 217 public int send(FileDescriptor fd, byte[] data, int offset, int length, 218 int port, InetAddress inetAddress) throws IOException { 219 // Note: no BlockGuard violation. We permit datagrams 220 // without hostname lookups. (short, bounded amount of time) 221 return mNetwork.send(fd, data, offset, length, port, inetAddress); 222 } 223 224 public int sendDirect(FileDescriptor fd, int address, int offset, int length, 225 int port, InetAddress inetAddress) throws IOException { 226 // Note: no BlockGuard violation. We permit datagrams 227 // without hostname lookups. (short, bounded amount of time) 228 return mNetwork.sendDirect(fd, address, offset, length, port, inetAddress); 229 } 230 231 public int recv(FileDescriptor fd, DatagramPacket packet, byte[] data, int offset, 232 int length, boolean peek, boolean connected) throws IOException { 233 BlockGuard.getThreadPolicy().onNetwork(); 234 return mNetwork.recv(fd, packet, data, offset, length, peek, connected); 235 } 236 237 public int recvDirect(FileDescriptor fd, DatagramPacket packet, int address, int offset, 238 int length, boolean peek, boolean connected) throws IOException { 239 BlockGuard.getThreadPolicy().onNetwork(); 240 return mNetwork.recvDirect(fd, packet, address, offset, length, peek, connected); 241 } 242 243 public void disconnectDatagram(FileDescriptor aFD) throws SocketException { 244 mNetwork.disconnectDatagram(aFD); 245 } 246 247 public void socket(FileDescriptor aFD, boolean stream) throws SocketException { 248 mNetwork.socket(aFD, stream); 249 } 250 251 public void sendUrgentData(FileDescriptor fd, byte value) { 252 mNetwork.sendUrgentData(fd, value); 253 } 254 255 public void connect(FileDescriptor aFD, InetAddress inetAddress, int port, 256 int timeout) throws SocketException { 257 BlockGuard.getThreadPolicy().onNetwork(); 258 mNetwork.connect(aFD, inetAddress, port, timeout); 259 } 260 261 public InetAddress getSocketLocalAddress(FileDescriptor aFD) { 262 return mNetwork.getSocketLocalAddress(aFD); 263 } 264 265 public boolean select(FileDescriptor[] readFDs, FileDescriptor[] writeFDs, 266 int numReadable, int numWritable, long timeout, int[] flags) 267 throws SocketException { 268 BlockGuard.getThreadPolicy().onNetwork(); 269 return mNetwork.select(readFDs, writeFDs, numReadable, numWritable, timeout, flags); 270 } 271 272 public int getSocketLocalPort(FileDescriptor aFD) { 273 return mNetwork.getSocketLocalPort(aFD); 274 } 275 276 public Object getSocketOption(FileDescriptor aFD, int opt) 277 throws SocketException { 278 return mNetwork.getSocketOption(aFD, opt); 279 } 280 281 public void setSocketOption(FileDescriptor aFD, int opt, Object optVal) 282 throws SocketException { 283 mNetwork.setSocketOption(aFD, opt, optVal); 284 } 285 286 public void close(FileDescriptor aFD) throws IOException { 287 // We exclude sockets without SO_LINGER so that apps can close their network connections 288 // in methods like onDestroy, which will run on the UI thread, without jumping through 289 // extra hoops. 290 if (isLingerSocket(aFD)) { 291 BlockGuard.getThreadPolicy().onNetwork(); 292 } 293 mNetwork.close(aFD); 294 } 295 296 private boolean isLingerSocket(FileDescriptor fd) throws SocketException { 297 try { 298 Object lingerValue = mNetwork.getSocketOption(fd, SocketOptions.SO_LINGER); 299 if (lingerValue instanceof Boolean) { 300 return (Boolean) lingerValue; 301 } else if (lingerValue instanceof Integer) { 302 return ((Integer) lingerValue) != 0; 303 } 304 throw new AssertionError(lingerValue.getClass().getName()); 305 } catch (Exception ignored) { 306 // We're called via Socket.close (which doesn't ask for us to be called), so we 307 // must not throw here, because Socket.close must not throw if asked to close an 308 // already-closed socket. 309 return false; 310 } 311 } 312 } 313} 314