1 /* 2 * Copyright (C) 2011 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 17 package libcore.io; 18 19 import android.system.ErrnoException; 20 import android.system.GaiException; 21 import android.system.Int64Ref; 22 import android.system.OsConstants; 23 import android.system.StructAddrinfo; 24 import android.system.StructLinger; 25 import android.system.StructPollfd; 26 import android.system.StructStat; 27 import android.system.StructStatVfs; 28 import dalvik.system.BlockGuard; 29 import dalvik.system.SocketTagger; 30 import java.io.FileDescriptor; 31 import java.io.InterruptedIOException; 32 import java.net.InetAddress; 33 import java.net.InetSocketAddress; 34 import java.net.SocketAddress; 35 import java.net.SocketException; 36 import java.nio.ByteBuffer; 37 38 import static android.system.OsConstants.*; 39 40 /** 41 * Informs BlockGuard of any activity it should be aware of. 42 */ 43 public class BlockGuardOs extends ForwardingOs { 44 public BlockGuardOs(Os os) { 45 super(os); 46 } 47 48 private FileDescriptor tagSocket(FileDescriptor fd) throws ErrnoException { 49 try { 50 SocketTagger.get().tag(fd); 51 return fd; 52 } catch (SocketException e) { 53 throw new ErrnoException("socket", EINVAL, e); 54 } 55 } 56 57 @Override public FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException { 58 BlockGuard.getThreadPolicy().onNetwork(); 59 final FileDescriptor acceptFd = os.accept(fd, peerAddress); 60 if (isInetSocket(acceptFd)) { 61 tagSocket(acceptFd); 62 } 63 return acceptFd; 64 } 65 66 @Override public boolean access(String path, int mode) throws ErrnoException { 67 BlockGuard.getThreadPolicy().onReadFromDisk(); 68 return os.access(path, mode); 69 } 70 71 @Override public void chmod(String path, int mode) throws ErrnoException { 72 BlockGuard.getThreadPolicy().onWriteToDisk(); 73 os.chmod(path, mode); 74 } 75 76 @Override public void chown(String path, int uid, int gid) throws ErrnoException { 77 BlockGuard.getThreadPolicy().onWriteToDisk(); 78 os.chown(path, uid, gid); 79 } 80 81 @Override public void close(FileDescriptor fd) throws ErrnoException { 82 try { 83 // The usual case is that this _isn't_ a socket, so the getsockopt(2) call in 84 // isLingerSocket will throw, and that's really expensive. Try to avoid asking 85 // if we don't care. 86 if (fd.isSocket$()) { 87 if (isLingerSocket(fd)) { 88 // If the fd is a socket with SO_LINGER set, we might block indefinitely. 89 // We allow non-linger sockets so that apps can close their network 90 // connections in methods like onDestroy which will run on the UI thread. 91 BlockGuard.getThreadPolicy().onNetwork(); 92 } 93 } 94 } catch (ErrnoException ignored) { 95 // We're called via Socket.close (which doesn't ask for us to be called), so we 96 // must not throw here, because Socket.close must not throw if asked to close an 97 // already-closed socket. Also, the passed-in FileDescriptor isn't necessarily 98 // a socket at all. 99 } 100 os.close(fd); 101 } 102 103 private static boolean isInetSocket(FileDescriptor fd) throws ErrnoException{ 104 return isInetDomain(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_DOMAIN)); 105 } 106 107 private static boolean isInetDomain(int domain) { 108 return (domain == AF_INET) || (domain == AF_INET6); 109 } 110 111 private static boolean isLingerSocket(FileDescriptor fd) throws ErrnoException { 112 StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER); 113 return linger.isOn() && linger.l_linger > 0; 114 } 115 116 @Override public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { 117 BlockGuard.getThreadPolicy().onNetwork(); 118 os.connect(fd, address, port); 119 } 120 121 @Override public void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException, 122 SocketException { 123 BlockGuard.getThreadPolicy().onNetwork(); 124 os.connect(fd, address); 125 } 126 127 @Override public void fchmod(FileDescriptor fd, int mode) throws ErrnoException { 128 BlockGuard.getThreadPolicy().onWriteToDisk(); 129 os.fchmod(fd, mode); 130 } 131 132 @Override public void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException { 133 BlockGuard.getThreadPolicy().onWriteToDisk(); 134 os.fchown(fd, uid, gid); 135 } 136 137 // TODO: Untag newFd when needed for dup2(FileDescriptor oldFd, int newFd) 138 139 @Override public void fdatasync(FileDescriptor fd) throws ErrnoException { 140 BlockGuard.getThreadPolicy().onWriteToDisk(); 141 os.fdatasync(fd); 142 } 143 144 @Override public StructStat fstat(FileDescriptor fd) throws ErrnoException { 145 BlockGuard.getThreadPolicy().onReadFromDisk(); 146 return os.fstat(fd); 147 } 148 149 @Override public StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException { 150 BlockGuard.getThreadPolicy().onReadFromDisk(); 151 return os.fstatvfs(fd); 152 } 153 154 @Override public void fsync(FileDescriptor fd) throws ErrnoException { 155 BlockGuard.getThreadPolicy().onWriteToDisk(); 156 os.fsync(fd); 157 } 158 159 @Override public void ftruncate(FileDescriptor fd, long length) throws ErrnoException { 160 BlockGuard.getThreadPolicy().onWriteToDisk(); 161 os.ftruncate(fd, length); 162 } 163 164 @Override public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException { 165 // With AI_NUMERICHOST flag set, the node must a numerical network address, therefore no 166 // host address lookups will be performed. In this case, it is fine to perform on main 167 // thread. 168 boolean isNumericHost = (hints.ai_flags & AI_NUMERICHOST) != 0; 169 if (!isNumericHost) { 170 BlockGuard.getThreadPolicy().onNetwork(); 171 } 172 return os.android_getaddrinfo(node, hints, netId); 173 } 174 175 @Override public void lchown(String path, int uid, int gid) throws ErrnoException { 176 BlockGuard.getThreadPolicy().onWriteToDisk(); 177 os.lchown(path, uid, gid); 178 } 179 180 @Override public void link(String oldPath, String newPath) throws ErrnoException { 181 BlockGuard.getThreadPolicy().onWriteToDisk(); 182 os.link(oldPath, newPath); 183 } 184 185 @Override public long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException { 186 BlockGuard.getThreadPolicy().onReadFromDisk(); 187 return os.lseek(fd, offset, whence); 188 } 189 190 @Override public StructStat lstat(String path) throws ErrnoException { 191 BlockGuard.getThreadPolicy().onReadFromDisk(); 192 return os.lstat(path); 193 } 194 195 @Override public void mkdir(String path, int mode) throws ErrnoException { 196 BlockGuard.getThreadPolicy().onWriteToDisk(); 197 os.mkdir(path, mode); 198 } 199 200 @Override public void mkfifo(String path, int mode) throws ErrnoException { 201 BlockGuard.getThreadPolicy().onWriteToDisk(); 202 os.mkfifo(path, mode); 203 } 204 205 @Override public FileDescriptor open(String path, int flags, int mode) throws ErrnoException { 206 BlockGuard.getThreadPolicy().onReadFromDisk(); 207 if ((flags & O_ACCMODE) != O_RDONLY) { 208 BlockGuard.getThreadPolicy().onWriteToDisk(); 209 } 210 return os.open(path, flags, mode); 211 } 212 213 @Override public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException { 214 // Greater than 0 is a timeout in milliseconds and -1 means "block forever", 215 // but 0 means "poll and return immediately", which shouldn't be subject to BlockGuard. 216 if (timeoutMs != 0) { 217 BlockGuard.getThreadPolicy().onNetwork(); 218 } 219 return os.poll(fds, timeoutMs); 220 } 221 222 @Override public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException { 223 BlockGuard.getThreadPolicy().onWriteToDisk(); 224 os.posix_fallocate(fd, offset, length); 225 } 226 227 @Override public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { 228 BlockGuard.getThreadPolicy().onReadFromDisk(); 229 return os.pread(fd, buffer, offset); 230 } 231 232 @Override public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { 233 BlockGuard.getThreadPolicy().onReadFromDisk(); 234 return os.pread(fd, bytes, byteOffset, byteCount, offset); 235 } 236 237 @Override public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { 238 BlockGuard.getThreadPolicy().onWriteToDisk(); 239 return os.pwrite(fd, buffer, offset); 240 } 241 242 @Override public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { 243 BlockGuard.getThreadPolicy().onWriteToDisk(); 244 return os.pwrite(fd, bytes, byteOffset, byteCount, offset); 245 } 246 247 @Override public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { 248 BlockGuard.getThreadPolicy().onReadFromDisk(); 249 return os.read(fd, buffer); 250 } 251 252 @Override public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { 253 BlockGuard.getThreadPolicy().onReadFromDisk(); 254 return os.read(fd, bytes, byteOffset, byteCount); 255 } 256 257 @Override public String readlink(String path) throws ErrnoException { 258 BlockGuard.getThreadPolicy().onReadFromDisk(); 259 return os.readlink(path); 260 } 261 262 @Override public String realpath(String path) throws ErrnoException { 263 BlockGuard.getThreadPolicy().onReadFromDisk(); 264 return os.realpath(path); 265 } 266 267 @Override public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException { 268 BlockGuard.getThreadPolicy().onReadFromDisk(); 269 return os.readv(fd, buffers, offsets, byteCounts); 270 } 271 272 @Override public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { 273 BlockGuard.getThreadPolicy().onNetwork(); 274 return os.recvfrom(fd, buffer, flags, srcAddress); 275 } 276 277 @Override public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { 278 BlockGuard.getThreadPolicy().onNetwork(); 279 return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); 280 } 281 282 @Override public void remove(String path) throws ErrnoException { 283 BlockGuard.getThreadPolicy().onWriteToDisk(); 284 os.remove(path); 285 } 286 287 @Override public void rename(String oldPath, String newPath) throws ErrnoException { 288 BlockGuard.getThreadPolicy().onWriteToDisk(); 289 os.rename(oldPath, newPath); 290 } 291 292 @Override public long sendfile(FileDescriptor outFd, FileDescriptor inFd, Int64Ref offset, long byteCount) throws ErrnoException { 293 BlockGuard.getThreadPolicy().onWriteToDisk(); 294 return os.sendfile(outFd, inFd, offset, byteCount); 295 } 296 297 @Override public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { 298 BlockGuard.getThreadPolicy().onNetwork(); 299 return os.sendto(fd, buffer, flags, inetAddress, port); 300 } 301 302 @Override public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { 303 // We permit datagrams without hostname lookups. 304 if (inetAddress != null) { 305 BlockGuard.getThreadPolicy().onNetwork(); 306 } 307 return os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); 308 } 309 310 @Override public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException { 311 final FileDescriptor fd = os.socket(domain, type, protocol); 312 if (isInetDomain(domain)) { 313 tagSocket(fd); 314 } 315 return fd; 316 } 317 318 @Override public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException { 319 os.socketpair(domain, type, protocol, fd1, fd2); 320 if (isInetDomain(domain)) { 321 tagSocket(fd1); 322 tagSocket(fd2); 323 } 324 } 325 326 @Override public StructStat stat(String path) throws ErrnoException { 327 BlockGuard.getThreadPolicy().onReadFromDisk(); 328 return os.stat(path); 329 } 330 331 @Override public StructStatVfs statvfs(String path) throws ErrnoException { 332 BlockGuard.getThreadPolicy().onReadFromDisk(); 333 return os.statvfs(path); 334 } 335 336 @Override public void symlink(String oldPath, String newPath) throws ErrnoException { 337 BlockGuard.getThreadPolicy().onWriteToDisk(); 338 os.symlink(oldPath, newPath); 339 } 340 341 @Override public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { 342 BlockGuard.getThreadPolicy().onWriteToDisk(); 343 return os.write(fd, buffer); 344 } 345 346 @Override public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { 347 BlockGuard.getThreadPolicy().onWriteToDisk(); 348 return os.write(fd, bytes, byteOffset, byteCount); 349 } 350 351 @Override public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException { 352 BlockGuard.getThreadPolicy().onWriteToDisk(); 353 return os.writev(fd, buffers, offsets, byteCounts); 354 } 355 356 @Override public void execv(String filename, String[] argv) throws ErrnoException { 357 BlockGuard.getThreadPolicy().onReadFromDisk(); 358 os.execv(filename, argv); 359 } 360 361 @Override public void execve(String filename, String[] argv, String[] envp) 362 throws ErrnoException { 363 BlockGuard.getThreadPolicy().onReadFromDisk(); 364 os.execve(filename, argv, envp); 365 } 366 367 @Override public byte[] getxattr(String path, String name) throws ErrnoException { 368 BlockGuard.getThreadPolicy().onReadFromDisk(); 369 return os.getxattr(path, name); 370 } 371 372 @Override public void msync(long address, long byteCount, int flags) throws ErrnoException { 373 if ((flags & OsConstants.MS_SYNC) != 0) { 374 BlockGuard.getThreadPolicy().onWriteToDisk(); 375 } 376 os.msync(address, byteCount, flags); 377 } 378 379 @Override public void removexattr(String path, String name) throws ErrnoException { 380 BlockGuard.getThreadPolicy().onWriteToDisk(); 381 os.removexattr(path, name); 382 } 383 384 @Override public void setxattr(String path, String name, byte[] value, int flags) 385 throws ErrnoException { 386 BlockGuard.getThreadPolicy().onWriteToDisk(); 387 os.setxattr(path, name, value, flags); 388 } 389 390 @Override public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, 391 int flags, SocketAddress address) throws ErrnoException, SocketException { 392 BlockGuard.getThreadPolicy().onNetwork(); 393 return os.sendto(fd, bytes, byteOffset, byteCount, flags, address); 394 } 395 396 @Override public void unlink(String pathname) throws ErrnoException { 397 BlockGuard.getThreadPolicy().onWriteToDisk(); 398 os.unlink(pathname); 399 } 400 401 @Override public long splice(FileDescriptor fdIn, Int64Ref offIn, FileDescriptor fdOut, Int64Ref offOut, long len, int flags) throws ErrnoException { 402 // It's infeasible to figure out if splice will result in read or write (would require fstat to figure out which fd is pipe). 403 // So, signal both read and write. 404 BlockGuard.getThreadPolicy().onWriteToDisk(); 405 BlockGuard.getThreadPolicy().onReadFromDisk(); 406 return os.splice(fdIn, offIn, fdOut, offOut, len, flags); 407 } 408 } 409