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