BlockGuardOs.java revision 90d96a4f168b7e56cff54dc94dca2f3cde60ebcd
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 17package libcore.io; 18 19import dalvik.system.BlockGuard; 20import java.io.FileDescriptor; 21import java.net.InetAddress; 22import java.net.SocketException; 23import java.nio.ByteBuffer; 24import static libcore.io.OsConstants.*; 25 26/** 27 * Informs BlockGuard of any activity it should be aware of. 28 */ 29public class BlockGuardOs extends ForwardingOs { 30 public BlockGuardOs(Os os) { 31 super(os); 32 } 33 34 @Override public void close(FileDescriptor fd) throws ErrnoException { 35 // TODO: is there a way to avoid calling getsockopt(2) on non-socket fds? 36 if (isLingerSocket(fd)) { 37 // If the fd is a socket with SO_LINGER set, we might block indefinitely. 38 // We allow non-linger sockets so that apps can close their network connections in 39 // methods like onDestroy which will run on the UI thread. 40 BlockGuard.getThreadPolicy().onNetwork(); 41 } 42 os.close(fd); 43 } 44 45 private static boolean isLingerSocket(FileDescriptor fd) { 46 try { 47 StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER); 48 return linger.isOn() && linger.l_linger > 0; 49 } catch (ErrnoException ignored) { 50 // We're called via Socket.close (which doesn't ask for us to be called), so we 51 // must not throw here, because Socket.close must not throw if asked to close an 52 // already-closed socket. Also, the passed-in FileDescriptor isn't necessarily 53 // a socket at all. 54 return false; 55 } 56 } 57 58 @Override public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException { 59 BlockGuard.getThreadPolicy().onNetwork(); 60 os.connect(fd, address, port); 61 } 62 63 @Override public void fdatasync(FileDescriptor fd) throws ErrnoException { 64 BlockGuard.getThreadPolicy().onWriteToDisk(); 65 os.fdatasync(fd); 66 } 67 68 @Override public void fsync(FileDescriptor fd) throws ErrnoException { 69 BlockGuard.getThreadPolicy().onWriteToDisk(); 70 os.fsync(fd); 71 } 72 73 @Override public void ftruncate(FileDescriptor fd, long length) throws ErrnoException { 74 BlockGuard.getThreadPolicy().onWriteToDisk(); 75 os.ftruncate(fd, length); 76 } 77 78 @Override public FileDescriptor open(String path, int flags, int mode) throws ErrnoException { 79 BlockGuard.getThreadPolicy().onReadFromDisk(); 80 if ((mode & O_ACCMODE) != O_RDONLY) { 81 BlockGuard.getThreadPolicy().onWriteToDisk(); 82 } 83 return os.open(path, flags, mode); 84 } 85 86 @Override public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException { 87 // Greater than 0 is a timeout in milliseconds and -1 means "block forever", 88 // but 0 means "poll and return immediately", which shouldn't be subject to BlockGuard. 89 if (timeoutMs != 0) { 90 BlockGuard.getThreadPolicy().onNetwork(); 91 } 92 return os.poll(fds, timeoutMs); 93 } 94 95 @Override public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { 96 BlockGuard.getThreadPolicy().onReadFromDisk(); 97 return os.read(fd, buffer); 98 } 99 100 @Override public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException { 101 BlockGuard.getThreadPolicy().onReadFromDisk(); 102 return os.read(fd, bytes, byteOffset, byteCount); 103 } 104 105 @Override public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException { 106 BlockGuard.getThreadPolicy().onReadFromDisk(); 107 return os.readv(fd, buffers, offsets, byteCounts); 108 } 109 110 @Override public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException { 111 BlockGuard.getThreadPolicy().onNetwork(); 112 return os.sendto(fd, buffer, flags, inetAddress, port); 113 } 114 115 @Override public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException { 116 // We permit datagrams without hostname lookups. 117 if (inetAddress != null) { 118 BlockGuard.getThreadPolicy().onNetwork(); 119 } 120 return os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); 121 } 122 123 @Override public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException { 124 final FileDescriptor fd = os.socket(domain, type, protocol); 125 try { 126 BlockGuard.tagSocketFd(fd); 127 } catch (SocketException e) { 128 throw new ErrnoException("socket", EINVAL, e); 129 } 130 return fd; 131 } 132 133 @Override public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { 134 BlockGuard.getThreadPolicy().onWriteToDisk(); 135 return os.write(fd, buffer); 136 } 137 138 @Override public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException { 139 BlockGuard.getThreadPolicy().onWriteToDisk(); 140 return os.write(fd, bytes, byteOffset, byteCount); 141 } 142 143 @Override public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException { 144 BlockGuard.getThreadPolicy().onWriteToDisk(); 145 return os.writev(fd, buffers, offsets, byteCounts); 146 } 147} 148