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 android.system.ErrnoException;
20 import android.system.StructGroupReq;
21 import android.system.StructGroupSourceReq;
22 import android.system.StructLinger;
23 import android.system.StructPollfd;
24 import android.system.StructTimeval;
25 import android.util.MutableInt;
26 import java.io.FileDescriptor;
27 import java.io.FileNotFoundException;
28 import java.io.IOException;
29 import java.net.BindException;
30 import java.net.ConnectException;
31 import java.net.DatagramPacket;
32 import java.net.Inet4Address;
33 import java.net.Inet6Address;
34 import java.net.InetAddress;
35 import java.net.InetSocketAddress;
36 import java.net.NetworkInterface;
37 import java.net.PortUnreachableException;
38 import java.net.SocketAddress;
39 import java.net.SocketException;
40 import java.net.SocketOptions;
41 import java.net.SocketTimeoutException;
42 import java.net.UnknownHostException;
43 import java.nio.ByteBuffer;
44 import java.util.Arrays;
45 import java.util.concurrent.TimeUnit;
46 
47 import static android.system.OsConstants.*;
48 
49 /**
50  * Implements java.io/java.net/java.nio semantics in terms of the underlying POSIX system calls.
51  */
52 public final class IoBridge {
53 
IoBridge()54     private IoBridge() {
55     }
56 
available(FileDescriptor fd)57     public static int available(FileDescriptor fd) throws IOException {
58         try {
59             MutableInt available = new MutableInt(0);
60             Libcore.os.ioctlInt(fd, FIONREAD, available);
61             if (available.value < 0) {
62                 // If the fd refers to a regular file, the result is the difference between
63                 // the file size and the file position. This may be negative if the position
64                 // is past the end of the file. If the fd refers to a special file masquerading
65                 // as a regular file, the result may be negative because the special file
66                 // may appear to have zero size and yet a previous read call may have
67                 // read some amount of data and caused the file position to be advanced.
68                 available.value = 0;
69             }
70             return available.value;
71         } catch (ErrnoException errnoException) {
72             if (errnoException.errno == ENOTTY) {
73                 // The fd is unwilling to opine about its read buffer.
74                 return 0;
75             }
76             throw errnoException.rethrowAsIOException();
77         }
78     }
79 
80 
bind(FileDescriptor fd, InetAddress address, int port)81     public static void bind(FileDescriptor fd, InetAddress address, int port) throws SocketException {
82         if (address instanceof Inet6Address) {
83             Inet6Address inet6Address = (Inet6Address) address;
84             if (inet6Address.getScopeId() == 0 && inet6Address.isLinkLocalAddress()) {
85                 // Linux won't let you bind a link-local address without a scope id.
86                 // Find one.
87                 NetworkInterface nif = NetworkInterface.getByInetAddress(address);
88                 if (nif == null) {
89                     throw new SocketException("Can't bind to a link-local address without a scope id: " + address);
90                 }
91                 try {
92                     address = Inet6Address.getByAddress(address.getHostName(), address.getAddress(), nif.getIndex());
93                 } catch (UnknownHostException ex) {
94                     throw new AssertionError(ex); // Can't happen.
95                 }
96             }
97         }
98         try {
99             Libcore.os.bind(fd, address, port);
100         } catch (ErrnoException errnoException) {
101             throw new BindException(errnoException.getMessage(), errnoException);
102         }
103     }
104 
105 
106     /**
107      * Connects socket 'fd' to 'inetAddress' on 'port', with no timeout. The lack of a timeout
108      * means this method won't throw SocketTimeoutException.
109      */
connect(FileDescriptor fd, InetAddress inetAddress, int port)110     public static void connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException {
111         try {
112             IoBridge.connect(fd, inetAddress, port, 0);
113         } catch (SocketTimeoutException ex) {
114             throw new AssertionError(ex); // Can't happen for a connect without a timeout.
115         }
116     }
117 
118     /**
119      * Connects socket 'fd' to 'inetAddress' on 'port', with a the given 'timeoutMs'.
120      * Use timeoutMs == 0 for a blocking connect with no timeout.
121      */
connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs)122     public static void connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException {
123         try {
124             connectErrno(fd, inetAddress, port, timeoutMs);
125         } catch (ErrnoException errnoException) {
126             throw new ConnectException(connectDetail(inetAddress, port, timeoutMs, errnoException), errnoException);
127         } catch (SocketException ex) {
128             throw ex; // We don't want to doubly wrap these.
129         } catch (SocketTimeoutException ex) {
130             throw ex; // We don't want to doubly wrap these.
131         } catch (IOException ex) {
132             throw new SocketException(ex);
133         }
134     }
135 
connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs)136     private static void connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws ErrnoException, IOException {
137         // With no timeout, just call connect(2) directly.
138         if (timeoutMs == 0) {
139             Libcore.os.connect(fd, inetAddress, port);
140             return;
141         }
142 
143         // For connect with a timeout, we:
144         //   1. set the socket to non-blocking,
145         //   2. connect(2),
146         //   3. loop using poll(2) to decide whether we're connected, whether we should keep
147         //      waiting, or whether we've seen a permanent failure and should give up,
148         //   4. set the socket back to blocking.
149 
150         // 1. set the socket to non-blocking.
151         IoUtils.setBlocking(fd, false);
152 
153         // 2. call connect(2) non-blocking.
154         long finishTimeNanos = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeoutMs);
155         try {
156             Libcore.os.connect(fd, inetAddress, port);
157             IoUtils.setBlocking(fd, true); // 4. set the socket back to blocking.
158             return; // We connected immediately.
159         } catch (ErrnoException errnoException) {
160             if (errnoException.errno != EINPROGRESS) {
161                 throw errnoException;
162             }
163             // EINPROGRESS means we should keep trying...
164         }
165 
166         // 3. loop using poll(2).
167         int remainingTimeoutMs;
168         do {
169             remainingTimeoutMs =
170                     (int) TimeUnit.NANOSECONDS.toMillis(finishTimeNanos - System.nanoTime());
171             if (remainingTimeoutMs <= 0) {
172                 throw new SocketTimeoutException(connectDetail(inetAddress, port, timeoutMs, null));
173             }
174         } while (!IoBridge.isConnected(fd, inetAddress, port, timeoutMs, remainingTimeoutMs));
175         IoUtils.setBlocking(fd, true); // 4. set the socket back to blocking.
176     }
177 
connectDetail(InetAddress inetAddress, int port, int timeoutMs, ErrnoException cause)178     private static String connectDetail(InetAddress inetAddress, int port, int timeoutMs, ErrnoException cause) {
179         String detail = "failed to connect to " + inetAddress + " (port " + port + ")";
180         if (timeoutMs > 0) {
181             detail += " after " + timeoutMs + "ms";
182         }
183         if (cause != null) {
184             detail += ": " + cause.getMessage();
185         }
186         return detail;
187     }
188 
189     /**
190      * Closes the supplied file descriptor and sends a signal to any threads are currently blocking.
191      * In order for the signal to be sent the blocked threads must have registered with
192      * the AsynchronousCloseMonitor before they entered the blocking operation.
193      *
194      * <p>This method is a no-op if passed a {@code null} or already-closed file descriptor.
195      */
closeAndSignalBlockedThreads(FileDescriptor fd)196     public static void closeAndSignalBlockedThreads(FileDescriptor fd) throws IOException {
197         if (fd == null || !fd.valid()) {
198             return;
199         }
200         int intFd = fd.getInt$();
201         fd.setInt$(-1);
202         FileDescriptor oldFd = new FileDescriptor();
203         oldFd.setInt$(intFd);
204         AsynchronousCloseMonitor.signalBlockedThreads(oldFd);
205         try {
206             Libcore.os.close(oldFd);
207         } catch (ErrnoException errnoException) {
208             // TODO: are there any cases in which we should throw?
209         }
210     }
211 
isConnected(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs, int remainingTimeoutMs)212     public static boolean isConnected(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs, int remainingTimeoutMs) throws IOException {
213         ErrnoException cause;
214         try {
215             StructPollfd[] pollFds = new StructPollfd[] { new StructPollfd() };
216             pollFds[0].fd = fd;
217             pollFds[0].events = (short) POLLOUT;
218             int rc = Libcore.os.poll(pollFds, remainingTimeoutMs);
219             if (rc == 0) {
220                 return false; // Timeout.
221             }
222             int connectError = Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_ERROR);
223             if (connectError == 0) {
224                 return true; // Success!
225             }
226             throw new ErrnoException("isConnected", connectError); // The connect(2) failed.
227         } catch (ErrnoException errnoException) {
228             if (!fd.valid()) {
229                 throw new SocketException("Socket closed");
230             }
231             cause = errnoException;
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      */
getSocketOption(FileDescriptor fd, int option)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 
getSocketOptionErrno(FileDescriptor fd, int option)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 
booleanFromInt(int i)308     private static boolean booleanFromInt(int i) {
309         return (i != 0);
310     }
311 
booleanToInt(boolean b)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      */
setSocketOption(FileDescriptor fd, int option, Object value)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 
setSocketOptionErrno(FileDescriptor fd, int option, Object value)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 
getGroupSourceReqOp(int javaValue)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      */
open(String path, int flags)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      */
read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount)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      */
write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount)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 
sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port)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 
sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port)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 
maybeThrowAfterSendto(boolean isDatagram, ErrnoException errnoException)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 
recvfrom(boolean isRead, FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, DatagramPacket packet, boolean isConnected)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 
recvfrom(boolean isRead, FileDescriptor fd, ByteBuffer buffer, int flags, DatagramPacket packet, boolean isConnected)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 
postRecvfrom(boolean isRead, DatagramPacket packet, boolean isConnected, InetSocketAddress srcAddress, int byteCount)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 
maybeThrowAfterRecvfrom(boolean isRead, boolean isConnected, ErrnoException errnoException)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 
socket(boolean stream)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 
getSocketLocalAddress(FileDescriptor fd)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 
getSocketLocalPort(FileDescriptor fd)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