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