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