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