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