IoBridge.java revision 933fbbf606268eec9fc430632b8bca7002a833b3
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 java.io.FileDescriptor;
20import java.io.FileNotFoundException;
21import java.io.IOException;
22import java.net.BindException;
23import java.net.ConnectException;
24import java.net.DatagramPacket;
25import java.net.Inet4Address;
26import java.net.Inet6Address;
27import java.net.InetAddress;
28import java.net.InetSocketAddress;
29import java.net.NetworkInterface;
30import java.net.PortUnreachableException;
31import java.net.SocketAddress;
32import java.net.SocketException;
33import java.net.SocketOptions;
34import java.net.SocketTimeoutException;
35import java.net.UnknownHostException;
36import java.nio.ByteBuffer;
37import java.util.Arrays;
38import static libcore.io.OsConstants.*;
39import libcore.util.MutableInt;
40
41/**
42 * Implements java.io/java.net/java.nio semantics in terms of the underlying POSIX system calls.
43 */
44public final class IoBridge {
45
46    private IoBridge() {
47    }
48
49    public static int available(FileDescriptor fd) throws IOException {
50        try {
51            MutableInt available = new MutableInt(0);
52            Libcore.os.ioctlInt(fd, FIONREAD, available);
53            if (available.value < 0) {
54                // If the fd refers to a regular file, the result is the difference between
55                // the file size and the file position. This may be negative if the position
56                // is past the end of the file. If the fd refers to a special file masquerading
57                // as a regular file, the result may be negative because the special file
58                // may appear to have zero size and yet a previous read call may have
59                // read some amount of data and caused the file position to be advanced.
60                available.value = 0;
61            }
62            return available.value;
63        } catch (ErrnoException errnoException) {
64            if (errnoException.errno == ENOTTY) {
65                // The fd is unwilling to opine about its read buffer.
66                return 0;
67            }
68            throw errnoException.rethrowAsIOException();
69        }
70    }
71
72
73    public static void bind(FileDescriptor fd, InetAddress address, int port) throws SocketException {
74        if (address instanceof Inet6Address && ((Inet6Address) address).getScopeId() == 0) {
75            // Linux won't let you bind a link-local address without a scope id. Find one.
76            NetworkInterface nif = NetworkInterface.getByInetAddress(address);
77            if (nif == null) {
78                throw new SocketException("Can't bind to a link-local address without a scope id: " + address);
79            }
80            try {
81                address = Inet6Address.getByAddress(address.getHostName(), address.getAddress(), nif.getIndex());
82            } catch (UnknownHostException ex) {
83                throw new AssertionError(ex); // Can't happen.
84            }
85        }
86        try {
87            Libcore.os.bind(fd, address, port);
88        } catch (ErrnoException errnoException) {
89            throw new BindException(errnoException.getMessage(), errnoException);
90        }
91    }
92
93
94    /**
95     * Connects socket 'fd' to 'inetAddress' on 'port', with no timeout. The lack of a timeout
96     * means this method won't throw SocketTimeoutException.
97     */
98    public static void connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException {
99        try {
100            IoBridge.connect(fd, inetAddress, port, 0);
101        } catch (SocketTimeoutException ex) {
102            throw new AssertionError(ex); // Can't happen for a connect without a timeout.
103        }
104    }
105
106    /**
107     * Connects socket 'fd' to 'inetAddress' on 'port', with a the given 'timeoutMs'.
108     * Use timeoutMs == 0 for a blocking connect with no timeout.
109     */
110    public static void connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException {
111        try {
112            connectErrno(fd, inetAddress, port, timeoutMs);
113        } catch (ErrnoException errnoException) {
114            throw new ConnectException(connectDetail(inetAddress, port, timeoutMs, errnoException), errnoException);
115        } catch (SocketException ex) {
116            throw ex; // We don't want to doubly wrap these.
117        } catch (SocketTimeoutException ex) {
118            throw ex; // We don't want to doubly wrap these.
119        } catch (IOException ex) {
120            throw new SocketException(ex);
121        }
122    }
123
124    private static void connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws ErrnoException, IOException {
125        // With no timeout, just call connect(2) directly.
126        if (timeoutMs == 0) {
127            Libcore.os.connect(fd, inetAddress, port);
128            return;
129        }
130
131        // For connect with a timeout, we:
132        //   1. set the socket to non-blocking,
133        //   2. connect(2),
134        //   3. loop using poll(2) to decide whether we're connected, whether we should keep
135        //      waiting, or whether we've seen a permanent failure and should give up,
136        //   4. set the socket back to blocking.
137
138        // 1. set the socket to non-blocking.
139        IoUtils.setBlocking(fd, false);
140
141        // 2. call connect(2) non-blocking.
142        long finishTimeMs = System.currentTimeMillis() + timeoutMs;
143        try {
144            Libcore.os.connect(fd, inetAddress, port);
145            IoUtils.setBlocking(fd, true); // 4. set the socket back to blocking.
146            return; // We connected immediately.
147        } catch (ErrnoException errnoException) {
148            if (errnoException.errno != EINPROGRESS) {
149                throw errnoException;
150            }
151            // EINPROGRESS means we should keep trying...
152        }
153
154        // 3. loop using poll(2).
155        int remainingTimeoutMs;
156        do {
157            remainingTimeoutMs = (int) (finishTimeMs - System.currentTimeMillis());
158            if (remainingTimeoutMs <= 0) {
159                throw new SocketTimeoutException(connectDetail(inetAddress, port, timeoutMs, null));
160            }
161        } while (!IoBridge.isConnected(fd, inetAddress, port, timeoutMs, remainingTimeoutMs));
162        IoUtils.setBlocking(fd, true); // 4. set the socket back to blocking.
163    }
164
165    private static String connectDetail(InetAddress inetAddress, int port, int timeoutMs, ErrnoException cause) {
166        String detail = "failed to connect to " + inetAddress + " (port " + port + ")";
167        if (timeoutMs > 0) {
168            detail += " after " + timeoutMs + "ms";
169        }
170        if (cause != null) {
171            detail += ": " + cause.getMessage();
172        }
173        return detail;
174    }
175
176    public static void closeSocket(FileDescriptor fd) throws IOException {
177        if (!fd.valid()) {
178            // Socket.close doesn't throw if you try to close an already-closed socket.
179            return;
180        }
181        int intFd = fd.getInt$();
182        fd.setInt$(-1);
183        FileDescriptor oldFd = new FileDescriptor();
184        oldFd.setInt$(intFd);
185        AsynchronousCloseMonitor.signalBlockedThreads(oldFd);
186        try {
187            Libcore.os.close(oldFd);
188        } catch (ErrnoException errnoException) {
189            // TODO: are there any cases in which we should throw?
190        }
191    }
192
193    public static boolean isConnected(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs, int remainingTimeoutMs) throws IOException {
194        ErrnoException cause;
195        try {
196            StructPollfd[] pollFds = new StructPollfd[] { new StructPollfd() };
197            pollFds[0].fd = fd;
198            pollFds[0].events = (short) POLLOUT;
199            int rc = Libcore.os.poll(pollFds, remainingTimeoutMs);
200            if (rc == 0) {
201                return false; // Timeout.
202            }
203            int connectError = Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_ERROR);
204            if (connectError == 0) {
205                return true; // Success!
206            }
207            throw new ErrnoException("isConnected", connectError); // The connect(2) failed.
208        } catch (ErrnoException errnoException) {
209            if (!fd.valid()) {
210                throw new SocketException("Socket closed");
211            }
212            if (errnoException.errno == EINTR) {
213                return false; // Punt and ask the caller to try again.
214            } else {
215                cause = errnoException;
216            }
217        }
218        String detail = connectDetail(inetAddress, port, timeoutMs, cause);
219        if (cause.errno == ETIMEDOUT) {
220            throw new SocketTimeoutException(detail, cause);
221        }
222        throw new ConnectException(detail, cause);
223    }
224
225    // Socket options used by java.net but not exposed in SocketOptions.
226    public static final int JAVA_MCAST_JOIN_GROUP = 19;
227    public static final int JAVA_MCAST_LEAVE_GROUP = 20;
228    public static final int JAVA_IP_MULTICAST_TTL = 17;
229
230    /**
231     * java.net has its own socket options similar to the underlying Unix ones. We paper over the
232     * differences here.
233     */
234    public static Object getSocketOption(FileDescriptor fd, int option) throws SocketException {
235        try {
236            return getSocketOptionErrno(fd, option);
237        } catch (ErrnoException errnoException) {
238            throw errnoException.rethrowAsSocketException();
239        }
240    }
241
242    private static Object getSocketOptionErrno(FileDescriptor fd, int option) throws ErrnoException, SocketException {
243        switch (option) {
244        case SocketOptions.IP_MULTICAST_IF:
245            // This is IPv4-only.
246            return Libcore.os.getsockoptInAddr(fd, IPPROTO_IP, IP_MULTICAST_IF);
247        case SocketOptions.IP_MULTICAST_IF2:
248            // This is IPv6-only.
249            return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF);
250        case SocketOptions.IP_MULTICAST_LOOP:
251            // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
252            // it doesn't matter which we return.
253            return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP));
254        case IoBridge.JAVA_IP_MULTICAST_TTL:
255            // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
256            // it doesn't matter which we return.
257            return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
258        case SocketOptions.IP_TOS:
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 Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS);
262        case SocketOptions.SO_BROADCAST:
263            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_BROADCAST));
264        case SocketOptions.SO_KEEPALIVE:
265            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE));
266        case SocketOptions.SO_LINGER:
267            StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER);
268            if (!linger.isOn()) {
269                return false;
270            }
271            return linger.l_linger;
272        case SocketOptions.SO_OOBINLINE:
273            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE));
274        case SocketOptions.SO_RCVBUF:
275            return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_RCVBUF);
276        case SocketOptions.SO_REUSEADDR:
277            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR));
278        case SocketOptions.SO_SNDBUF:
279            return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF);
280        case SocketOptions.SO_TIMEOUT:
281            return (int) Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO).toMillis();
282        case SocketOptions.TCP_NODELAY:
283            return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY));
284        default:
285            throw new SocketException("Unknown socket option: " + option);
286        }
287    }
288
289    private static boolean booleanFromInt(int i) {
290        return (i != 0);
291    }
292
293    private static int booleanToInt(boolean b) {
294        return b ? 1 : 0;
295    }
296
297    /**
298     * java.net has its own socket options similar to the underlying Unix ones. We paper over the
299     * differences here.
300     */
301    public static void setSocketOption(FileDescriptor fd, int option, Object value) throws SocketException {
302        try {
303            setSocketOptionErrno(fd, option, value);
304        } catch (ErrnoException errnoException) {
305            throw errnoException.rethrowAsSocketException();
306        }
307    }
308
309    private static void setSocketOptionErrno(FileDescriptor fd, int option, Object value) throws ErrnoException, SocketException {
310        switch (option) {
311        case SocketOptions.IP_MULTICAST_IF:
312            throw new UnsupportedOperationException("Use IP_MULTICAST_IF2 on Android");
313        case SocketOptions.IP_MULTICAST_IF2:
314            // Although IPv6 was cleaned up to use int, IPv4 uses an ip_mreqn containing an int.
315            Libcore.os.setsockoptIpMreqn(fd, IPPROTO_IP, IP_MULTICAST_IF, (Integer) value);
316            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (Integer) value);
317            return;
318        case SocketOptions.IP_MULTICAST_LOOP:
319            // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte.
320            Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_LOOP, booleanToInt((Boolean) value));
321            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, booleanToInt((Boolean) value));
322            return;
323        case IoBridge.JAVA_IP_MULTICAST_TTL:
324            // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int,
325            // IPv4 multicast TTL uses a byte.
326            Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_TTL, (Integer) value);
327            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (Integer) value);
328            return;
329        case SocketOptions.IP_TOS:
330            Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_TOS, (Integer) value);
331            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS, (Integer) value);
332            return;
333        case SocketOptions.SO_BROADCAST:
334            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_BROADCAST, booleanToInt((Boolean) value));
335            return;
336        case SocketOptions.SO_KEEPALIVE:
337            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE, booleanToInt((Boolean) value));
338            return;
339        case SocketOptions.SO_LINGER:
340            boolean on = false;
341            int seconds = 0;
342            if (value instanceof Integer) {
343                on = true;
344                seconds = Math.min((Integer) value, 65535);
345            }
346            StructLinger linger = new StructLinger(booleanToInt(on), seconds);
347            Libcore.os.setsockoptLinger(fd, SOL_SOCKET, SO_LINGER, linger);
348            return;
349        case SocketOptions.SO_OOBINLINE:
350            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE, booleanToInt((Boolean) value));
351            return;
352        case SocketOptions.SO_RCVBUF:
353            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, (Integer) value);
354            return;
355        case SocketOptions.SO_REUSEADDR:
356            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR, booleanToInt((Boolean) value));
357            return;
358        case SocketOptions.SO_SNDBUF:
359            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_SNDBUF, (Integer) value);
360            return;
361        case SocketOptions.SO_TIMEOUT:
362            int millis = (Integer) value;
363            StructTimeval tv = StructTimeval.fromMillis(millis);
364            Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv);
365            return;
366        case SocketOptions.TCP_NODELAY:
367            Libcore.os.setsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY, booleanToInt((Boolean) value));
368            return;
369        case IoBridge.JAVA_MCAST_JOIN_GROUP:
370        case IoBridge.JAVA_MCAST_LEAVE_GROUP:
371            StructGroupReq groupReq = (StructGroupReq) value;
372            int level = (groupReq.gr_group instanceof Inet4Address) ? IPPROTO_IP : IPPROTO_IPV6;
373            int op = (option == JAVA_MCAST_JOIN_GROUP) ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP;
374            Libcore.os.setsockoptGroupReq(fd, level, op, groupReq);
375            return;
376        default:
377            throw new SocketException("Unknown socket option: " + option);
378        }
379    }
380
381    /**
382     * java.io only throws FileNotFoundException when opening files, regardless of what actually
383     * went wrong. Additionally, java.io is more restrictive than POSIX when it comes to opening
384     * directories: POSIX says read-only is okay, but java.io doesn't even allow that. We also
385     * have an Android-specific hack to alter the default permissions.
386     */
387    public static FileDescriptor open(String path, int flags) throws FileNotFoundException {
388        FileDescriptor fd = null;
389        try {
390            // On Android, we don't want default permissions to allow global access.
391            int mode = ((flags & O_ACCMODE) == O_RDONLY) ? 0 : 0600;
392            fd = Libcore.os.open(path, flags, mode);
393            // Posix open(2) fails with EISDIR only if you ask for write permission.
394            // Java disallows reading directories too.
395            if (S_ISDIR(Libcore.os.fstat(fd).st_mode)) {
396                throw new ErrnoException("open", EISDIR);
397            }
398            return fd;
399        } catch (ErrnoException errnoException) {
400            try {
401                if (fd != null) {
402                    IoUtils.close(fd);
403                }
404            } catch (IOException ignored) {
405            }
406            FileNotFoundException ex = new FileNotFoundException(path + ": " + errnoException.getMessage());
407            ex.initCause(errnoException);
408            throw ex;
409        }
410    }
411
412    /**
413     * java.io thinks that a read at EOF is an error and should return -1, contrary to traditional
414     * Unix practice where you'd read until you got 0 bytes (and any future read would return -1).
415     */
416    public static int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
417        Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
418        if (byteCount == 0) {
419            return 0;
420        }
421        try {
422            int readCount = Libcore.os.read(fd, bytes, byteOffset, byteCount);
423            if (readCount == 0) {
424                return -1;
425            }
426            return readCount;
427        } catch (ErrnoException errnoException) {
428            if (errnoException.errno == EAGAIN) {
429                // We return 0 rather than throw if we try to read from an empty non-blocking pipe.
430                return 0;
431            }
432            throw errnoException.rethrowAsIOException();
433        }
434    }
435
436    /**
437     * java.io always writes every byte it's asked to, or fails with an error. (That is, unlike
438     * Unix it never just writes as many bytes as happens to be convenient.)
439     */
440    public static void write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
441        Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
442        if (byteCount == 0) {
443            return;
444        }
445        try {
446            while (byteCount > 0) {
447                int bytesWritten = Libcore.os.write(fd, bytes, byteOffset, byteCount);
448                byteCount -= bytesWritten;
449                byteOffset += bytesWritten;
450            }
451        } catch (ErrnoException errnoException) {
452            throw errnoException.rethrowAsIOException();
453        }
454    }
455
456    public static int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws IOException {
457        boolean isDatagram = (inetAddress != null);
458        if (!isDatagram && byteCount <= 0) {
459            return 0;
460        }
461        int result;
462        try {
463            result = Libcore.os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port);
464        } catch (ErrnoException errnoException) {
465            result = maybeThrowAfterSendto(isDatagram, errnoException);
466        }
467        return result;
468    }
469
470    public static int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws IOException {
471        boolean isDatagram = (inetAddress != null);
472        if (!isDatagram && buffer.remaining() == 0) {
473            return 0;
474        }
475        int result;
476        try {
477            result = Libcore.os.sendto(fd, buffer, flags, inetAddress, port);
478        } catch (ErrnoException errnoException) {
479            result = maybeThrowAfterSendto(isDatagram, errnoException);
480        }
481        return result;
482    }
483
484    private static int maybeThrowAfterSendto(boolean isDatagram, ErrnoException errnoException) throws SocketException {
485        if (isDatagram) {
486            if (errnoException.errno == ECONNRESET || errnoException.errno == ECONNREFUSED) {
487                return 0;
488            }
489        } else {
490            if (errnoException.errno == EAGAIN) {
491                // We were asked to write to a non-blocking socket, but were told
492                // it would block, so report "no bytes written".
493                return 0;
494            }
495        }
496        throw errnoException.rethrowAsSocketException();
497    }
498
499    public static int recvfrom(boolean isRead, FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
500        int result;
501        try {
502            InetSocketAddress srcAddress = (packet != null && !isConnected) ? new InetSocketAddress() : null;
503            result = Libcore.os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
504            result = postRecvfrom(isRead, packet, isConnected, srcAddress, result);
505        } catch (ErrnoException errnoException) {
506            result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException);
507        }
508        return result;
509    }
510
511    public static int recvfrom(boolean isRead, FileDescriptor fd, ByteBuffer buffer, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
512        int result;
513        try {
514            InetSocketAddress srcAddress = (packet != null && !isConnected) ? new InetSocketAddress() : null;
515            result = Libcore.os.recvfrom(fd, buffer, flags, srcAddress);
516            result = postRecvfrom(isRead, packet, isConnected, srcAddress, result);
517        } catch (ErrnoException errnoException) {
518            result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException);
519        }
520        return result;
521    }
522
523    private static int postRecvfrom(boolean isRead, DatagramPacket packet, boolean isConnected, InetSocketAddress srcAddress, int byteCount) {
524        if (isRead && byteCount == 0) {
525            return -1;
526        }
527        if (packet != null) {
528            packet.setReceivedLength(byteCount);
529            if (!isConnected) {
530                packet.setAddress(srcAddress.getAddress());
531                packet.setPort(srcAddress.getPort());
532            }
533        }
534        return byteCount;
535    }
536
537    private static int maybeThrowAfterRecvfrom(boolean isRead, boolean isConnected, ErrnoException errnoException) throws SocketException, SocketTimeoutException {
538        if (isRead) {
539            if (errnoException.errno == EAGAIN) {
540                return 0;
541            } else {
542                throw errnoException.rethrowAsSocketException();
543            }
544        } else {
545            if (isConnected && errnoException.errno == ECONNREFUSED) {
546                throw new PortUnreachableException("", errnoException);
547            } else if (errnoException.errno == EAGAIN) {
548                throw new SocketTimeoutException(errnoException);
549            } else {
550                throw errnoException.rethrowAsSocketException();
551            }
552        }
553    }
554
555    public static FileDescriptor socket(boolean stream) throws SocketException {
556        FileDescriptor fd;
557        try {
558            fd = Libcore.os.socket(AF_INET6, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
559
560            // The RFC (http://www.ietf.org/rfc/rfc3493.txt) says that IPV6_MULTICAST_HOPS defaults
561            // to 1. The Linux kernel (at least up to 2.6.38) accidentally defaults to 64 (which
562            // would be correct for the *unicast* hop limit).
563            // See http://www.spinics.net/lists/netdev/msg129022.html, though no patch appears to
564            // have been applied as a result of that discussion. If that bug is ever fixed, we can
565            // remove this code. Until then, we manually set the hop limit on IPv6 datagram sockets.
566            // (IPv4 is already correct.)
567            if (!stream) {
568                Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1);
569            }
570
571            return fd;
572        } catch (ErrnoException errnoException) {
573            throw errnoException.rethrowAsSocketException();
574        }
575    }
576
577    public static InetAddress getSocketLocalAddress(FileDescriptor fd) throws SocketException {
578        try {
579            SocketAddress sa = Libcore.os.getsockname(fd);
580            InetSocketAddress isa = (InetSocketAddress) sa;
581            return isa.getAddress();
582        } catch (ErrnoException errnoException) {
583            throw errnoException.rethrowAsSocketException();
584        }
585    }
586
587    public static int getSocketLocalPort(FileDescriptor fd) throws SocketException {
588        try {
589            SocketAddress sa = Libcore.os.getsockname(fd);
590            InetSocketAddress isa = (InetSocketAddress) sa;
591            return isa.getPort();
592        } catch (ErrnoException errnoException) {
593            throw errnoException.rethrowAsSocketException();
594        }
595    }
596}
597