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