BlockGuard.java revision 996bf79565ac88402920bd826d6f85952c83be20
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 isConnected(FileDescriptor fd, int timeout) throws IOException { 199 if (timeout != 0) { 200 // Greater than 0 is a timeout, but zero means "poll and return immediately". 201 BlockGuard.getThreadPolicy().onNetwork(); 202 } 203 return mNetwork.isConnected(fd, timeout); 204 } 205 206 public int send(FileDescriptor fd, byte[] data, int offset, int length, 207 int port, InetAddress inetAddress) throws IOException { 208 // Note: no BlockGuard violation. We permit datagrams 209 // without hostname lookups. (short, bounded amount of time) 210 return mNetwork.send(fd, data, offset, length, port, inetAddress); 211 } 212 213 public int sendDirect(FileDescriptor fd, int address, int offset, int length, 214 int port, InetAddress inetAddress) throws IOException { 215 // Note: no BlockGuard violation. We permit datagrams 216 // without hostname lookups. (short, bounded amount of time) 217 return mNetwork.sendDirect(fd, address, offset, length, port, inetAddress); 218 } 219 220 public int recv(FileDescriptor fd, DatagramPacket packet, byte[] data, int offset, 221 int length, boolean peek, boolean connected) throws IOException { 222 BlockGuard.getThreadPolicy().onNetwork(); 223 return mNetwork.recv(fd, packet, data, offset, length, peek, connected); 224 } 225 226 public int recvDirect(FileDescriptor fd, DatagramPacket packet, int address, int offset, 227 int length, boolean peek, boolean connected) throws IOException { 228 BlockGuard.getThreadPolicy().onNetwork(); 229 return mNetwork.recvDirect(fd, packet, address, offset, length, peek, connected); 230 } 231 232 public void disconnectDatagram(FileDescriptor aFD) throws SocketException { 233 mNetwork.disconnectDatagram(aFD); 234 } 235 236 public void sendUrgentData(FileDescriptor fd, byte value) { 237 mNetwork.sendUrgentData(fd, value); 238 } 239 240 public boolean select(FileDescriptor[] readFDs, FileDescriptor[] writeFDs, 241 int numReadable, int numWritable, long timeout, int[] flags) 242 throws SocketException { 243 BlockGuard.getThreadPolicy().onNetwork(); 244 return mNetwork.select(readFDs, writeFDs, numReadable, numWritable, timeout, flags); 245 } 246 247 public void close(FileDescriptor aFD) throws IOException { 248 // We exclude sockets without SO_LINGER so that apps can close their network connections 249 // in methods like onDestroy, which will run on the UI thread, without jumping through 250 // extra hoops. 251 if (isLingerSocket(aFD)) { 252 BlockGuard.getThreadPolicy().onNetwork(); 253 } 254 mNetwork.close(aFD); 255 } 256 257 private boolean isLingerSocket(FileDescriptor fd) throws SocketException { 258 try { 259 StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER); 260 return linger.isOn() && linger.l_linger > 0; 261 } catch (Exception ignored) { 262 // We're called via Socket.close (which doesn't ask for us to be called), so we 263 // must not throw here, because Socket.close must not throw if asked to close an 264 // already-closed socket. 265 return false; 266 } 267 } 268 } 269} 270