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