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