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