Home | History | Annotate | Download | only in io
      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