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