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 17 package libcore.io; 18 19 import android.system.ErrnoException; 20 import android.system.StructStat; 21 import java.io.File; 22 import java.io.FileDescriptor; 23 import java.io.FileNotFoundException; 24 import java.io.IOException; 25 import java.io.InterruptedIOException; 26 import java.net.Socket; 27 import java.nio.charset.Charset; 28 import java.nio.charset.StandardCharsets; 29 import java.util.Random; 30 import static android.system.OsConstants.*; 31 32 public final class IoUtils { IoUtils()33 private IoUtils() { 34 } 35 36 /** 37 * Calls close(2) on 'fd'. Also resets the internal int to -1. Does nothing if 'fd' is null 38 * or invalid. 39 */ close(FileDescriptor fd)40 public static void close(FileDescriptor fd) throws IOException { 41 try { 42 if (fd != null && fd.valid()) { 43 Libcore.os.close(fd); 44 } 45 } catch (ErrnoException errnoException) { 46 throw errnoException.rethrowAsIOException(); 47 } 48 } 49 50 /** 51 * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null. 52 */ closeQuietly(AutoCloseable closeable)53 public static void closeQuietly(AutoCloseable closeable) { 54 if (closeable != null) { 55 try { 56 closeable.close(); 57 } catch (RuntimeException rethrown) { 58 throw rethrown; 59 } catch (Exception ignored) { 60 } 61 } 62 } 63 64 /** 65 * Closes 'fd', ignoring any exceptions. Does nothing if 'fd' is null or invalid. 66 */ closeQuietly(FileDescriptor fd)67 public static void closeQuietly(FileDescriptor fd) { 68 try { 69 IoUtils.close(fd); 70 } catch (IOException ignored) { 71 } 72 } 73 74 /** 75 * Closes 'socket', ignoring any exceptions. Does nothing if 'socket' is null. 76 */ closeQuietly(Socket socket)77 public static void closeQuietly(Socket socket) { 78 if (socket != null) { 79 try { 80 socket.close(); 81 } catch (Exception ignored) { 82 } 83 } 84 } 85 86 /** 87 * Sets 'fd' to be blocking or non-blocking, according to the state of 'blocking'. 88 */ setBlocking(FileDescriptor fd, boolean blocking)89 public static void setBlocking(FileDescriptor fd, boolean blocking) throws IOException { 90 try { 91 int flags = Libcore.os.fcntlVoid(fd, F_GETFL); 92 if (!blocking) { 93 flags |= O_NONBLOCK; 94 } else { 95 flags &= ~O_NONBLOCK; 96 } 97 Libcore.os.fcntlInt(fd, F_SETFL, flags); 98 } catch (ErrnoException errnoException) { 99 throw errnoException.rethrowAsIOException(); 100 } 101 } 102 103 /** 104 * Returns the contents of 'path' as a byte array. 105 */ readFileAsByteArray(String absolutePath)106 public static byte[] readFileAsByteArray(String absolutePath) throws IOException { 107 return new FileReader(absolutePath).readFully().toByteArray(); 108 } 109 110 /** 111 * Returns the contents of 'path' as a string. The contents are assumed to be UTF-8. 112 */ readFileAsString(String absolutePath)113 public static String readFileAsString(String absolutePath) throws IOException { 114 return new FileReader(absolutePath).readFully().toString(StandardCharsets.UTF_8); 115 } 116 117 /** 118 * Do not use. Use createTemporaryDirectory instead. 119 * 120 * Used by frameworks/base unit tests to clean up a temporary directory. 121 * Deliberately ignores errors, on the assumption that test cleanup is only 122 * supposed to be best-effort. 123 * 124 * @deprecated Use {@link #createTemporaryDirectory} instead. 125 */ deleteContents(File dir)126 public static void deleteContents(File dir) throws IOException { 127 File[] files = dir.listFiles(); 128 if (files != null) { 129 for (File file : files) { 130 if (file.isDirectory()) { 131 deleteContents(file); 132 } 133 file.delete(); 134 } 135 } 136 } 137 138 /** 139 * Creates a unique new temporary directory under "java.io.tmpdir". 140 */ createTemporaryDirectory(String prefix)141 public static File createTemporaryDirectory(String prefix) { 142 while (true) { 143 String candidateName = prefix + Math.randomIntInternal(); 144 File result = new File(System.getProperty("java.io.tmpdir"), candidateName); 145 if (result.mkdir()) { 146 return result; 147 } 148 } 149 } 150 151 /** 152 * Do not use. This is for System.loadLibrary use only. 153 * 154 * Checks whether {@code path} can be opened read-only. Similar to File.exists, but doesn't 155 * require read permission on the parent, so it'll work in more cases, and allow you to 156 * remove read permission from more directories. Everyone else should just open(2) and then 157 * use the fd, but the loadLibrary API is broken by its need to ask ClassLoaders where to 158 * find a .so rather than just calling dlopen(3). 159 */ canOpenReadOnly(String path)160 public static boolean canOpenReadOnly(String path) { 161 try { 162 // Use open(2) rather than stat(2) so we require fewer permissions. http://b/6485312. 163 FileDescriptor fd = Libcore.os.open(path, O_RDONLY, 0); 164 Libcore.os.close(fd); 165 return true; 166 } catch (ErrnoException errnoException) { 167 return false; 168 } 169 } 170 throwInterruptedIoException()171 public static void throwInterruptedIoException() throws InterruptedIOException { 172 // This is typically thrown in response to an 173 // InterruptedException which does not leave the thread in an 174 // interrupted state, so explicitly interrupt here. 175 Thread.currentThread().interrupt(); 176 // TODO: set InterruptedIOException.bytesTransferred 177 throw new InterruptedIOException(); 178 } 179 180 /** 181 * A convenience class for reading the contents of a file into a {@code String} 182 * or a {@code byte[]}. This class attempts to minimize the number of allocations 183 * and copies required to read this data. 184 * 185 * For the case where we know the "true" length of a file (most ordinary files) 186 * we allocate exactly one byte[] and copy data into that. Calls to 187 * {@link #toByteArray} will then return the internal array and <b>not</b> a copy. 188 * 189 * <b>Note that an absolute path must be supplied. Expect your reads to fail 190 * if one isn't.</b> 191 */ 192 private static class FileReader { 193 private FileDescriptor fd; 194 private boolean unknownLength; 195 196 private byte[] bytes; 197 private int count; 198 FileReader(String absolutePath)199 public FileReader(String absolutePath) throws IOException { 200 // We use IoBridge.open because callers might differentiate 201 // between a FileNotFoundException and a general IOException. 202 // 203 // NOTE: This costs us an additional call to fstat(2) to test whether 204 // "absolutePath" is a directory or not. We can eliminate it 205 // at the cost of copying some code from IoBridge.open. 206 try { 207 fd = IoBridge.open(absolutePath, O_RDONLY); 208 } catch (FileNotFoundException fnfe) { 209 throw fnfe; 210 } 211 212 int capacity; 213 try { 214 final StructStat stat = Libcore.os.fstat(fd); 215 // Like RAF & other APIs, we assume that the file size fits 216 // into a 32 bit integer. 217 capacity = (int) stat.st_size; 218 if (capacity == 0) { 219 unknownLength = true; 220 capacity = 8192; 221 } 222 } catch (ErrnoException exception) { 223 closeQuietly(fd); 224 throw exception.rethrowAsIOException(); 225 } 226 227 bytes = new byte[capacity]; 228 } 229 readFully()230 public FileReader readFully() throws IOException { 231 int read; 232 int capacity = bytes.length; 233 try { 234 while ((read = Libcore.os.read(fd, bytes, count, capacity - count)) != 0) { 235 count += read; 236 if (count == capacity) { 237 if (unknownLength) { 238 // If we don't know the length of this file, we need to continue 239 // reading until we reach EOF. Double the capacity in preparation. 240 final int newCapacity = capacity * 2; 241 byte[] newBytes = new byte[newCapacity]; 242 System.arraycopy(bytes, 0, newBytes, 0, capacity); 243 bytes = newBytes; 244 capacity = newCapacity; 245 } else { 246 // We know the length of this file and we've read the right number 247 // of bytes from it, return. 248 break; 249 } 250 } 251 } 252 253 return this; 254 } catch (ErrnoException e) { 255 throw e.rethrowAsIOException(); 256 } finally { 257 closeQuietly(fd); 258 } 259 } 260 261 @FindBugsSuppressWarnings("EI_EXPOSE_REP") toByteArray()262 public byte[] toByteArray() { 263 if (count == bytes.length) { 264 return bytes; 265 } 266 byte[] result = new byte[count]; 267 System.arraycopy(bytes, 0, result, 0, count); 268 return result; 269 } 270 toString(Charset cs)271 public String toString(Charset cs) { 272 return new String(bytes, 0, count, cs); 273 } 274 } 275 } 276