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