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