IoUtils.java revision ae394a866dd86df8819b652dfe00b3d2c7ee204c
1/*
2 * Copyright (C) 2010 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 java.io.Closeable;
20import java.io.FileDescriptor;
21import java.io.FileNotFoundException;
22import java.io.IOException;
23import java.io.RandomAccessFile;
24import java.net.InetAddress;
25import java.net.Inet4Address;
26import java.net.InetSocketAddress;
27import java.net.Socket;
28import java.net.SocketAddress;
29import java.net.SocketException;
30import java.net.SocketOptions;
31import java.util.Arrays;
32import libcore.io.ErrnoException;
33import libcore.io.Libcore;
34import libcore.util.MutableInt;
35import static libcore.io.OsConstants.*;
36
37public final class IoUtils {
38    private IoUtils() {
39    }
40
41    /**
42     * Implements java.io/java.net "available" semantics.
43     */
44    public static int available(FileDescriptor fd) throws IOException {
45        try {
46            MutableInt available = new MutableInt(0);
47            int rc = Libcore.os.ioctlInt(fd, FIONREAD, available);
48            if (available.value < 0) {
49                // If the fd refers to a regular file, the result is the difference between
50                // the file size and the file position. This may be negative if the position
51                // is past the end of the file. If the fd refers to a special file masquerading
52                // as a regular file, the result may be negative because the special file
53                // may appear to have zero size and yet a previous read call may have
54                // read some amount of data and caused the file position to be advanced.
55                available.value = 0;
56            }
57            return available.value;
58        } catch (ErrnoException errnoException) {
59            if (errnoException.errno == ENOTTY) {
60                // The fd is unwilling to opine about its read buffer.
61                return 0;
62            }
63            throw errnoException.rethrowAsIOException();
64        }
65    }
66
67    /**
68     * java.io only throws FileNotFoundException when opening files, regardless of what actually
69     * went wrong. Additionally, java.io is more restrictive than POSIX when it comes to opening
70     * directories: POSIX says read-only is okay, but java.io doesn't even allow that. We also
71     * have an Android-specific hack to alter the default permissions.
72     */
73    public static FileDescriptor open(String path, int flags) throws FileNotFoundException {
74        FileDescriptor fd = null;
75        try {
76            // On Android, we don't want default permissions to allow global access.
77            int mode = ((flags & O_ACCMODE) == O_RDONLY) ? 0 : 0600;
78            fd = Libcore.os.open(path, flags, mode);
79            if (fd.valid()) {
80                // Posix open(2) fails with EISDIR only if you ask for write permission.
81                // Java disallows reading directories too.
82                boolean isDirectory = false;
83                if (S_ISDIR(Libcore.os.fstat(fd).st_mode)) {
84                    throw new ErrnoException("open", EISDIR);
85                }
86            }
87            return fd;
88        } catch (ErrnoException errnoException) {
89            try {
90                if (fd != null) {
91                    close(fd);
92                }
93            } catch (IOException ignored) {
94            }
95            FileNotFoundException ex = new FileNotFoundException(path + ": " + errnoException.getMessage());
96            ex.initCause(errnoException);
97            throw ex;
98        }
99    }
100
101    /**
102     * java.io thinks that a read at EOF is an error and should return -1, contrary to traditional
103     * Unix practice where you'd read until you got 0 bytes (and any future read would return -1).
104     */
105    public static int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
106        Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
107        if (byteCount == 0) {
108            return 0;
109        }
110        try {
111            int readCount = Libcore.os.read(fd, bytes, byteOffset, byteCount);
112            if (readCount == 0) {
113                return -1;
114            }
115            return readCount;
116        } catch (ErrnoException errnoException) {
117            if (errnoException.errno == EAGAIN) {
118                // We return 0 rather than throw if we try to read from an empty non-blocking pipe.
119                return 0;
120            }
121            throw errnoException.rethrowAsIOException();
122        }
123    }
124
125    /**
126     * java.io always writes every byte it's asked to, or fails with an error. (That is, unlike
127     * Unix it never just writes as many bytes as happens to be convenient.)
128     */
129    public static void write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
130        Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
131        if (byteCount == 0) {
132            return;
133        }
134        try {
135            while (byteCount > 0) {
136                int bytesWritten = Libcore.os.write(fd, bytes, byteOffset, byteCount);
137                byteCount -= bytesWritten;
138                byteOffset += bytesWritten;
139            }
140        } catch (ErrnoException errnoException) {
141            throw errnoException.rethrowAsIOException();
142        }
143    }
144
145    /**
146     * Calls close(2) on 'fd'. Also resets the internal int to -1. Does nothing if 'fd' is null
147     * or invalid.
148     */
149    public static void close(FileDescriptor fd) throws IOException {
150        try {
151            if (fd != null && fd.valid()) {
152                Libcore.os.close(fd);
153            }
154        } catch (ErrnoException errnoException) {
155            throw errnoException.rethrowAsIOException();
156        }
157    }
158
159    /**
160     * Closes 'closeable', ignoring any exceptions. Does nothing if 'closeable' is null.
161     */
162    public static void closeQuietly(Closeable closeable) {
163        if (closeable != null) {
164            try {
165                closeable.close();
166            } catch (IOException ignored) {
167            }
168        }
169    }
170
171    /**
172     * Closes 'fd', ignoring any exceptions. Does nothing if 'fd' is null or invalid.
173     */
174    public static void closeQuietly(FileDescriptor fd) {
175        try {
176            IoUtils.close(fd);
177        } catch (IOException ignored) {
178        }
179    }
180
181    /**
182     * Closes 'socket', ignoring any exceptions. Does nothing if 'socket' is null.
183     */
184    public static void closeQuietly(Socket socket) {
185        if (socket != null) {
186            try {
187                socket.close();
188            } catch (Exception ignored) {
189            }
190        }
191    }
192
193    /**
194     * Sets 'fd' to be blocking or non-blocking, according to the state of 'blocking'.
195     */
196    public static void setBlocking(FileDescriptor fd, boolean blocking) throws IOException {
197        try {
198            int flags = Libcore.os.fcntlVoid(fd, F_GETFL);
199            if (!blocking) {
200                flags |= O_NONBLOCK;
201            } else {
202                flags &= ~O_NONBLOCK;
203            }
204            Libcore.os.fcntlLong(fd, F_SETFL, flags);
205        } catch (ErrnoException errnoException) {
206            throw errnoException.rethrowAsIOException();
207        }
208    }
209
210    public static FileDescriptor socket(boolean stream) throws SocketException {
211        FileDescriptor fd;
212        try {
213            fd = Libcore.os.socket(AF_INET6, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
214
215            // The RFC (http://www.ietf.org/rfc/rfc3493.txt) says that IPV6_MULTICAST_HOPS defaults
216            // to 1. The Linux kernel (at least up to 2.6.38) accidentally defaults to 64 (which
217            // would be correct for the *unicast* hop limit).
218            // See http://www.spinics.net/lists/netdev/msg129022.html, though no patch appears to
219            // have been applied as a result of that discussion. If that bug is ever fixed, we can
220            // remove this code. Until then, we manually set the hop limit on IPv6 datagram sockets.
221            // (IPv4 is already correct.)
222            if (!stream) {
223                Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1);
224            }
225
226            return fd;
227        } catch (ErrnoException errnoException) {
228            throw errnoException.rethrowAsSocketException();
229        }
230    }
231
232    // Socket options used by java.net but not exposed in SocketOptions.
233    public static final int JAVA_MCAST_JOIN_GROUP = 19;
234    public static final int JAVA_MCAST_LEAVE_GROUP = 20;
235    public static final int JAVA_IP_MULTICAST_TTL = 17;
236
237    /**
238     * java.net has its own socket options similar to the underlying Unix ones. We paper over the
239     * differences here.
240     */
241    public static Object getSocketOption(FileDescriptor fd, int option) throws SocketException {
242        try {
243            return getSocketOptionErrno(fd, option);
244        } catch (ErrnoException errnoException) {
245            throw errnoException.rethrowAsSocketException();
246        }
247    }
248
249    private static Object getSocketOptionErrno(FileDescriptor fd, int option) throws SocketException {
250        switch (option) {
251        case SocketOptions.IP_MULTICAST_IF:
252            // This is IPv4-only.
253            return Libcore.os.getsockoptInAddr(fd, IPPROTO_IP, IP_MULTICAST_IF);
254        case SocketOptions.IP_MULTICAST_IF2:
255            // This is IPv6-only.
256            return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF);
257        case SocketOptions.IP_MULTICAST_LOOP:
258            // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
259            // it doesn't matter which we return.
260            return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP));
261        case IoUtils.JAVA_IP_MULTICAST_TTL:
262            // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
263            // it doesn't matter which we return.
264            return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
265        case SocketOptions.IP_TOS:
266            // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
267            // it doesn't matter which we return.
268            return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS);
269        case SocketOptions.SO_BROADCAST:
270            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_BROADCAST));
271        case SocketOptions.SO_KEEPALIVE:
272            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE));
273        case SocketOptions.SO_LINGER:
274            StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER);
275            if (!linger.isOn()) {
276                return false;
277            }
278            return linger.l_linger;
279        case SocketOptions.SO_OOBINLINE:
280            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE));
281        case SocketOptions.SO_RCVBUF:
282            return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF);
283        case SocketOptions.SO_REUSEADDR:
284            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR));
285        case SocketOptions.SO_SNDBUF:
286            return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF);
287        case SocketOptions.SO_TIMEOUT:
288            return (int) Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO).toMillis();
289        case SocketOptions.TCP_NODELAY:
290            return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY));
291        default:
292            throw new SocketException("Unknown socket option: " + option);
293        }
294    }
295
296    private static boolean booleanFromInt(int i) {
297        return (i != 0);
298    }
299
300    private static int booleanToInt(boolean b) {
301        return b ? 1 : 0;
302    }
303
304    /**
305     * java.net has its own socket options similar to the underlying Unix ones. We paper over the
306     * differences here.
307     */
308    public static void setSocketOption(FileDescriptor fd, int option, Object value) throws SocketException {
309        try {
310            setSocketOptionErrno(fd, option, value);
311        } catch (ErrnoException errnoException) {
312            throw errnoException.rethrowAsSocketException();
313        }
314    }
315
316    private static void setSocketOptionErrno(FileDescriptor fd, int option, Object value) throws SocketException {
317        switch (option) {
318        case SocketOptions.IP_MULTICAST_IF:
319            throw new UnsupportedOperationException("Use IP_MULTICAST_IF2 on Android");
320        case SocketOptions.IP_MULTICAST_IF2:
321            // Although IPv6 was cleaned up to use int, IPv4 uses an ip_mreqn containing an int.
322            Libcore.os.setsockoptIpMreqn(fd, IPPROTO_IP, IP_MULTICAST_IF, (Integer) value);
323            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (Integer) value);
324            return;
325        case SocketOptions.IP_MULTICAST_LOOP:
326            // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte.
327            Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_LOOP, booleanToInt((Boolean) value));
328            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, booleanToInt((Boolean) value));
329            return;
330        case IoUtils.JAVA_IP_MULTICAST_TTL:
331            // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int,
332            // IPv4 multicast TTL uses a byte.
333            Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_TTL, (Integer) value);
334            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (Integer) value);
335            return;
336        case SocketOptions.IP_TOS:
337            Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_TOS, (Integer) value);
338            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS, (Integer) value);
339            return;
340        case SocketOptions.SO_BROADCAST:
341            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_BROADCAST, booleanToInt((Boolean) value));
342            return;
343        case SocketOptions.SO_KEEPALIVE:
344            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE, booleanToInt((Boolean) value));
345            return;
346        case SocketOptions.SO_LINGER:
347            boolean on = false;
348            int seconds = 0;
349            if (value instanceof Integer) {
350                on = true;
351                seconds = Math.min((Integer) value, 65535);
352            }
353            StructLinger linger = new StructLinger(booleanToInt(on), seconds);
354            Libcore.os.setsockoptLinger(fd, SOL_SOCKET, SO_LINGER, linger);
355            return;
356        case SocketOptions.SO_OOBINLINE:
357            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE, booleanToInt((Boolean) value));
358            return;
359        case SocketOptions.SO_RCVBUF:
360            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, (Integer) value);
361            return;
362        case SocketOptions.SO_REUSEADDR:
363            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR, booleanToInt((Boolean) value));
364            return;
365        case SocketOptions.SO_SNDBUF:
366            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_SNDBUF, (Integer) value);
367            return;
368        case SocketOptions.SO_TIMEOUT:
369            int millis = (Integer) value;
370            StructTimeval tv = StructTimeval.fromMillis(millis);
371            Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv);
372            return;
373        case SocketOptions.TCP_NODELAY:
374            Libcore.os.setsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY, booleanToInt((Boolean) value));
375            return;
376        case IoUtils.JAVA_MCAST_JOIN_GROUP:
377        case IoUtils.JAVA_MCAST_LEAVE_GROUP:
378            StructGroupReq groupReq = (StructGroupReq) value;
379            int level = (groupReq.gr_group instanceof Inet4Address) ? IPPROTO_IP : IPPROTO_IPV6;
380            int op = (option == JAVA_MCAST_JOIN_GROUP) ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP;
381            Libcore.os.setsockoptGroupReq(fd, level, op, groupReq);
382            return;
383        default:
384            throw new SocketException("Unknown socket option: " + option);
385        }
386    }
387
388    public static InetAddress getSocketLocalAddress(FileDescriptor fd) {
389        SocketAddress sa = Libcore.os.getsockname(fd);
390        InetSocketAddress isa = (InetSocketAddress) sa;
391        return isa.getAddress();
392    }
393
394    public static int getSocketLocalPort(FileDescriptor fd) {
395        SocketAddress sa = Libcore.os.getsockname(fd);
396        InetSocketAddress isa = (InetSocketAddress) sa;
397        return isa.getPort();
398    }
399
400    /**
401     * Returns the contents of 'path' as a byte array.
402     */
403    public static byte[] readFileAsByteArray(String path) throws IOException {
404        RandomAccessFile f = null;
405        try {
406            f = new RandomAccessFile(path, "r");
407            byte[] buf = new byte[(int) f.length()];
408            f.readFully(buf);
409            return buf;
410        } finally {
411            IoUtils.closeQuietly(f);
412        }
413    }
414}
415