Home | History | Annotate | Download | only in io
      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 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      */
     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      */
     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      */
     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      */
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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 
    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 
    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")
    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 
    271         public String toString(Charset cs) {
    272             return new String(bytes, 0, count, cs);
    273         }
    274     }
    275 }
    276