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