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