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 android.system.ErrnoException;
20import android.system.StructLinger;
21import android.system.StructPollfd;
22import android.system.StructStat;
23import android.system.StructStatVfs;
24import android.util.MutableLong;
25import dalvik.system.BlockGuard;
26import dalvik.system.SocketTagger;
27import java.io.FileDescriptor;
28import java.io.InterruptedIOException;
29import java.net.InetAddress;
30import java.net.InetSocketAddress;
31import java.net.SocketAddress;
32import java.net.SocketException;
33import java.nio.ByteBuffer;
34import static android.system.OsConstants.*;
35import static dalvik.system.BlockGuard.DISALLOW_NETWORK;
36
37/**
38 * Informs BlockGuard of any activity it should be aware of.
39 */
40public class BlockGuardOs extends ForwardingOs {
41    public BlockGuardOs(Os os) {
42        super(os);
43    }
44
45    private FileDescriptor tagSocket(FileDescriptor fd) throws ErrnoException {
46        try {
47            SocketTagger.get().tag(fd);
48            return fd;
49        } catch (SocketException e) {
50            throw new ErrnoException("socket", EINVAL, e);
51        }
52    }
53
54    private void untagSocket(FileDescriptor fd) throws ErrnoException {
55        try {
56            SocketTagger.get().untag(fd);
57        } catch (SocketException e) {
58            throw new ErrnoException("socket", EINVAL, e);
59        }
60    }
61
62    @Override public FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException {
63        BlockGuard.getThreadPolicy().onNetwork();
64        return tagSocket(os.accept(fd, peerAddress));
65    }
66
67    @Override public boolean access(String path, int mode) throws ErrnoException {
68        BlockGuard.getThreadPolicy().onReadFromDisk();
69        return os.access(path, mode);
70    }
71
72    @Override public void chmod(String path, int mode) throws ErrnoException {
73        BlockGuard.getThreadPolicy().onWriteToDisk();
74        os.chmod(path, mode);
75    }
76
77    @Override public void chown(String path, int uid, int gid) throws ErrnoException {
78        BlockGuard.getThreadPolicy().onWriteToDisk();
79        os.chown(path, uid, gid);
80    }
81
82    @Override public void close(FileDescriptor fd) throws ErrnoException {
83        try {
84            // The usual case is that this _isn't_ a socket, so the getsockopt(2) call in
85            // isLingerSocket will throw, and that's really expensive. Try to avoid asking
86            // if we don't care.
87            if (fd.isSocket$()) {
88                if (isLingerSocket(fd)) {
89                    // If the fd is a socket with SO_LINGER set, we might block indefinitely.
90                    // We allow non-linger sockets so that apps can close their network
91                    // connections in methods like onDestroy which will run on the UI thread.
92                    BlockGuard.getThreadPolicy().onNetwork();
93                }
94                untagSocket(fd);
95            }
96        } catch (ErrnoException ignored) {
97            // We're called via Socket.close (which doesn't ask for us to be called), so we
98            // must not throw here, because Socket.close must not throw if asked to close an
99            // already-closed socket. Also, the passed-in FileDescriptor isn't necessarily
100            // a socket at all.
101        }
102        os.close(fd);
103    }
104
105    private static boolean isLingerSocket(FileDescriptor fd) throws ErrnoException {
106        StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER);
107        return linger.isOn() && linger.l_linger > 0;
108    }
109
110    @Override public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException {
111        BlockGuard.getThreadPolicy().onNetwork();
112        os.connect(fd, address, port);
113    }
114
115    @Override public void fchmod(FileDescriptor fd, int mode) throws ErrnoException {
116        BlockGuard.getThreadPolicy().onWriteToDisk();
117        os.fchmod(fd, mode);
118    }
119
120    @Override public void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException {
121        BlockGuard.getThreadPolicy().onWriteToDisk();
122        os.fchown(fd, uid, gid);
123    }
124
125    // TODO: Untag newFd when needed for dup2(FileDescriptor oldFd, int newFd)
126
127    @Override public void fdatasync(FileDescriptor fd) throws ErrnoException {
128        BlockGuard.getThreadPolicy().onWriteToDisk();
129        os.fdatasync(fd);
130    }
131
132    @Override public StructStat fstat(FileDescriptor fd) throws ErrnoException {
133        BlockGuard.getThreadPolicy().onReadFromDisk();
134        return os.fstat(fd);
135    }
136
137    @Override public StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException {
138        BlockGuard.getThreadPolicy().onReadFromDisk();
139        return os.fstatvfs(fd);
140    }
141
142    @Override public void fsync(FileDescriptor fd) throws ErrnoException {
143        BlockGuard.getThreadPolicy().onWriteToDisk();
144        os.fsync(fd);
145    }
146
147    @Override public void ftruncate(FileDescriptor fd, long length) throws ErrnoException {
148        BlockGuard.getThreadPolicy().onWriteToDisk();
149        os.ftruncate(fd, length);
150    }
151
152    @Override public void lchown(String path, int uid, int gid) throws ErrnoException {
153        BlockGuard.getThreadPolicy().onWriteToDisk();
154        os.lchown(path, uid, gid);
155    }
156
157    @Override public void link(String oldPath, String newPath) throws ErrnoException {
158        BlockGuard.getThreadPolicy().onWriteToDisk();
159        os.link(oldPath, newPath);
160    }
161
162    @Override public long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
163        BlockGuard.getThreadPolicy().onReadFromDisk();
164        return os.lseek(fd, offset, whence);
165    }
166
167    @Override public StructStat lstat(String path) throws ErrnoException {
168        BlockGuard.getThreadPolicy().onReadFromDisk();
169        return os.lstat(path);
170    }
171
172    @Override public void mkdir(String path, int mode) throws ErrnoException {
173        BlockGuard.getThreadPolicy().onWriteToDisk();
174        os.mkdir(path, mode);
175    }
176
177    @Override public void mkfifo(String path, int mode) throws ErrnoException {
178        BlockGuard.getThreadPolicy().onWriteToDisk();
179        os.mkfifo(path, mode);
180    }
181
182    @Override public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
183        BlockGuard.getThreadPolicy().onReadFromDisk();
184        if ((mode & O_ACCMODE) != O_RDONLY) {
185            BlockGuard.getThreadPolicy().onWriteToDisk();
186        }
187        return os.open(path, flags, mode);
188    }
189
190    @Override public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException {
191        // Greater than 0 is a timeout in milliseconds and -1 means "block forever",
192        // but 0 means "poll and return immediately", which shouldn't be subject to BlockGuard.
193        if (timeoutMs != 0) {
194            BlockGuard.getThreadPolicy().onNetwork();
195        }
196        return os.poll(fds, timeoutMs);
197    }
198
199    @Override public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException {
200        BlockGuard.getThreadPolicy().onWriteToDisk();
201        os.posix_fallocate(fd, offset, length);
202    }
203
204    @Override public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
205        BlockGuard.getThreadPolicy().onReadFromDisk();
206        return os.pread(fd, buffer, offset);
207    }
208
209    @Override public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
210        BlockGuard.getThreadPolicy().onReadFromDisk();
211        return os.pread(fd, bytes, byteOffset, byteCount, offset);
212    }
213
214    @Override public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
215        BlockGuard.getThreadPolicy().onWriteToDisk();
216        return os.pwrite(fd, buffer, offset);
217    }
218
219    @Override public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
220        BlockGuard.getThreadPolicy().onWriteToDisk();
221        return os.pwrite(fd, bytes, byteOffset, byteCount, offset);
222    }
223
224    @Override public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
225        BlockGuard.getThreadPolicy().onReadFromDisk();
226        return os.read(fd, buffer);
227    }
228
229    @Override public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
230        BlockGuard.getThreadPolicy().onReadFromDisk();
231        return os.read(fd, bytes, byteOffset, byteCount);
232    }
233
234    @Override public String readlink(String path) throws ErrnoException {
235      BlockGuard.getThreadPolicy().onReadFromDisk();
236      return os.readlink(path);
237    }
238
239    @Override public String realpath(String path) throws ErrnoException {
240      BlockGuard.getThreadPolicy().onReadFromDisk();
241      return os.realpath(path);
242    }
243
244    @Override public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException {
245        BlockGuard.getThreadPolicy().onReadFromDisk();
246        return os.readv(fd, buffers, offsets, byteCounts);
247    }
248
249    @Override public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
250        BlockGuard.getThreadPolicy().onNetwork();
251        return os.recvfrom(fd, buffer, flags, srcAddress);
252    }
253
254    @Override public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
255        BlockGuard.getThreadPolicy().onNetwork();
256        return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
257    }
258
259    @Override public void remove(String path) throws ErrnoException {
260        BlockGuard.getThreadPolicy().onWriteToDisk();
261        os.remove(path);
262    }
263
264    @Override public void rename(String oldPath, String newPath) throws ErrnoException {
265        BlockGuard.getThreadPolicy().onWriteToDisk();
266        os.rename(oldPath, newPath);
267    }
268
269    @Override public long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException {
270        BlockGuard.getThreadPolicy().onWriteToDisk();
271        return os.sendfile(outFd, inFd, inOffset, byteCount);
272    }
273
274    @Override public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
275        BlockGuard.getThreadPolicy().onNetwork();
276        return os.sendto(fd, buffer, flags, inetAddress, port);
277    }
278
279    @Override public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
280        // We permit datagrams without hostname lookups.
281        if (inetAddress != null) {
282            BlockGuard.getThreadPolicy().onNetwork();
283        }
284        return os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port);
285    }
286
287    @Override public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException {
288        return tagSocket(os.socket(domain, type, protocol));
289    }
290
291    @Override public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException {
292        os.socketpair(domain, type, protocol, fd1, fd2);
293        tagSocket(fd1);
294        tagSocket(fd2);
295    }
296
297    @Override public StructStat stat(String path) throws ErrnoException {
298        BlockGuard.getThreadPolicy().onReadFromDisk();
299        return os.stat(path);
300    }
301
302    @Override public StructStatVfs statvfs(String path) throws ErrnoException {
303        BlockGuard.getThreadPolicy().onReadFromDisk();
304        return os.statvfs(path);
305    }
306
307    @Override public void symlink(String oldPath, String newPath) throws ErrnoException {
308        BlockGuard.getThreadPolicy().onWriteToDisk();
309        os.symlink(oldPath, newPath);
310    }
311
312    @Override public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
313        BlockGuard.getThreadPolicy().onWriteToDisk();
314        return os.write(fd, buffer);
315    }
316
317    @Override public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
318        BlockGuard.getThreadPolicy().onWriteToDisk();
319        return os.write(fd, bytes, byteOffset, byteCount);
320    }
321
322    @Override public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException {
323        BlockGuard.getThreadPolicy().onWriteToDisk();
324        return os.writev(fd, buffers, offsets, byteCounts);
325    }
326}
327