IoUtils.java revision 220c0af1763283b75617226efe3919c3e3b044c8
1/* 2 * Copyright (C) 2010 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 17package libcore.io; 18 19import java.io.Closeable; 20import java.io.FileDescriptor; 21import java.io.FileNotFoundException; 22import java.io.IOException; 23import java.io.RandomAccessFile; 24import java.net.InetAddress; 25import java.net.Inet4Address; 26import java.net.InetSocketAddress; 27import java.net.Socket; 28import java.net.SocketAddress; 29import java.net.SocketException; 30import java.net.SocketOptions; 31import java.net.SocketTimeoutException; 32import java.nio.charset.Charsets; 33import java.util.Arrays; 34import libcore.io.ErrnoException; 35import libcore.io.Libcore; 36import libcore.util.MutableInt; 37import static libcore.io.OsConstants.*; 38 39// TODO: kill this! 40import org.apache.harmony.luni.platform.Platform; 41 42public final class IoUtils { 43 private IoUtils() { 44 } 45 46 /** 47 * Implements java.io/java.net "available" semantics. 48 */ 49 public static int available(FileDescriptor fd) throws IOException { 50 try { 51 MutableInt available = new MutableInt(0); 52 int rc = Libcore.os.ioctlInt(fd, FIONREAD, available); 53 if (available.value < 0) { 54 // If the fd refers to a regular file, the result is the difference between 55 // the file size and the file position. This may be negative if the position 56 // is past the end of the file. If the fd refers to a special file masquerading 57 // as a regular file, the result may be negative because the special file 58 // may appear to have zero size and yet a previous read call may have 59 // read some amount of data and caused the file position to be advanced. 60 available.value = 0; 61 } 62 return available.value; 63 } catch (ErrnoException errnoException) { 64 if (errnoException.errno == ENOTTY) { 65 // The fd is unwilling to opine about its read buffer. 66 return 0; 67 } 68 throw errnoException.rethrowAsIOException(); 69 } 70 } 71 72 /** 73 * java.io only throws FileNotFoundException when opening files, regardless of what actually 74 * went wrong. Additionally, java.io is more restrictive than POSIX when it comes to opening 75 * directories: POSIX says read-only is okay, but java.io doesn't even allow that. We also 76 * have an Android-specific hack to alter the default permissions. 77 */ 78 public static FileDescriptor open(String path, int flags) throws FileNotFoundException { 79 FileDescriptor fd = null; 80 try { 81 // On Android, we don't want default permissions to allow global access. 82 int mode = ((flags & O_ACCMODE) == O_RDONLY) ? 0 : 0600; 83 fd = Libcore.os.open(path, flags, mode); 84 if (fd.valid()) { 85 // Posix open(2) fails with EISDIR only if you ask for write permission. 86 // Java disallows reading directories too. 87 boolean isDirectory = false; 88 if (S_ISDIR(Libcore.os.fstat(fd).st_mode)) { 89 throw new ErrnoException("open", EISDIR); 90 } 91 } 92 return fd; 93 } catch (ErrnoException errnoException) { 94 try { 95 if (fd != null) { 96 close(fd); 97 } 98 } catch (IOException ignored) { 99 } 100 FileNotFoundException ex = new FileNotFoundException(path + ": " + errnoException.getMessage()); 101 ex.initCause(errnoException); 102 throw ex; 103 } 104 } 105 106 /** 107 * java.io thinks that a read at EOF is an error and should return -1, contrary to traditional 108 * Unix practice where you'd read until you got 0 bytes (and any future read would return -1). 109 */ 110 public static int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException { 111 Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount); 112 if (byteCount == 0) { 113 return 0; 114 } 115 try { 116 int readCount = Libcore.os.read(fd, bytes, byteOffset, byteCount); 117 if (readCount == 0) { 118 return -1; 119 } 120 return readCount; 121 } catch (ErrnoException errnoException) { 122 if (errnoException.errno == EAGAIN) { 123 // We return 0 rather than throw if we try to read from an empty non-blocking pipe. 124 return 0; 125 } 126 throw errnoException.rethrowAsIOException(); 127 } 128 } 129 130 /** 131 * java.io always writes every byte it's asked to, or fails with an error. (That is, unlike 132 * Unix it never just writes as many bytes as happens to be convenient.) 133 */ 134 public static void write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException { 135 Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount); 136 if (byteCount == 0) { 137 return; 138 } 139 try { 140 while (byteCount > 0) { 141 int bytesWritten = Libcore.os.write(fd, bytes, byteOffset, byteCount); 142 byteCount -= bytesWritten; 143 byteOffset += bytesWritten; 144 } 145 } catch (ErrnoException errnoException) { 146 throw errnoException.rethrowAsIOException(); 147 } 148 } 149 150 /** 151 * Calls close(2) on 'fd'. Also resets the internal int to -1. Does nothing if 'fd' is null 152 * or invalid. 153 */ 154 public static void close(FileDescriptor fd) throws IOException { 155 try { 156 if (fd != null && fd.valid()) { 157 Libcore.os.close(fd); 158 } 159 } catch (ErrnoException errnoException) { 160 throw errnoException.rethrowAsIOException(); 161 } 162 } 163 164 /** 165 * Closes 'closeable', ignoring any exceptions. Does nothing if 'closeable' is null. 166 */ 167 public static void closeQuietly(Closeable closeable) { 168 if (closeable != null) { 169 try { 170 closeable.close(); 171 } catch (IOException ignored) { 172 } 173 } 174 } 175 176 /** 177 * Closes 'fd', ignoring any exceptions. Does nothing if 'fd' is null or invalid. 178 */ 179 public static void closeQuietly(FileDescriptor fd) { 180 try { 181 IoUtils.close(fd); 182 } catch (IOException ignored) { 183 } 184 } 185 186 /** 187 * Closes 'socket', ignoring any exceptions. Does nothing if 'socket' is null. 188 */ 189 public static void closeQuietly(Socket socket) { 190 if (socket != null) { 191 try { 192 socket.close(); 193 } catch (Exception ignored) { 194 } 195 } 196 } 197 198 /** 199 * Connects socket 'fd' to 'inetAddress' on 'port', with no timeout. The lack of a timeout 200 * means this method won't throw SocketTimeoutException. 201 */ 202 public static void connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException { 203 try { 204 IoUtils.connect(fd, inetAddress, port, 0); 205 } catch (SocketTimeoutException ex) { 206 throw new AssertionError(ex); // Can't happen for a connect without a timeout. 207 } 208 } 209 210 /** 211 * Connects socket 'fd' to 'inetAddress' on 'port', with a the given 'timeoutMs'. 212 * Use timeoutMs == 0 for a blocking connect with no timeout. 213 */ 214 public static void connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException { 215 try { 216 connectErrno(fd, inetAddress, port, timeoutMs); 217 } catch (SocketException ex) { 218 throw ex; // We don't want to doubly wrap these. 219 } catch (SocketTimeoutException ex) { 220 throw ex; // We don't want to doubly wrap these. 221 } catch (IOException ex) { 222 throw new SocketException(ex); 223 } 224 } 225 226 // TODO: this is the wrong name now, but when this gets rewritten without Platform.NETWORK... 227 private static void connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws IOException { 228 // With no timeout, just call connect(2) directly. 229 if (timeoutMs == 0) { 230 Platform.NETWORK.connect(fd, inetAddress, port); 231 return; 232 } 233 234 // With a timeout, we set the socket to non-blocking, connect(2), and then loop 235 // using select(2) to decide whether we're connected, whether we should keep waiting, 236 // or whether we've seen a permanent failure and should give up. 237 long finishTimeMs = System.currentTimeMillis() + timeoutMs; 238 IoUtils.setBlocking(fd, false); 239 try { 240 if (Platform.NETWORK.connect(fd, inetAddress, port)) { 241 return; 242 } 243 int remainingTimeoutMs; 244 do { 245 remainingTimeoutMs = (int) (finishTimeMs - System.currentTimeMillis()); 246 if (remainingTimeoutMs <= 0) { 247 String detail = "failed to connect to " + inetAddress + " (port " + port + ")"; 248 if (timeoutMs > 0) { 249 detail += " after " + timeoutMs + "ms"; 250 } 251 throw new SocketTimeoutException(detail); 252 } 253 } while (!Platform.NETWORK.isConnected(fd, remainingTimeoutMs)); 254 } finally { 255 IoUtils.setBlocking(fd, true); 256 } 257 } 258 259 /** 260 * Sets 'fd' to be blocking or non-blocking, according to the state of 'blocking'. 261 */ 262 public static void setBlocking(FileDescriptor fd, boolean blocking) throws IOException { 263 try { 264 int flags = Libcore.os.fcntlVoid(fd, F_GETFL); 265 if (!blocking) { 266 flags |= O_NONBLOCK; 267 } else { 268 flags &= ~O_NONBLOCK; 269 } 270 Libcore.os.fcntlLong(fd, F_SETFL, flags); 271 } catch (ErrnoException errnoException) { 272 throw errnoException.rethrowAsIOException(); 273 } 274 } 275 276 public static FileDescriptor socket(boolean stream) throws SocketException { 277 FileDescriptor fd; 278 try { 279 fd = Libcore.os.socket(AF_INET6, stream ? SOCK_STREAM : SOCK_DGRAM, 0); 280 281 // The RFC (http://www.ietf.org/rfc/rfc3493.txt) says that IPV6_MULTICAST_HOPS defaults 282 // to 1. The Linux kernel (at least up to 2.6.38) accidentally defaults to 64 (which 283 // would be correct for the *unicast* hop limit). 284 // See http://www.spinics.net/lists/netdev/msg129022.html, though no patch appears to 285 // have been applied as a result of that discussion. If that bug is ever fixed, we can 286 // remove this code. Until then, we manually set the hop limit on IPv6 datagram sockets. 287 // (IPv4 is already correct.) 288 if (!stream) { 289 Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1); 290 } 291 292 return fd; 293 } catch (ErrnoException errnoException) { 294 throw errnoException.rethrowAsSocketException(); 295 } 296 } 297 298 // Socket options used by java.net but not exposed in SocketOptions. 299 public static final int JAVA_MCAST_JOIN_GROUP = 19; 300 public static final int JAVA_MCAST_LEAVE_GROUP = 20; 301 public static final int JAVA_IP_MULTICAST_TTL = 17; 302 303 /** 304 * java.net has its own socket options similar to the underlying Unix ones. We paper over the 305 * differences here. 306 */ 307 public static Object getSocketOption(FileDescriptor fd, int option) throws SocketException { 308 try { 309 return getSocketOptionErrno(fd, option); 310 } catch (ErrnoException errnoException) { 311 throw errnoException.rethrowAsSocketException(); 312 } 313 } 314 315 private static Object getSocketOptionErrno(FileDescriptor fd, int option) throws SocketException { 316 switch (option) { 317 case SocketOptions.IP_MULTICAST_IF: 318 // This is IPv4-only. 319 return Libcore.os.getsockoptInAddr(fd, IPPROTO_IP, IP_MULTICAST_IF); 320 case SocketOptions.IP_MULTICAST_IF2: 321 // This is IPv6-only. 322 return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF); 323 case SocketOptions.IP_MULTICAST_LOOP: 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 booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP)); 327 case IoUtils.JAVA_IP_MULTICAST_TTL: 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_MULTICAST_HOPS); 331 case SocketOptions.IP_TOS: 332 // Since setting this from java.net always sets IPv4 and IPv6 to the same value, 333 // it doesn't matter which we return. 334 return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS); 335 case SocketOptions.SO_BROADCAST: 336 return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_BROADCAST)); 337 case SocketOptions.SO_KEEPALIVE: 338 return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE)); 339 case SocketOptions.SO_LINGER: 340 StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER); 341 if (!linger.isOn()) { 342 return false; 343 } 344 return linger.l_linger; 345 case SocketOptions.SO_OOBINLINE: 346 return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE)); 347 case SocketOptions.SO_RCVBUF: 348 return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF); 349 case SocketOptions.SO_REUSEADDR: 350 return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR)); 351 case SocketOptions.SO_SNDBUF: 352 return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF); 353 case SocketOptions.SO_TIMEOUT: 354 return (int) Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO).toMillis(); 355 case SocketOptions.TCP_NODELAY: 356 return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY)); 357 default: 358 throw new SocketException("Unknown socket option: " + option); 359 } 360 } 361 362 private static boolean booleanFromInt(int i) { 363 return (i != 0); 364 } 365 366 private static int booleanToInt(boolean b) { 367 return b ? 1 : 0; 368 } 369 370 /** 371 * java.net has its own socket options similar to the underlying Unix ones. We paper over the 372 * differences here. 373 */ 374 public static void setSocketOption(FileDescriptor fd, int option, Object value) throws SocketException { 375 try { 376 setSocketOptionErrno(fd, option, value); 377 } catch (ErrnoException errnoException) { 378 throw errnoException.rethrowAsSocketException(); 379 } 380 } 381 382 private static void setSocketOptionErrno(FileDescriptor fd, int option, Object value) throws SocketException { 383 switch (option) { 384 case SocketOptions.IP_MULTICAST_IF: 385 throw new UnsupportedOperationException("Use IP_MULTICAST_IF2 on Android"); 386 case SocketOptions.IP_MULTICAST_IF2: 387 // Although IPv6 was cleaned up to use int, IPv4 uses an ip_mreqn containing an int. 388 Libcore.os.setsockoptIpMreqn(fd, IPPROTO_IP, IP_MULTICAST_IF, (Integer) value); 389 Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (Integer) value); 390 return; 391 case SocketOptions.IP_MULTICAST_LOOP: 392 // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte. 393 Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_LOOP, booleanToInt((Boolean) value)); 394 Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, booleanToInt((Boolean) value)); 395 return; 396 case IoUtils.JAVA_IP_MULTICAST_TTL: 397 // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int, 398 // IPv4 multicast TTL uses a byte. 399 Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_TTL, (Integer) value); 400 Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (Integer) value); 401 return; 402 case SocketOptions.IP_TOS: 403 Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_TOS, (Integer) value); 404 Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS, (Integer) value); 405 return; 406 case SocketOptions.SO_BROADCAST: 407 Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_BROADCAST, booleanToInt((Boolean) value)); 408 return; 409 case SocketOptions.SO_KEEPALIVE: 410 Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE, booleanToInt((Boolean) value)); 411 return; 412 case SocketOptions.SO_LINGER: 413 boolean on = false; 414 int seconds = 0; 415 if (value instanceof Integer) { 416 on = true; 417 seconds = Math.min((Integer) value, 65535); 418 } 419 StructLinger linger = new StructLinger(booleanToInt(on), seconds); 420 Libcore.os.setsockoptLinger(fd, SOL_SOCKET, SO_LINGER, linger); 421 return; 422 case SocketOptions.SO_OOBINLINE: 423 Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE, booleanToInt((Boolean) value)); 424 return; 425 case SocketOptions.SO_RCVBUF: 426 Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, (Integer) value); 427 return; 428 case SocketOptions.SO_REUSEADDR: 429 Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR, booleanToInt((Boolean) value)); 430 return; 431 case SocketOptions.SO_SNDBUF: 432 Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_SNDBUF, (Integer) value); 433 return; 434 case SocketOptions.SO_TIMEOUT: 435 int millis = (Integer) value; 436 StructTimeval tv = StructTimeval.fromMillis(millis); 437 Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv); 438 return; 439 case SocketOptions.TCP_NODELAY: 440 Libcore.os.setsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY, booleanToInt((Boolean) value)); 441 return; 442 case IoUtils.JAVA_MCAST_JOIN_GROUP: 443 case IoUtils.JAVA_MCAST_LEAVE_GROUP: 444 StructGroupReq groupReq = (StructGroupReq) value; 445 int level = (groupReq.gr_group instanceof Inet4Address) ? IPPROTO_IP : IPPROTO_IPV6; 446 int op = (option == JAVA_MCAST_JOIN_GROUP) ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP; 447 Libcore.os.setsockoptGroupReq(fd, level, op, groupReq); 448 return; 449 default: 450 throw new SocketException("Unknown socket option: " + option); 451 } 452 } 453 454 public static InetAddress getSocketLocalAddress(FileDescriptor fd) { 455 SocketAddress sa = Libcore.os.getsockname(fd); 456 InetSocketAddress isa = (InetSocketAddress) sa; 457 return isa.getAddress(); 458 } 459 460 public static int getSocketLocalPort(FileDescriptor fd) { 461 SocketAddress sa = Libcore.os.getsockname(fd); 462 InetSocketAddress isa = (InetSocketAddress) sa; 463 return isa.getPort(); 464 } 465 466 /** 467 * Returns the contents of 'path' as a byte array. 468 */ 469 public static byte[] readFileAsByteArray(String path) throws IOException { 470 return readFileAsBytes(path).toByteArray(); 471 } 472 473 /** 474 * Returns the contents of 'path' as a string. The contents are assumed to be UTF-8. 475 */ 476 public static String readFileAsString(String path) throws IOException { 477 return readFileAsBytes(path).toString(Charsets.UTF_8); 478 } 479 480 private static UnsafeByteSequence readFileAsBytes(String path) throws IOException { 481 RandomAccessFile f = null; 482 try { 483 f = new RandomAccessFile(path, "r"); 484 UnsafeByteSequence bytes = new UnsafeByteSequence((int) f.length()); 485 byte[] buffer = new byte[8192]; 486 while (true) { 487 int byteCount = f.read(buffer); 488 if (byteCount == -1) { 489 return bytes; 490 } 491 bytes.write(buffer, 0, byteCount); 492 } 493 } finally { 494 IoUtils.closeQuietly(f); 495 } 496 } 497} 498