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