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.Objects; 30 31 import dalvik.annotation.compat.UnsupportedAppUsage; 32 import libcore.util.NonNull; 33 import static android.system.OsConstants.*; 34 35 /** @hide */ 36 @libcore.api.CorePlatformApi 37 public final class IoUtils { IoUtils()38 private IoUtils() { 39 } 40 41 /** 42 * Acquires ownership of an integer file descriptor from a FileDescriptor. 43 * 44 * This method invalidates the FileDescriptor passed in. 45 * 46 * The important part of this function is that you are taking ownership of a resource that you 47 * must either clean up yourself, or hand off to some other object that does that for you. 48 * 49 * See bionic/include/android/fdsan.h for more details. 50 * 51 * @param fd FileDescriptor to take ownership from, must be non-null. 52 * @throws NullPointerException if fd is null 53 */ 54 @libcore.api.CorePlatformApi acquireRawFd(@onNull FileDescriptor fd)55 public static int acquireRawFd(@NonNull FileDescriptor fd) { 56 Objects.requireNonNull(fd); 57 58 FileDescriptor copy = fd.release$(); 59 // Get the numeric Unix file descriptor. -1 means it is invalid; for example if 60 // {@link FileDescriptor#release$()} has already been called on the FileDescriptor. 61 int rawFd = copy.getInt$(); 62 long previousOwnerId = copy.getOwnerId$(); 63 if (rawFd != -1 && previousOwnerId != FileDescriptor.NO_OWNER) { 64 // Clear the file descriptor's owner ID, aborting if the previous value isn't as expected. 65 Libcore.os.android_fdsan_exchange_owner_tag(copy, previousOwnerId, 66 FileDescriptor.NO_OWNER); 67 } 68 return rawFd; 69 } 70 isParcelFileDescriptor(Object object)71 private static boolean isParcelFileDescriptor(Object object) { 72 // We need to look up ParcelFileDescriptor dynamically, because there are cases where the 73 // framework classes will not be found on the classpath such as on-host development. 74 try { 75 Class<?> pfdClass = Class.forName("android.os.ParcelFileDescriptor"); 76 if (pfdClass.isInstance(object)) { 77 return true; 78 } 79 return false; 80 } catch (ClassNotFoundException ex) { 81 return false; 82 } 83 } 84 generateFdOwnerId(Object owner)85 private static long generateFdOwnerId(Object owner) { 86 if (owner == null) { 87 return 0; 88 } 89 90 // Type values from bionic's <android/fdsan.h>. 91 long tagType; 92 if (owner instanceof java.io.FileInputStream) { 93 tagType = 5; 94 } else if (owner instanceof java.io.FileOutputStream) { 95 tagType = 6; 96 } else if (owner instanceof java.io.RandomAccessFile) { 97 tagType = 7; 98 } else if (owner instanceof java.net.DatagramSocketImpl) { 99 tagType = 10; 100 } else if (owner instanceof java.net.SocketImpl) { 101 tagType = 11; 102 } else if (isParcelFileDescriptor(owner)) { 103 tagType = 8; 104 } else { 105 // Generic Java type. 106 tagType = 255; 107 } 108 109 // The owner ID is not required to be unique but should be stable and attempt to avoid 110 // collision with identifiers generated both here and in native code (which are simply the 111 // address of the owning object). identityHashCode(Object) meets these requirements. 112 long tagValue = System.identityHashCode(owner); 113 return tagType << 56 | tagValue; 114 } 115 116 /** 117 * Assigns ownership of an unowned FileDescriptor. 118 * 119 * Associates the supplied FileDescriptor and the underlying Unix file descriptor with an owner 120 * ID derived from the supplied {@code owner} object. If the FileDescriptor already has an 121 * associated owner an {@link IllegalStateException} will be thrown. If the underlying Unix 122 * file descriptor already has an associated owner, the process will abort. 123 * 124 * See bionic/include/android/fdsan.h for more details. 125 * 126 * @param fd FileDescriptor to take ownership from, must be non-null. 127 * @throws NullPointerException if fd or owner are null 128 * @throws IllegalStateException if fd is already owned 129 */ 130 @libcore.api.CorePlatformApi setFdOwner(@onNull FileDescriptor fd, @NonNull Object owner)131 public static void setFdOwner(@NonNull FileDescriptor fd, @NonNull Object owner) { 132 Objects.requireNonNull(fd); 133 Objects.requireNonNull(owner); 134 135 long previousOwnerId = fd.getOwnerId$(); 136 if (previousOwnerId != FileDescriptor.NO_OWNER) { 137 throw new IllegalStateException("Attempted to take ownership of already-owned " + 138 "FileDescriptor"); 139 } 140 141 long ownerId = generateFdOwnerId(owner); 142 fd.setOwnerId$(ownerId); 143 144 // Set the file descriptor's owner ID, aborting if the previous value isn't as expected. 145 Libcore.os.android_fdsan_exchange_owner_tag(fd, previousOwnerId, ownerId); 146 } 147 148 /** 149 * Calls close(2) on 'fd'. Also resets the internal int to -1. Does nothing if 'fd' is null 150 * or invalid. 151 */ 152 @libcore.api.CorePlatformApi close(FileDescriptor fd)153 public static void close(FileDescriptor fd) throws IOException { 154 IoBridge.closeAndSignalBlockedThreads(fd); 155 } 156 157 /** 158 * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null. 159 */ 160 @UnsupportedAppUsage 161 @libcore.api.CorePlatformApi closeQuietly(AutoCloseable closeable)162 public static void closeQuietly(AutoCloseable closeable) { 163 if (closeable != null) { 164 try { 165 closeable.close(); 166 } catch (RuntimeException rethrown) { 167 throw rethrown; 168 } catch (Exception ignored) { 169 } 170 } 171 } 172 173 /** 174 * Closes 'fd', ignoring any exceptions. Does nothing if 'fd' is null or invalid. 175 */ 176 @UnsupportedAppUsage 177 @libcore.api.CorePlatformApi closeQuietly(FileDescriptor fd)178 public static void closeQuietly(FileDescriptor fd) { 179 try { 180 IoUtils.close(fd); 181 } catch (IOException ignored) { 182 } 183 } 184 185 /** 186 * Closes 'socket', ignoring any exceptions. Does nothing if 'socket' is null. 187 */ 188 @UnsupportedAppUsage 189 @libcore.api.CorePlatformApi closeQuietly(Socket socket)190 public static void closeQuietly(Socket socket) { 191 if (socket != null) { 192 try { 193 socket.close(); 194 } catch (Exception ignored) { 195 } 196 } 197 } 198 199 /** 200 * Sets 'fd' to be blocking or non-blocking, according to the state of 'blocking'. 201 */ 202 @UnsupportedAppUsage 203 @libcore.api.CorePlatformApi setBlocking(FileDescriptor fd, boolean blocking)204 public static void setBlocking(FileDescriptor fd, boolean blocking) throws IOException { 205 try { 206 int flags = Libcore.os.fcntlVoid(fd, F_GETFL); 207 if (!blocking) { 208 flags |= O_NONBLOCK; 209 } else { 210 flags &= ~O_NONBLOCK; 211 } 212 Libcore.os.fcntlInt(fd, F_SETFL, flags); 213 } catch (ErrnoException errnoException) { 214 throw errnoException.rethrowAsIOException(); 215 } 216 } 217 218 /** 219 * Returns the contents of 'path' as a byte array. 220 */ 221 @UnsupportedAppUsage 222 @libcore.api.CorePlatformApi readFileAsByteArray(String absolutePath)223 public static byte[] readFileAsByteArray(String absolutePath) throws IOException { 224 return new FileReader(absolutePath).readFully().toByteArray(); 225 } 226 227 /** 228 * Returns the contents of 'path' as a string. The contents are assumed to be UTF-8. 229 */ 230 @UnsupportedAppUsage 231 @libcore.api.CorePlatformApi readFileAsString(String absolutePath)232 public static String readFileAsString(String absolutePath) throws IOException { 233 return new FileReader(absolutePath).readFully().toString(StandardCharsets.UTF_8); 234 } 235 236 /** 237 * Do not use. Use createTemporaryDirectory instead. 238 * 239 * Used by frameworks/base unit tests to clean up a temporary directory. 240 * Deliberately ignores errors, on the assumption that test cleanup is only 241 * supposed to be best-effort. 242 * 243 * @deprecated Use {@link TestIoUtils#createTemporaryDirectory} instead. 244 */ 245 @libcore.api.CorePlatformApi 246 @Deprecated deleteContents(File dir)247 public static void deleteContents(File dir) throws IOException { 248 File[] files = dir.listFiles(); 249 if (files != null) { 250 for (File file : files) { 251 if (file.isDirectory()) { 252 deleteContents(file); 253 } 254 file.delete(); 255 } 256 } 257 } 258 259 /** 260 * Creates a unique new temporary directory under "java.io.tmpdir". 261 * 262 * @deprecated Use {@link TestIoUtils#createTemporaryDirectory} instead. 263 */ 264 @libcore.api.CorePlatformApi 265 @Deprecated createTemporaryDirectory(String prefix)266 public static File createTemporaryDirectory(String prefix) { 267 while (true) { 268 String candidateName = prefix + Math.randomIntInternal(); 269 File result = new File(System.getProperty("java.io.tmpdir"), candidateName); 270 if (result.mkdir()) { 271 return result; 272 } 273 } 274 } 275 276 /** 277 * Do not use. This is for System.loadLibrary use only. 278 * 279 * Checks whether {@code path} can be opened read-only. Similar to File.exists, but doesn't 280 * require read permission on the parent, so it'll work in more cases, and allow you to 281 * remove read permission from more directories. Everyone else should just open(2) and then 282 * use the fd, but the loadLibrary API is broken by its need to ask ClassLoaders where to 283 * find a .so rather than just calling dlopen(3). 284 */ canOpenReadOnly(String path)285 public static boolean canOpenReadOnly(String path) { 286 try { 287 // Use open(2) rather than stat(2) so we require fewer permissions. http://b/6485312. 288 FileDescriptor fd = Libcore.os.open(path, O_RDONLY, 0); 289 Libcore.os.close(fd); 290 return true; 291 } catch (ErrnoException errnoException) { 292 return false; 293 } 294 } 295 throwInterruptedIoException()296 public static void throwInterruptedIoException() throws InterruptedIOException { 297 // This is typically thrown in response to an 298 // InterruptedException which does not leave the thread in an 299 // interrupted state, so explicitly interrupt here. 300 Thread.currentThread().interrupt(); 301 // TODO: set InterruptedIOException.bytesTransferred 302 throw new InterruptedIOException(); 303 } 304 305 /** 306 * A convenience class for reading the contents of a file into a {@code String} 307 * or a {@code byte[]}. This class attempts to minimize the number of allocations 308 * and copies required to read this data. 309 * 310 * For the case where we know the "true" length of a file (most ordinary files) 311 * we allocate exactly one byte[] and copy data into that. Calls to 312 * {@link #toByteArray} will then return the internal array and <b>not</b> a copy. 313 * 314 * <b>Note that an absolute path must be supplied. Expect your reads to fail 315 * if one isn't.</b> 316 */ 317 private static class FileReader { 318 private FileDescriptor fd; 319 private boolean unknownLength; 320 321 private byte[] bytes; 322 private int count; 323 FileReader(String absolutePath)324 public FileReader(String absolutePath) throws IOException { 325 // We use IoBridge.open because callers might differentiate 326 // between a FileNotFoundException and a general IOException. 327 // 328 // NOTE: This costs us an additional call to fstat(2) to test whether 329 // "absolutePath" is a directory or not. We can eliminate it 330 // at the cost of copying some code from IoBridge.open. 331 try { 332 fd = IoBridge.open(absolutePath, O_RDONLY); 333 } catch (FileNotFoundException fnfe) { 334 throw fnfe; 335 } 336 337 int capacity; 338 try { 339 final StructStat stat = Libcore.os.fstat(fd); 340 // Like RAF & other APIs, we assume that the file size fits 341 // into a 32 bit integer. 342 capacity = (int) stat.st_size; 343 if (capacity == 0) { 344 unknownLength = true; 345 capacity = 8192; 346 } 347 } catch (ErrnoException exception) { 348 closeQuietly(fd); 349 throw exception.rethrowAsIOException(); 350 } 351 352 bytes = new byte[capacity]; 353 } 354 readFully()355 public FileReader readFully() throws IOException { 356 int read; 357 int capacity = bytes.length; 358 try { 359 while ((read = Libcore.os.read(fd, bytes, count, capacity - count)) != 0) { 360 count += read; 361 if (count == capacity) { 362 if (unknownLength) { 363 // If we don't know the length of this file, we need to continue 364 // reading until we reach EOF. Double the capacity in preparation. 365 final int newCapacity = capacity * 2; 366 byte[] newBytes = new byte[newCapacity]; 367 System.arraycopy(bytes, 0, newBytes, 0, capacity); 368 bytes = newBytes; 369 capacity = newCapacity; 370 } else { 371 // We know the length of this file and we've read the right number 372 // of bytes from it, return. 373 break; 374 } 375 } 376 } 377 378 return this; 379 } catch (ErrnoException e) { 380 throw e.rethrowAsIOException(); 381 } finally { 382 closeQuietly(fd); 383 } 384 } 385 386 @FindBugsSuppressWarnings("EI_EXPOSE_REP") toByteArray()387 public byte[] toByteArray() { 388 if (count == bytes.length) { 389 return bytes; 390 } 391 byte[] result = new byte[count]; 392 System.arraycopy(bytes, 0, result, 0, count); 393 return result; 394 } 395 toString(Charset cs)396 public String toString(Charset cs) { 397 return new String(bytes, 0, count, cs); 398 } 399 } 400 } 401