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