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