• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.SystemApi;
20 import android.compat.annotation.UnsupportedAppUsage;
21 import android.system.ErrnoException;
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.Socket;
41 import java.net.SocketAddress;
42 import java.net.SocketException;
43 import java.net.SocketOptions;
44 import java.net.SocketTimeoutException;
45 import java.net.UnknownHostException;
46 import java.nio.ByteBuffer;
47 import java.util.concurrent.TimeUnit;
48 
49 import libcore.util.ArrayUtils;
50 import libcore.util.NonNull;
51 import libcore.util.Nullable;
52 
53 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
54 import static android.system.OsConstants.*;
55 
56 /**
57  * Collection of utility methods to work with blocking and non-blocking I/O that wrap raw POSIX
58  * system calls, e.g. {@link android.system.Os}. These wrappers are to signal other blocked I/O
59  * threads and avoid boilerplate code of routine error checks when using raw system calls.
60  *
61  * <p>
62  * For example, when using {@link Os#read(FileDescriptor, byte[], int, int)}, return value can
63  * contain:
64  * <ul>
65  *   <li>{@code 0} which means EOF</li>
66  *   <li>{@code N > 0} which means number of bytes read</li>
67  *   <li>{@code -1} which means error, and {@link ErrnoException} is thrown</li>
68  * </ul>
69  *
70  * <p>
71  * {@link ErrnoException} in its turn can be one of:
72  * <ul>
73  *   <li>{@link android.system.OsConstants#EAGAIN} which means the file descriptor refers to a file
74  *       or a socket, which has been marked nonblocking
75  *       ({@link android.system.OsConstants#O_NONBLOCK}), and the read would block</li>
76  *   <li>{@link android.system.OsConstants#EBADF} which means the file descriptor is not a valid
77  *       file descriptor or is not open for reading</li>
78  *   <li>{@link android.system.OsConstants#EFAULT} which means given buffer is outside accessible
79  *       address space</li>
80  *   <li>{@link android.system.OsConstants#EINTR} which means the call was interrupted by a signal
81  *       before any data was read</li>
82  *   <li>{@link android.system.OsConstants#EINVAL} which means the file descriptor is attached to
83  *       an object which is unsuitable for reading; or the file was opened with the
84  *       {@link android.system.OsConstants#O_DIRECT} flag, and either the address specified in
85  *       {@code buffer}, the value specified in {@code count}, or the file {@code offset} is not
86  *       suitably aligned</li>
87  *   <li>{@link android.system.OsConstants#EIO} which means I/O error happened</li>
88  *   <li>{@link android.system.OsConstants#EISDIR} which means the file descriptor refers to a
89  *       directory</li>
90  * </ul>
91  *
92  * All these errors require handling, and this class contains some wrapper methods that handle most
93  * common cases, making usage of system calls more user friendly.
94  *
95  * @hide
96  */
97 @SystemApi(client = MODULE_LIBRARIES)
98 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
99 public final class IoBridge {
100 
IoBridge()101     private IoBridge() {
102     }
103 
104     /** @hide */
available(FileDescriptor fd)105     public static int available(FileDescriptor fd) throws IOException {
106         try {
107             int available = Libcore.os.ioctlInt(fd, FIONREAD);
108             if (available < 0) {
109                 // If the fd refers to a regular file, the result is the difference between
110                 // the file size and the file position. This may be negative if the position
111                 // is past the end of the file. If the fd refers to a special file masquerading
112                 // as a regular file, the result may be negative because the special file
113                 // may appear to have zero size and yet a previous read call may have
114                 // read some amount of data and caused the file position to be advanced.
115                 available = 0;
116             }
117             return available;
118         } catch (ErrnoException errnoException) {
119             if (errnoException.errno == ENOTTY) {
120                 // The fd is unwilling to opine about its read buffer.
121                 return 0;
122             }
123             throw errnoException.rethrowAsIOException();
124         }
125     }
126 
127     /** @hide */
bind(FileDescriptor fd, InetAddress address, int port)128     public static void bind(FileDescriptor fd, InetAddress address, int port) throws SocketException {
129         if (address instanceof Inet6Address) {
130             Inet6Address inet6Address = (Inet6Address) address;
131             if (inet6Address.getScopeId() == 0 && inet6Address.isLinkLocalAddress()) {
132                 // Linux won't let you bind a link-local address without a scope id.
133                 // Find one.
134                 NetworkInterface nif = NetworkInterface.getByInetAddress(address);
135                 if (nif == null) {
136                     throw new SocketException("Can't bind to a link-local address without a scope id: " + address);
137                 }
138                 try {
139                     address = Inet6Address.getByAddress(address.getHostName(), address.getAddress(), nif.getIndex());
140                 } catch (UnknownHostException ex) {
141                     throw new AssertionError(ex); // Can't happen.
142                 }
143             }
144         }
145         try {
146             Libcore.os.bind(fd, address, port);
147         } catch (ErrnoException errnoException) {
148             if (errnoException.errno == EADDRINUSE || errnoException.errno == EADDRNOTAVAIL ||
149                 errnoException.errno == EPERM || errnoException.errno == EACCES) {
150                 throw new BindException(errnoException.getMessage(), errnoException);
151             } else {
152                 throw new SocketException(errnoException.getMessage(), errnoException);
153             }
154         }
155     }
156 
157 
158     /**
159      * Connects socket 'fd' to 'inetAddress' on 'port', with no timeout. The lack of a timeout
160      * means this method won't throw SocketTimeoutException.
161      *
162      * @hide
163      */
connect(FileDescriptor fd, InetAddress inetAddress, int port)164     public static void connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException {
165         try {
166             IoBridge.connect(fd, inetAddress, port, 0);
167         } catch (SocketTimeoutException ex) {
168             throw new AssertionError(ex); // Can't happen for a connect without a timeout.
169         }
170     }
171 
172     /**
173      * Connects socket 'fd' to 'inetAddress' on 'port', with a the given 'timeoutMs'.
174      * Use timeoutMs == 0 for a blocking connect with no timeout.
175      *
176      * @hide
177      */
connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs)178     public static void connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException {
179         try {
180             connectErrno(fd, inetAddress, port, timeoutMs);
181         } catch (ErrnoException errnoException) {
182             if (errnoException.errno == EHOSTUNREACH) {
183                 throw new NoRouteToHostException("Host unreachable");
184             }
185             if (errnoException.errno == EADDRNOTAVAIL) {
186                 throw new NoRouteToHostException("Address not available");
187             }
188             throw new ConnectException(createMessageForException(fd, inetAddress, port, timeoutMs,
189                     errnoException), errnoException);
190         } catch (SocketException ex) {
191             throw ex; // We don't want to doubly wrap these.
192         } catch (SocketTimeoutException ex) {
193             throw ex; // We don't want to doubly wrap these.
194         } catch (IOException ex) {
195             throw new SocketException(ex);
196         }
197     }
198 
connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs)199     private static void connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws ErrnoException, IOException {
200         // With no timeout, just call connect(2) directly.
201         if (timeoutMs <= 0) {
202             Libcore.os.connect(fd, inetAddress, port);
203             return;
204         }
205 
206         // For connect with a timeout, we:
207         //   1. set the socket to non-blocking,
208         //   2. connect(2),
209         //   3. loop using poll(2) to decide whether we're connected, whether we should keep
210         //      waiting, or whether we've seen a permanent failure and should give up,
211         //   4. set the socket back to blocking.
212 
213         // 1. set the socket to non-blocking.
214         IoUtils.setBlocking(fd, false);
215 
216         // 2. call connect(2) non-blocking.
217         long finishTimeNanos = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeoutMs);
218         try {
219             Libcore.os.connect(fd, inetAddress, port);
220             IoUtils.setBlocking(fd, true); // 4. set the socket back to blocking.
221             return; // We connected immediately.
222         } catch (ErrnoException errnoException) {
223             if (errnoException.errno != EINPROGRESS) {
224                 throw errnoException;
225             }
226             // EINPROGRESS means we should keep trying...
227         }
228 
229         // 3. loop using poll(2).
230         int remainingTimeoutMs;
231         do {
232             remainingTimeoutMs =
233                     (int) TimeUnit.NANOSECONDS.toMillis(finishTimeNanos - System.nanoTime());
234             if (remainingTimeoutMs <= 0) {
235                 throw new SocketTimeoutException(
236                         createMessageForException(fd, inetAddress, port, timeoutMs, null));
237             }
238         } while (!IoBridge.isConnected(fd, inetAddress, port, timeoutMs, remainingTimeoutMs));
239         IoUtils.setBlocking(fd, true); // 4. set the socket back to blocking.
240     }
241 
242     /**
243      * Constructs the message for an exception that the caller is about to throw.
244      *
245      * @hide
246      */
createMessageForException(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs, Exception causeOrNull)247     private static String createMessageForException(FileDescriptor fd, InetAddress inetAddress,
248             int port, int timeoutMs, Exception causeOrNull) {
249         // Figure out source address from fd.
250         InetSocketAddress localAddress = null;
251         try {
252             localAddress = getLocalInetSocketAddress(fd);
253         } catch (SocketException ignored) {
254             // The caller is about to throw an exception, so this one would only distract.
255         }
256 
257         StringBuilder sb = new StringBuilder("failed to connect")
258               .append(" to ")
259               .append(inetAddress)
260               .append(" (port ")
261               .append(port)
262               .append(")");
263         if (localAddress != null) {
264             sb.append(" from ")
265               .append(localAddress.getAddress())
266               .append(" (port ")
267               .append(localAddress.getPort())
268               .append(")");
269         }
270         if (timeoutMs > 0) {
271             sb.append(" after ")
272               .append(timeoutMs)
273               .append("ms");
274         }
275         if (causeOrNull != null) {
276             sb.append(": ")
277               .append(causeOrNull.getMessage());
278         }
279         return sb.toString();
280     }
281 
282     /**
283      * Closes the Unix file descriptor associated with the supplied file descriptor, resets the
284      * internal int to -1, and sends a signal to any threads are currently blocking. In order for
285      * the signal to be sent the blocked threads must have registered with the
286      * {@link AsynchronousCloseMonitor} before they entered the blocking operation. {@code fd} will be
287      * invalid after this call.
288      *
289      * <p>This method is a no-op if passed a {@code null} or already-closed file descriptor.
290      *
291      * @param fd file descriptor to be closed
292      * @throws IOException if underlying system call fails with {@link ErrnoException}
293      *
294      * @hide
295      */
296     @SystemApi(client = MODULE_LIBRARIES)
297     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
closeAndSignalBlockedThreads(@onNull FileDescriptor fd)298     public static void closeAndSignalBlockedThreads(@NonNull FileDescriptor fd) throws IOException {
299         if (fd == null) {
300             return;
301         }
302 
303         // fd is invalid after we call release$.
304         // If multiple threads reach this point simultaneously, release$ is synchronized, so one
305         // of them will receive the old fd, and the rest will get an empty FileDescriptor.
306         FileDescriptor oldFd = fd.release$();
307         if (!oldFd.valid()) {
308             return;
309         }
310 
311         AsynchronousCloseMonitor.signalBlockedThreads(oldFd);
312         try {
313             Libcore.os.close(oldFd);
314         } catch (ErrnoException errnoException) {
315             throw errnoException.rethrowAsIOException();
316         }
317     }
318 
319     /** @hide */
320     @UnsupportedAppUsage
isConnected(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs, int remainingTimeoutMs)321     public static boolean isConnected(FileDescriptor fd, InetAddress inetAddress, int port,
322             int timeoutMs, int remainingTimeoutMs) throws IOException {
323         ErrnoException cause;
324         try {
325             StructPollfd[] pollFds = new StructPollfd[] { new StructPollfd() };
326             pollFds[0].fd = fd;
327             pollFds[0].events = (short) POLLOUT;
328             int rc = Libcore.os.poll(pollFds, remainingTimeoutMs);
329             if (rc == 0) {
330                 return false; // Timeout.
331             }
332             int connectError = Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_ERROR);
333             if (connectError == 0) {
334                 return true; // Success!
335             }
336             throw new ErrnoException("isConnected", connectError); // The connect(2) failed.
337         } catch (ErrnoException errnoException) {
338             if (!fd.valid()) {
339                 throw new SocketException("Socket closed");
340             }
341             cause = errnoException;
342         }
343         String detail = createMessageForException(fd, inetAddress, port, timeoutMs, cause);
344         if (cause.errno == ETIMEDOUT) {
345             SocketTimeoutException e = new SocketTimeoutException(detail);
346             e.initCause(cause);
347             throw e;
348         }
349         throw new ConnectException(detail, cause);
350     }
351 
352     // Socket options used by java.net but not exposed in SocketOptions.
353     /** @hide */
354     public static final int JAVA_MCAST_JOIN_GROUP = 19;
355     /** @hide */
356     public static final int JAVA_MCAST_LEAVE_GROUP = 20;
357     /** @hide */
358     public static final int JAVA_IP_MULTICAST_TTL = 17;
359     /** @hide */
360     public static final int JAVA_IP_TTL = 25;
361 
362     /**
363      * java.net has its own socket options similar to the underlying Unix ones. We paper over the
364      * differences here.
365      * @hide
366      */
getSocketOption(FileDescriptor fd, int option)367     public static Object getSocketOption(FileDescriptor fd, int option) throws SocketException {
368         try {
369             return getSocketOptionErrno(fd, option);
370         } catch (ErrnoException errnoException) {
371             throw errnoException.rethrowAsSocketException();
372         }
373     }
374 
getSocketOptionErrno(FileDescriptor fd, int option)375     private static Object getSocketOptionErrno(FileDescriptor fd, int option) throws ErrnoException, SocketException {
376         switch (option) {
377         case SocketOptions.IP_MULTICAST_IF:
378         case SocketOptions.IP_MULTICAST_IF2:
379             return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF);
380         case SocketOptions.IP_MULTICAST_LOOP:
381             // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
382             // it doesn't matter which we return.
383             // NOTE: getsockopt's return value means "isEnabled", while OpenJDK code java.net
384             // requires a value that means "isDisabled" so we NEGATE the system call value here.
385             return !booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP));
386         case IoBridge.JAVA_IP_MULTICAST_TTL:
387             // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
388             // it doesn't matter which we return.
389             return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
390         case IoBridge.JAVA_IP_TTL:
391             // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
392             // it doesn't matter which we return.
393             return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS);
394         case SocketOptions.IP_TOS:
395             // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
396             // it doesn't matter which we return.
397             return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS);
398         case SocketOptions.SO_BROADCAST:
399             return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_BROADCAST));
400         case SocketOptions.SO_KEEPALIVE:
401             return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE));
402         case SocketOptions.SO_LINGER:
403             StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER);
404             if (!linger.isOn()) {
405                 return false;
406             }
407             return linger.l_linger;
408         case SocketOptions.SO_OOBINLINE:
409             return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE));
410         case SocketOptions.SO_RCVBUF:
411             return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_RCVBUF);
412         case SocketOptions.SO_REUSEADDR:
413             return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR));
414         case SocketOptions.SO_SNDBUF:
415             return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF);
416         case SocketOptions.SO_TIMEOUT:
417             return (int) Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO).toMillis();
418         case SocketOptions.TCP_NODELAY:
419             return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY));
420         case SocketOptions.SO_BINDADDR:
421             return ((InetSocketAddress) Libcore.os.getsockname(fd)).getAddress();
422         default:
423             throw new SocketException("Unknown socket option: " + option);
424         }
425     }
426 
booleanFromInt(int i)427     private static boolean booleanFromInt(int i) {
428         return (i != 0);
429     }
430 
booleanToInt(boolean b)431     private static int booleanToInt(boolean b) {
432         return b ? 1 : 0;
433     }
434 
435     /**
436      * java.net has its own socket options similar to the underlying Unix ones. We paper over the
437      * differences here.
438      *
439      * @hide
440      */
setSocketOption(FileDescriptor fd, int option, Object value)441     public static void setSocketOption(FileDescriptor fd, int option, Object value) throws SocketException {
442         try {
443             setSocketOptionErrno(fd, option, value);
444         } catch (ErrnoException errnoException) {
445             throw errnoException.rethrowAsSocketException();
446         }
447     }
448 
setSocketOptionErrno(FileDescriptor fd, int option, Object value)449     private static void setSocketOptionErrno(FileDescriptor fd, int option, Object value) throws ErrnoException, SocketException {
450         switch (option) {
451         case SocketOptions.IP_MULTICAST_IF:
452             NetworkInterface nif = NetworkInterface.getByInetAddress((InetAddress) value);
453             if (nif == null) {
454                 throw new SocketException(
455                         "bad argument for IP_MULTICAST_IF : address not bound to any interface");
456             }
457             // Although IPv6 was cleaned up to use int, IPv4 uses an ip_mreqn containing an int.
458             Libcore.os.setsockoptIpMreqn(fd, IPPROTO_IP, IP_MULTICAST_IF, nif.getIndex());
459             Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, nif.getIndex());
460             return;
461         case SocketOptions.IP_MULTICAST_IF2:
462             // Although IPv6 was cleaned up to use int, IPv4 uses an ip_mreqn containing an int.
463             Libcore.os.setsockoptIpMreqn(fd, IPPROTO_IP, IP_MULTICAST_IF, (Integer) value);
464             Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (Integer) value);
465             return;
466         case SocketOptions.IP_MULTICAST_LOOP:
467             // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte.
468             // NOTE: setsockopt's arguement value means "isEnabled", while OpenJDK code java.net
469             // uses a value that means "isDisabled" so we NEGATE the system call value here.
470             int enable = booleanToInt(!((Boolean) value));
471             Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_LOOP, enable);
472             Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, enable);
473             return;
474         case IoBridge.JAVA_IP_MULTICAST_TTL:
475             // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int,
476             // IPv4 multicast TTL uses a byte.
477             Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_TTL, (Integer) value);
478             Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (Integer) value);
479             return;
480         case IoBridge.JAVA_IP_TTL:
481             Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_TTL, (Integer) value);
482             Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (Integer) value);
483             return;
484         case SocketOptions.IP_TOS:
485             Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_TOS, (Integer) value);
486             Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS, (Integer) value);
487             return;
488         case SocketOptions.SO_BROADCAST:
489             Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_BROADCAST, booleanToInt((Boolean) value));
490             return;
491         case SocketOptions.SO_KEEPALIVE:
492             Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE, booleanToInt((Boolean) value));
493             return;
494         case SocketOptions.SO_LINGER:
495             boolean on = false;
496             int seconds = 0;
497             if (value instanceof Integer) {
498                 on = true;
499                 seconds = Math.min((Integer) value, 65535);
500             }
501             StructLinger linger = new StructLinger(booleanToInt(on), seconds);
502             Libcore.os.setsockoptLinger(fd, SOL_SOCKET, SO_LINGER, linger);
503             return;
504         case SocketOptions.SO_OOBINLINE:
505             Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE, booleanToInt((Boolean) value));
506             return;
507         case SocketOptions.SO_RCVBUF:
508             Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, (Integer) value);
509             return;
510         case SocketOptions.SO_REUSEADDR:
511             Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR, booleanToInt((Boolean) value));
512             return;
513         case SocketOptions.SO_SNDBUF:
514             Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_SNDBUF, (Integer) value);
515             return;
516         case SocketOptions.SO_TIMEOUT:
517             int millis = (Integer) value;
518             StructTimeval tv = StructTimeval.fromMillis(millis);
519             Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv);
520             return;
521         case SocketOptions.TCP_NODELAY:
522             Libcore.os.setsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY, booleanToInt((Boolean) value));
523             return;
524         case IoBridge.JAVA_MCAST_JOIN_GROUP:
525         case IoBridge.JAVA_MCAST_LEAVE_GROUP:
526         {
527             StructGroupReq groupReq = (StructGroupReq) value;
528             int level = (groupReq.gr_group instanceof Inet4Address) ? IPPROTO_IP : IPPROTO_IPV6;
529             int op = (option == JAVA_MCAST_JOIN_GROUP) ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP;
530             Libcore.os.setsockoptGroupReq(fd, level, op, groupReq);
531             return;
532         }
533         default:
534             throw new SocketException("Unknown socket option: " + option);
535         }
536     }
537 
538     /**
539      * Wrapper for {@link Os#open(String, int, int)} that behaves similar to {@link java.io.File}.
540      * When a {@link java.io.File} is opened and there is an error, it throws
541      * {@link java.io.FileNotFoundException} regardless of what went wrong, when POSIX
542      * {@link Os#open(String, int, int)} throws more grained exceptions of what went wrong.
543      *
544      * <p>Additionally, attempt to open directory using {@link java.io.File} is also error, however
545      * POSIX {@link Os#open(String, int, int)} for read-only directories is not error.
546      *
547      * @see <a href="https://man7.org/linux/man-pages/man2/open.2.html">open(2)</a>.
548      *
549      * @param path  path of the file to be opened
550      * @param flags bitmask of the access, file creation and file status flags
551      * @return {@link FileDescriptor} of an opened file
552      * @throws FileNotFoundException if there was error opening file under {@code path}
553      *
554      * @hide
555      */
556     @SystemApi(client = MODULE_LIBRARIES)
557     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
open(@onNull String path, int flags)558     public static @NonNull FileDescriptor open(@NonNull String path, int flags) throws FileNotFoundException {
559         FileDescriptor fd = null;
560         try {
561             fd = Libcore.os.open(path, flags, 0666);
562             // Posix open(2) fails with EISDIR only if you ask for write permission.
563             // Java disallows reading directories too.f
564             if (S_ISDIR(Libcore.os.fstat(fd).st_mode)) {
565                 throw new ErrnoException("open", EISDIR);
566             }
567             return fd;
568         } catch (ErrnoException errnoException) {
569             try {
570                 if (fd != null) {
571                     closeAndSignalBlockedThreads(fd);
572                 }
573             } catch (IOException ignored) {
574             }
575             FileNotFoundException ex = new FileNotFoundException(path + ": " + errnoException.getMessage());
576             ex.initCause(errnoException);
577             throw ex;
578         }
579     }
580 
581     /**
582      * Wrapper for {@link Os#read(FileDescriptor, byte[], int, int)} that behaves similar to
583      * {@link java.io.FileInputStream#read(byte[], int, int)} and
584      * {@link java.io.FileReader#read(char[], int, int)} which interpret reading at {@code EOF} as
585      * error, when POSIX system call returns {@code 0} (and future reads return {@code -1}).
586      *
587      * <p>@see <a href="https://man7.org/linux/man-pages/man2/read.2.html">read(2)</a>.
588      *
589      * @param fd         file descriptor to read from
590      * @param bytes      buffer to put data read from {@code fd}
591      * @param byteOffset offset in {@code bytes} buffer to put read data at
592      * @param byteCount  number of bytes to read from {@code fd}
593      * @return           number of bytes read, if read operation was successful
594      * @throws IOException if underlying system call returned error
595      *
596      * @hide
597      */
598     @SystemApi(client = MODULE_LIBRARIES)
599     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
read(@onNull FileDescriptor fd, @NonNull byte[] bytes, int byteOffset, int byteCount)600     public static int read(@NonNull FileDescriptor fd, @NonNull byte[] bytes, int byteOffset, int byteCount) throws IOException {
601         ArrayUtils.throwsIfOutOfBounds(bytes.length, byteOffset, byteCount);
602         if (byteCount == 0) {
603             return 0;
604         }
605         try {
606             int readCount = Libcore.os.read(fd, bytes, byteOffset, byteCount);
607             if (readCount == 0) {
608                 return -1;
609             }
610             return readCount;
611         } catch (ErrnoException errnoException) {
612             if (errnoException.errno == EAGAIN) {
613                 // We return 0 rather than throw if we try to read from an empty non-blocking pipe.
614                 return 0;
615             }
616             throw errnoException.rethrowAsIOException();
617         }
618     }
619 
620     /**
621      * Wrapper for {@link Os#write(FileDescriptor, byte[], int, int)} that behaves similar to
622      * {@link java.io.FileOutputStream#write(byte[], int, int)} and
623      * {@link java.io.FileWriter#write(char[], int, int)} which always either write all requested
624      * bytes, or fail with error; as opposed to POSIX write, when the number of bytes written may
625      * be less than {@code bytes}. This may happen, for example, if there is insufficient space on
626      * the underlying  physical medium, or the {@code RLIMIT_FSIZE} resource limit is encountered,
627      * or the call was interrupted by a signal handler after having written less than {@code bytes}
628      * bytes.
629      *
630      * <p>@see <a href="https://man7.org/linux/man-pages/man2/write.2.html">write(2)</a>.
631      *
632      * @param fd         file descriptor to write to
633      * @param bytes      buffer containing the data to be written
634      * @param byteOffset offset in {@code bytes} buffer to read written data from
635      * @param byteCount  number of bytes to write to {@code fd}
636      * @throws IOException if underlying system call returned error
637      *
638      * @hide
639      */
640     @SystemApi(client = MODULE_LIBRARIES)
641     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
write(@onNull FileDescriptor fd,@NonNull byte[] bytes, int byteOffset, int byteCount)642     public static void write(@NonNull FileDescriptor fd,@NonNull  byte[] bytes, int byteOffset, int byteCount) throws IOException {
643         ArrayUtils.throwsIfOutOfBounds(bytes.length, byteOffset, byteCount);
644         if (byteCount == 0) {
645             return;
646         }
647         try {
648             while (byteCount > 0) {
649                 int bytesWritten = Libcore.os.write(fd, bytes, byteOffset, byteCount);
650                 byteCount -= bytesWritten;
651                 byteOffset += bytesWritten;
652             }
653         } catch (ErrnoException errnoException) {
654             throw errnoException.rethrowAsIOException();
655         }
656     }
657 
658     /**
659      * Wrapper around {@link Os#sendto(FileDescriptor, byte[], int, int, int, InetAddress, int)}
660      * that allows sending data over both TCP and UDP socket; handles
661      * {@link android.system.OsConstants#EAGAIN} and {@link android.system.OsConstants#ECONNREFUSED}
662      * and behaves similar to and behaves similar to
663      * {@link java.net.DatagramSocket#send(DatagramPacket)} and
664      * {@link Socket#getOutputStream()#write(FileDescriptor, byte[], int, int)}.
665      *
666      * <p>See {@link android.system.OsConstants} for available flags.
667      *
668      * <p>@see <a href="https://man7.org/linux/man-pages/man2/send.2.html">send(2)</a>.
669      *
670      * @param fd          {@link FileDescriptor} of the socket to send data over
671      * @param bytes       byte buffer containing the data to be sent
672      * @param byteOffset  offset in {@code bytes} at which data to be sent starts
673      * @param byteCount   number of bytes to be sent
674      * @param flags       bitwise OR of zero or more of flags, like {@link android.system.OsConstants#MSG_DONTROUTE}
675      * @param inetAddress destination address
676      * @param port        destination port
677      * @return            number of bytes sent on success
678      * @throws IOException if underlying system call returned error
679      *
680      * @hide
681      */
sendto(@onNull FileDescriptor fd, @NonNull byte[] bytes, int byteOffset, int byteCount, int flags, @Nullable InetAddress inetAddress, int port)682     public static int sendto(@NonNull FileDescriptor fd, @NonNull byte[] bytes, int byteOffset, int byteCount, int flags, @Nullable InetAddress inetAddress, int port) throws IOException {
683         boolean isDatagram = (inetAddress != null);
684         if (!isDatagram && byteCount <= 0) {
685             return 0;
686         }
687         int result;
688         try {
689             result = Libcore.os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port);
690         } catch (ErrnoException errnoException) {
691             result = maybeThrowAfterSendto(isDatagram, errnoException);
692         }
693         return result;
694     }
695 
696     /** @hide */
sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port)697     public static int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws IOException {
698         boolean isDatagram = (inetAddress != null);
699         if (!isDatagram && buffer.remaining() == 0) {
700             return 0;
701         }
702         int result;
703         try {
704             result = Libcore.os.sendto(fd, buffer, flags, inetAddress, port);
705         } catch (ErrnoException errnoException) {
706             result = maybeThrowAfterSendto(isDatagram, errnoException);
707         }
708         return result;
709     }
710 
maybeThrowAfterSendto(boolean isDatagram, ErrnoException errnoException)711     private static int maybeThrowAfterSendto(boolean isDatagram, ErrnoException errnoException)
712             throws IOException {
713         if (isDatagram) {
714             if (errnoException.errno == ECONNREFUSED) {
715                 throw new PortUnreachableException("ICMP Port Unreachable");
716             }
717         } else {
718             if (errnoException.errno == EAGAIN) {
719                 // We were asked to write to a non-blocking socket, but were told
720                 // it would block, so report "no bytes written".
721                 return 0;
722             }
723         }
724         throw errnoException.rethrowAsIOException();
725     }
726 
727     /**
728      * Wrapper around {@link Os#recvfrom(FileDescriptor, byte[], int, int, int, InetSocketAddress)}
729      * that receives a message from both TCP and UDP socket; handles
730      * {@link android.system.OsConstants#EAGAIN} and {@link android.system.OsConstants#ECONNREFUSED}
731      * and behaves similar to {@link java.net.DatagramSocket#receive(DatagramPacket)} and
732      * {@link Socket#getInputStream()#recvfrom(boolean, FileDescriptor, byte[], int, int, int, DatagramPacket, boolean)}.
733      *
734      * <p>If {@code packet} is not {@code null}, and the underlying protocol provides the source
735      * address of the message, that source address is placed in the {@code packet}.
736      *
737      * @see <a href="https://man7.org/linux/man-pages/man2/recv.2.html">recv(2)</a>.
738      *
739      * @param isRead      {@code true} if some data been read already from {@code fd}
740      * @param fd          socket to receive data from
741      * @param bytes       buffer to put data read from {@code fd}
742      * @param byteOffset  offset in {@code bytes} buffer to put read data at
743      * @param byteCount   number of bytes to read from {@code fd}
744      * @param flags       bitwise OR of zero or more of flags, like {@link android.system.OsConstants#MSG_DONTROUTE}
745      * @param packet      {@link DatagramPacket} to fill with source address
746      * @param isConnected {@code true} if socket {@code fd} is connected
747      * @return            number of bytes read, if read operation was successful
748      * @throws IOException if underlying system call returned error
749      *
750      * @hide
751      */
recvfrom(boolean isRead, @NonNull FileDescriptor fd, @NonNull byte[] bytes, int byteOffset, int byteCount, int flags, @Nullable DatagramPacket packet, boolean isConnected)752     public static int recvfrom(boolean isRead, @NonNull FileDescriptor fd, @NonNull byte[] bytes, int byteOffset, int byteCount, int flags, @Nullable DatagramPacket packet, boolean isConnected) throws IOException {
753         int result;
754         try {
755             InetSocketAddress srcAddress = packet != null ? new InetSocketAddress() : null;
756             result = Libcore.os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
757             result = postRecvfrom(isRead, packet, srcAddress, result);
758         } catch (ErrnoException errnoException) {
759             result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException);
760         }
761         return result;
762     }
763 
764     /** @hide */
recvfrom(boolean isRead, FileDescriptor fd, ByteBuffer buffer, int flags, DatagramPacket packet, boolean isConnected)765     public static int recvfrom(boolean isRead, FileDescriptor fd, ByteBuffer buffer, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
766         int result;
767         try {
768             InetSocketAddress srcAddress = packet != null ? new InetSocketAddress() : null;
769             result = Libcore.os.recvfrom(fd, buffer, flags, srcAddress);
770             result = postRecvfrom(isRead, packet, srcAddress, result);
771         } catch (ErrnoException errnoException) {
772             result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException);
773         }
774         return result;
775     }
776 
postRecvfrom(boolean isRead, DatagramPacket packet, InetSocketAddress srcAddress, int byteCount)777     private static int postRecvfrom(boolean isRead, DatagramPacket packet, InetSocketAddress srcAddress, int byteCount) {
778         if (isRead && byteCount == 0) {
779             return -1;
780         }
781         if (packet != null) {
782             packet.setReceivedLength(byteCount);
783             packet.setPort(srcAddress.getPort());
784 
785             // packet.address should only be changed when it is different from srcAddress.
786             if (!srcAddress.getAddress().equals(packet.getAddress())) {
787                 packet.setAddress(srcAddress.getAddress());
788             }
789         }
790         return byteCount;
791     }
792 
maybeThrowAfterRecvfrom(boolean isRead, boolean isConnected, ErrnoException errnoException)793     private static int maybeThrowAfterRecvfrom(boolean isRead, boolean isConnected, ErrnoException errnoException) throws SocketException, SocketTimeoutException {
794         if (isRead) {
795             if (errnoException.errno == EAGAIN) {
796                 return 0;
797             } else {
798                 throw errnoException.rethrowAsSocketException();
799             }
800         } else {
801             if (isConnected && errnoException.errno == ECONNREFUSED) {
802                 throw new PortUnreachableException("ICMP Port Unreachable", errnoException);
803             } else if (errnoException.errno == EAGAIN) {
804                 SocketTimeoutException e = new SocketTimeoutException();
805                 e.initCause(errnoException);
806                 throw e;
807             } else {
808                 throw errnoException.rethrowAsSocketException();
809             }
810         }
811     }
812 
813     /**
814      * Creates an endpoint for communication and returns a file descriptor that refers
815      * to that endpoint.
816      *
817      * <p>The {@code domain} specifies a communication domain; this selects the protocol
818      * family which will be used for communication, e.g. {@link android.system.OsConstants#AF_UNIX}
819      * {@link android.system.OsConstants#AF_INET}.
820      *
821      * <p>The socket has the indicated type, which specifies the communication semantics,
822      * e.g. {@link android.system.OsConstants#SOCK_STREAM} or
823      * {@link android.system.OsConstants#SOCK_DGRAM}.
824      *
825      * <p>The protocol specifies a particular protocol to be used with the
826      * socket. Normally only a single protocol exists to support a
827      * particular socket type within a given protocol family, in which
828      * case protocol can be specified as {@code 0}.
829      *
830      * @see <a href="https://man7.org/linux/man-pages/man2/socket.2.html">socket(2)</a>.
831      *
832      * @param domain   socket domain
833      * @param type     socket type
834      * @param protocol socket protocol
835      * @return {@link FileDescriptor} of an opened socket
836      * @throws SocketException if underlying system call returned error
837      *
838      * @hide
839      */
socket(int domain, int type, int protocol)840     public static @NonNull FileDescriptor socket(int domain, int type, int protocol) throws SocketException {
841         FileDescriptor fd;
842         try {
843             fd = Libcore.os.socket(domain, type, protocol);
844 
845             return fd;
846         } catch (ErrnoException errnoException) {
847             throw errnoException.rethrowAsSocketException();
848         }
849     }
850 
851     /**
852      * Wait for some event on a file descriptor, blocks until the event happened or timeout period
853      * passed. See poll(2) and @link{android.system.Os.Poll}.
854      *
855      * @throws SocketException if poll(2) fails.
856      * @throws SocketTimeoutException if the event has not happened before timeout period has passed.
857      *
858      * @hide
859      */
poll(FileDescriptor fd, int events, int timeout)860     public static void poll(FileDescriptor fd, int events, int timeout)
861             throws SocketException, SocketTimeoutException {
862         StructPollfd[] pollFds = new StructPollfd[]{ new StructPollfd() };
863         pollFds[0].fd = fd;
864         pollFds[0].events = (short) events;
865 
866         try {
867             int ret = android.system.Os.poll(pollFds, timeout);
868             if (ret == 0) {
869                 throw new SocketTimeoutException("Poll timed out");
870             }
871         } catch (ErrnoException e) {
872             e.rethrowAsSocketException();
873         }
874     }
875 
876     /**
877      * Returns the current address to which the socket {@code fd} is bound.
878      *
879      * @see <a href="https://man7.org/linux/man-pages/man2/getsockname.2.html">getsockname(2)</a>.
880      *
881      * @param fd socket to get the bounded address of
882      * @return current address to which the socket {@code fd} is bound
883      * @throws SocketException if {@code fd} is not currently bound to an {@link InetSocketAddress}
884      *
885      * @hide
886      */
getLocalInetSocketAddress(@onNull FileDescriptor fd)887     public static @NonNull InetSocketAddress getLocalInetSocketAddress(@NonNull FileDescriptor fd)
888             throws SocketException {
889         try {
890             SocketAddress socketAddress = Libcore.os.getsockname(fd);
891             // When a Socket is pending closure because socket.close() was called but other threads
892             // are still using it, the FileDescriptor can be dup2'ed to an AF_UNIX one; see the
893             // deferred close logic in PlainSocketImpl.socketClose0(true) for details.
894             // If socketAddress is not the expected type then we assume that the socket is being
895             // closed, so we throw a SocketException (just like in the case of an ErrnoException).
896             // http://b/64209834
897             if ((socketAddress != null) && !(socketAddress instanceof InetSocketAddress)) {
898                 throw new SocketException("Socket assumed to be pending closure: Expected sockname "
899                         + "to be an InetSocketAddress, got " + socketAddress.getClass());
900             }
901             return (InetSocketAddress) socketAddress;
902         } catch (ErrnoException errnoException) {
903             throw errnoException.rethrowAsSocketException();
904         }
905     }
906 }
907