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