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