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