Home | History | Annotate | Download | only in io
      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 
     17 package libcore.io;
     18 
     19 import java.io.FileDescriptor;
     20 import java.io.FileNotFoundException;
     21 import java.io.IOException;
     22 import java.net.BindException;
     23 import java.net.ConnectException;
     24 import java.net.DatagramPacket;
     25 import java.net.Inet4Address;
     26 import java.net.Inet6Address;
     27 import java.net.InetAddress;
     28 import java.net.InetSocketAddress;
     29 import java.net.NetworkInterface;
     30 import java.net.PortUnreachableException;
     31 import java.net.SocketAddress;
     32 import java.net.SocketException;
     33 import java.net.SocketOptions;
     34 import java.net.SocketTimeoutException;
     35 import java.net.UnknownHostException;
     36 import java.nio.ByteBuffer;
     37 import java.util.Arrays;
     38 import static libcore.io.OsConstants.*;
     39 import libcore.util.MutableInt;
     40 
     41 /**
     42  * Implements java.io/java.net/java.nio semantics in terms of the underlying POSIX system calls.
     43  */
     44 public 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 boolean connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException {
     99         try {
    100             return 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 boolean connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException {
    111         try {
    112             return 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 boolean 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 true;
    129         }
    130 
    131         // With a timeout, we set the socket to non-blocking, connect(2), and then loop
    132         // using poll(2) to decide whether we're connected, whether we should keep waiting,
    133         // or whether we've seen a permanent failure and should give up.
    134         long finishTimeMs = System.currentTimeMillis() + timeoutMs;
    135         IoUtils.setBlocking(fd, false);
    136         try {
    137             try {
    138                 Libcore.os.connect(fd, inetAddress, port);
    139                 return true; // We connected immediately.
    140             } catch (ErrnoException errnoException) {
    141                 if (errnoException.errno != EINPROGRESS) {
    142                     throw errnoException;
    143                 }
    144                 // EINPROGRESS means we should keep trying...
    145             }
    146             int remainingTimeoutMs;
    147             do {
    148                 remainingTimeoutMs = (int) (finishTimeMs - System.currentTimeMillis());
    149                 if (remainingTimeoutMs <= 0) {
    150                     throw new SocketTimeoutException(connectDetail(inetAddress, port, timeoutMs, null));
    151                 }
    152             } while (!IoBridge.isConnected(fd, inetAddress, port, timeoutMs, remainingTimeoutMs));
    153             return true; // Or we'd have thrown.
    154         } finally {
    155             IoUtils.setBlocking(fd, true);
    156         }
    157     }
    158 
    159     private static String connectDetail(InetAddress inetAddress, int port, int timeoutMs, ErrnoException cause) {
    160         String detail = "failed to connect to " + inetAddress + " (port " + port + ")";
    161         if (timeoutMs > 0) {
    162             detail += " after " + timeoutMs + "ms";
    163         }
    164         if (cause != null) {
    165             detail += ": " + cause.getMessage();
    166         }
    167         return detail;
    168     }
    169 
    170     public static void closeSocket(FileDescriptor fd) throws IOException {
    171         if (!fd.valid()) {
    172             // Socket.close doesn't throw if you try to close an already-closed socket.
    173             return;
    174         }
    175         int intFd = fd.getInt$();
    176         fd.setInt$(-1);
    177         FileDescriptor oldFd = new FileDescriptor();
    178         oldFd.setInt$(intFd);
    179         AsynchronousCloseMonitor.signalBlockedThreads(oldFd);
    180         try {
    181             Libcore.os.close(oldFd);
    182         } catch (ErrnoException errnoException) {
    183             // TODO: are there any cases in which we should throw?
    184         }
    185     }
    186 
    187     public static boolean isConnected(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs, int remainingTimeoutMs) throws IOException {
    188         ErrnoException cause;
    189         try {
    190             StructPollfd[] pollFds = new StructPollfd[] { new StructPollfd() };
    191             pollFds[0].fd = fd;
    192             pollFds[0].events = (short) POLLOUT;
    193             int rc = Libcore.os.poll(pollFds, remainingTimeoutMs);
    194             if (rc == 0) {
    195                 return false; // Timeout.
    196             }
    197             int connectError = Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_ERROR);
    198             if (connectError == 0) {
    199                 return true; // Success!
    200             }
    201             throw new ErrnoException("isConnected", connectError); // The connect(2) failed.
    202         } catch (ErrnoException errnoException) {
    203             if (errnoException.errno == EINTR) {
    204                 return false; // Punt and ask the caller to try again.
    205             } else {
    206                 cause = errnoException;
    207             }
    208         }
    209         // TODO: is it really helpful/necessary to throw so many different exceptions?
    210         String detail = connectDetail(inetAddress, port, timeoutMs, cause);
    211         if (cause.errno == ECONNRESET || cause.errno == ECONNREFUSED ||
    212         cause.errno == EADDRNOTAVAIL || cause.errno == EADDRINUSE ||
    213         cause.errno == ENETUNREACH) {
    214             throw new ConnectException(detail, cause);
    215         } else if (cause.errno == EACCES) {
    216             throw new SecurityException(detail, cause);
    217         } else if (cause.errno == ETIMEDOUT) {
    218             throw new SocketTimeoutException(detail, cause);
    219         }
    220         throw new SocketException(detail, cause);
    221     }
    222 
    223     // Socket options used by java.net but not exposed in SocketOptions.
    224     public static final int JAVA_MCAST_JOIN_GROUP = 19;
    225     public static final int JAVA_MCAST_LEAVE_GROUP = 20;
    226     public static final int JAVA_IP_MULTICAST_TTL = 17;
    227 
    228     /**
    229      * java.net has its own socket options similar to the underlying Unix ones. We paper over the
    230      * differences here.
    231      */
    232     public static Object getSocketOption(FileDescriptor fd, int option) throws SocketException {
    233         try {
    234             return getSocketOptionErrno(fd, option);
    235         } catch (ErrnoException errnoException) {
    236             throw errnoException.rethrowAsSocketException();
    237         }
    238     }
    239 
    240     private static Object getSocketOptionErrno(FileDescriptor fd, int option) throws ErrnoException, SocketException {
    241         switch (option) {
    242         case SocketOptions.IP_MULTICAST_IF:
    243             // This is IPv4-only.
    244             return Libcore.os.getsockoptInAddr(fd, IPPROTO_IP, IP_MULTICAST_IF);
    245         case SocketOptions.IP_MULTICAST_IF2:
    246             // This is IPv6-only.
    247             return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF);
    248         case SocketOptions.IP_MULTICAST_LOOP:
    249             // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
    250             // it doesn't matter which we return.
    251             return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP));
    252         case IoBridge.JAVA_IP_MULTICAST_TTL:
    253             // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
    254             // it doesn't matter which we return.
    255             return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
    256         case SocketOptions.IP_TOS:
    257             // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
    258             // it doesn't matter which we return.
    259             return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS);
    260         case SocketOptions.SO_BROADCAST:
    261             return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_BROADCAST));
    262         case SocketOptions.SO_KEEPALIVE:
    263             return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE));
    264         case SocketOptions.SO_LINGER:
    265             StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER);
    266             if (!linger.isOn()) {
    267                 return false;
    268             }
    269             return linger.l_linger;
    270         case SocketOptions.SO_OOBINLINE:
    271             return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE));
    272         case SocketOptions.SO_RCVBUF:
    273             return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF);
    274         case SocketOptions.SO_REUSEADDR:
    275             return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR));
    276         case SocketOptions.SO_SNDBUF:
    277             return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF);
    278         case SocketOptions.SO_TIMEOUT:
    279             return (int) Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO).toMillis();
    280         case SocketOptions.TCP_NODELAY:
    281             return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY));
    282         default:
    283             throw new SocketException("Unknown socket option: " + option);
    284         }
    285     }
    286 
    287     private static boolean booleanFromInt(int i) {
    288         return (i != 0);
    289     }
    290 
    291     private static int booleanToInt(boolean b) {
    292         return b ? 1 : 0;
    293     }
    294 
    295     /**
    296      * java.net has its own socket options similar to the underlying Unix ones. We paper over the
    297      * differences here.
    298      */
    299     public static void setSocketOption(FileDescriptor fd, int option, Object value) throws SocketException {
    300         try {
    301             setSocketOptionErrno(fd, option, value);
    302         } catch (ErrnoException errnoException) {
    303             throw errnoException.rethrowAsSocketException();
    304         }
    305     }
    306 
    307     private static void setSocketOptionErrno(FileDescriptor fd, int option, Object value) throws ErrnoException, SocketException {
    308         switch (option) {
    309         case SocketOptions.IP_MULTICAST_IF:
    310             throw new UnsupportedOperationException("Use IP_MULTICAST_IF2 on Android");
    311         case SocketOptions.IP_MULTICAST_IF2:
    312             // Although IPv6 was cleaned up to use int, IPv4 uses an ip_mreqn containing an int.
    313             Libcore.os.setsockoptIpMreqn(fd, IPPROTO_IP, IP_MULTICAST_IF, (Integer) value);
    314             Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (Integer) value);
    315             return;
    316         case SocketOptions.IP_MULTICAST_LOOP:
    317             // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte.
    318             Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_LOOP, booleanToInt((Boolean) value));
    319             Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, booleanToInt((Boolean) value));
    320             return;
    321         case IoBridge.JAVA_IP_MULTICAST_TTL:
    322             // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int,
    323             // IPv4 multicast TTL uses a byte.
    324             Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_TTL, (Integer) value);
    325             Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (Integer) value);
    326             return;
    327         case SocketOptions.IP_TOS:
    328             Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_TOS, (Integer) value);
    329             Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS, (Integer) value);
    330             return;
    331         case SocketOptions.SO_BROADCAST:
    332             Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_BROADCAST, booleanToInt((Boolean) value));
    333             return;
    334         case SocketOptions.SO_KEEPALIVE:
    335             Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE, booleanToInt((Boolean) value));
    336             return;
    337         case SocketOptions.SO_LINGER:
    338             boolean on = false;
    339             int seconds = 0;
    340             if (value instanceof Integer) {
    341                 on = true;
    342                 seconds = Math.min((Integer) value, 65535);
    343             }
    344             StructLinger linger = new StructLinger(booleanToInt(on), seconds);
    345             Libcore.os.setsockoptLinger(fd, SOL_SOCKET, SO_LINGER, linger);
    346             return;
    347         case SocketOptions.SO_OOBINLINE:
    348             Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE, booleanToInt((Boolean) value));
    349             return;
    350         case SocketOptions.SO_RCVBUF:
    351             Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, (Integer) value);
    352             return;
    353         case SocketOptions.SO_REUSEADDR:
    354             Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR, booleanToInt((Boolean) value));
    355             return;
    356         case SocketOptions.SO_SNDBUF:
    357             Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_SNDBUF, (Integer) value);
    358             return;
    359         case SocketOptions.SO_TIMEOUT:
    360             int millis = (Integer) value;
    361             StructTimeval tv = StructTimeval.fromMillis(millis);
    362             Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv);
    363             return;
    364         case SocketOptions.TCP_NODELAY:
    365             Libcore.os.setsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY, booleanToInt((Boolean) value));
    366             return;
    367         case IoBridge.JAVA_MCAST_JOIN_GROUP:
    368         case IoBridge.JAVA_MCAST_LEAVE_GROUP:
    369             StructGroupReq groupReq = (StructGroupReq) value;
    370             int level = (groupReq.gr_group instanceof Inet4Address) ? IPPROTO_IP : IPPROTO_IPV6;
    371             int op = (option == JAVA_MCAST_JOIN_GROUP) ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP;
    372             Libcore.os.setsockoptGroupReq(fd, level, op, groupReq);
    373             return;
    374         default:
    375             throw new SocketException("Unknown socket option: " + option);
    376         }
    377     }
    378 
    379     /**
    380      * java.io only throws FileNotFoundException when opening files, regardless of what actually
    381      * went wrong. Additionally, java.io is more restrictive than POSIX when it comes to opening
    382      * directories: POSIX says read-only is okay, but java.io doesn't even allow that. We also
    383      * have an Android-specific hack to alter the default permissions.
    384      */
    385     public static FileDescriptor open(String path, int flags) throws FileNotFoundException {
    386         FileDescriptor fd = null;
    387         try {
    388             // On Android, we don't want default permissions to allow global access.
    389             int mode = ((flags & O_ACCMODE) == O_RDONLY) ? 0 : 0600;
    390             fd = Libcore.os.open(path, flags, mode);
    391             if (fd.valid()) {
    392                 // Posix open(2) fails with EISDIR only if you ask for write permission.
    393                 // Java disallows reading directories too.
    394                 if (S_ISDIR(Libcore.os.fstat(fd).st_mode)) {
    395                     throw new ErrnoException("open", EISDIR);
    396                 }
    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 || errnoException.errno == EWOULDBLOCK) {
    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.setLength(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 || errnoException.errno == EWOULDBLOCK) {
    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 || errnoException.errno == EWOULDBLOCK) {
    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) {
    578         try {
    579             SocketAddress sa = Libcore.os.getsockname(fd);
    580             InetSocketAddress isa = (InetSocketAddress) sa;
    581             return isa.getAddress();
    582         } catch (ErrnoException errnoException) {
    583             throw new AssertionError(errnoException);
    584         }
    585     }
    586 
    587     public static int getSocketLocalPort(FileDescriptor fd) {
    588         try {
    589             SocketAddress sa = Libcore.os.getsockname(fd);
    590             InetSocketAddress isa = (InetSocketAddress) sa;
    591             return isa.getPort();
    592         } catch (ErrnoException errnoException) {
    593             throw new AssertionError(errnoException);
    594         }
    595     }
    596 }
    597