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