Merge changes from topic "nov29" into main
* changes:
`LruCache` under Ravenwood.
`FileUtils` and `AtomicFile` under Ravenwood.
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 5b24dca..3a32b2b 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -52,9 +52,11 @@
import android.provider.MediaStore;
import android.system.ErrnoException;
import android.system.Os;
+import android.system.OsConstants;
import android.system.StructStat;
import android.text.TextUtils;
import android.util.DataUnit;
+import android.util.EmptyArray;
import android.util.Log;
import android.util.Slog;
import android.webkit.MimeTypeMap;
@@ -64,7 +66,6 @@
import com.android.internal.util.SizedInputStream;
import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
@@ -94,6 +95,7 @@
/**
* Utility methods useful for working with files.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FileUtils {
private static final String TAG = "FileUtils";
@@ -116,9 +118,6 @@
private FileUtils() {
}
- private static final String CAMERA_DIR_LOWER_CASE = "/storage/emulated/" + UserHandle.myUserId()
- + "/dcim/camera";
-
/** Regular expression for safe filenames: no spaces or metacharacters.
*
* Use a preload holder so that FileUtils can be compile-time initialized.
@@ -133,6 +132,21 @@
private static final long COPY_CHECKPOINT_BYTES = 524288;
+ static {
+ sEnableCopyOptimizations = shouldEnableCopyOptimizations();
+ }
+
+ @android.ravenwood.annotation.RavenwoodReplace
+ private static boolean shouldEnableCopyOptimizations() {
+ // Advanced copy operations enabled by default
+ return true;
+ }
+
+ private static boolean shouldEnableCopyOptimizations$ravenwood() {
+ // Disabled under Ravenwood due to missing kernel support
+ return false;
+ }
+
/**
* Listener that is called periodically as progress is made.
*/
@@ -150,6 +164,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
public static int setPermissions(File path, int mode, int uid, int gid) {
return setPermissions(path.getAbsolutePath(), mode, uid, gid);
}
@@ -164,6 +179,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
public static int setPermissions(String path, int mode, int uid, int gid) {
try {
Os.chmod(path, mode);
@@ -194,6 +210,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
public static int setPermissions(FileDescriptor fd, int mode, int uid, int gid) {
try {
Os.fchmod(fd, mode);
@@ -221,6 +238,7 @@
* @param to File where attributes should be copied to.
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
public static void copyPermissions(@NonNull File from, @NonNull File to) throws IOException {
try {
final StructStat stat = Os.stat(from.getAbsolutePath());
@@ -236,6 +254,7 @@
* @hide
*/
@Deprecated
+ @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
public static int getUid(String path) {
try {
return Os.stat(path).st_uid;
@@ -314,11 +333,7 @@
}
try (FileOutputStream out = new FileOutputStream(destFile)) {
copy(in, out);
- try {
- Os.fsync(out.getFD());
- } catch (ErrnoException e) {
- throw e.rethrowAsIOException();
- }
+ sync(out);
}
}
@@ -475,6 +490,7 @@
* @hide
*/
@VisibleForTesting
+ @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
public static long copyInternalSplice(FileDescriptor in, FileDescriptor out, long count,
CancellationSignal signal, Executor executor, ProgressListener listener)
throws ErrnoException {
@@ -516,6 +532,7 @@
* @hide
*/
@VisibleForTesting
+ @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
public static long copyInternalSendfile(FileDescriptor in, FileDescriptor out, long count,
CancellationSignal signal, Executor executor, ProgressListener listener)
throws ErrnoException {
@@ -699,6 +716,7 @@
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static void bytesToFile(String filename, byte[] content) throws IOException {
if (filename.startsWith("/proc/")) {
final int oldMask = StrictMode.allowThreadDiskWritesMask();
@@ -714,6 +732,14 @@
}
}
+ /** @hide */
+ public static void bytesToFile$ravenwood(String filename, byte[] content) throws IOException {
+ // No StrictMode support, so we can just directly write
+ try (FileOutputStream fos = new FileOutputStream(filename)) {
+ fos.write(content);
+ }
+ }
+
/**
* Writes string to file. Basically same as "echo -n $string > $filename"
*
@@ -1176,6 +1202,7 @@
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = MimeTypeMap.class)
public static String[] splitFileName(String mimeType, String displayName) {
String name;
String ext;
@@ -1423,11 +1450,13 @@
* indicate a failure to flush bytes to the underlying resource.
*/
@Deprecated
+ @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires ART support")
public static void closeQuietly(@Nullable FileDescriptor fd) {
IoUtils.closeQuietly(fd);
}
/** {@hide} */
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class)
public static int translateModeStringToPosix(String mode) {
// Quick check for invalid chars
for (int i = 0; i < mode.length(); i++) {
@@ -1462,6 +1491,7 @@
}
/** {@hide} */
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class)
public static String translateModePosixToString(int mode) {
String res = "";
if ((mode & O_ACCMODE) == O_RDWR) {
@@ -1483,6 +1513,7 @@
}
/** {@hide} */
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class)
public static int translateModePosixToPfd(int mode) {
int res = 0;
if ((mode & O_ACCMODE) == O_RDWR) {
@@ -1507,6 +1538,7 @@
}
/** {@hide} */
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class)
public static int translateModePfdToPosix(int mode) {
int res = 0;
if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) {
@@ -1531,6 +1563,7 @@
}
/** {@hide} */
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class)
public static int translateModeAccessToPosix(int mode) {
if (mode == F_OK) {
// There's not an exact mapping, so we attempt a read-only open to
@@ -1549,6 +1582,7 @@
/** {@hide} */
@VisibleForTesting
+ @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
public static ParcelFileDescriptor convertToModernFd(FileDescriptor fd) {
Context context = AppGlobals.getInitialApplication();
if (UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) {
@@ -1565,6 +1599,7 @@
}
}
+ @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
private static int getMediaProviderAppId(Context context) {
if (sMediaProviderAppId != -1) {
return sMediaProviderAppId;
@@ -1605,10 +1640,12 @@
return this;
}
+ @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
public static MemoryPipe createSource(byte[] data) throws IOException {
return new MemoryPipe(data, false).startInternal();
}
+ @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
public static MemoryPipe createSink(byte[] data) throws IOException {
return new MemoryPipe(data, true).startInternal();
}
diff --git a/core/java/android/util/AtomicFile.java b/core/java/android/util/AtomicFile.java
index d084a9e..36800ea 100644
--- a/core/java/android/util/AtomicFile.java
+++ b/core/java/android/util/AtomicFile.java
@@ -48,6 +48,7 @@
* be accessed or modified concurrently by multiple threads or processes. The caller is responsible
* for ensuring appropriate mutual exclusion invariants whenever it accesses the file.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AtomicFile {
private static final String LOG_TAG = "AtomicFile";
@@ -68,6 +69,8 @@
* @hide Internal constructor that also allows you to have the class
* automatically log commit events.
*/
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy =
+ SystemConfigFileCommitEventLogger.class)
public AtomicFile(File baseName, String commitTag) {
this(baseName, new SystemConfigFileCommitEventLogger(commitTag));
}
diff --git a/core/java/android/util/LruCache.java b/core/java/android/util/LruCache.java
index 3f7fdd8..be1ec41 100644
--- a/core/java/android/util/LruCache.java
+++ b/core/java/android/util/LruCache.java
@@ -18,6 +18,7 @@
import android.compat.annotation.UnsupportedAppUsage;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -61,6 +62,7 @@
* of <a href="http://developer.android.com/sdk/compatibility-library.html">Android's
* Support Package</a> for earlier releases.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LruCache<K, V> {
@UnsupportedAppUsage
private final LinkedHashMap<K, V> map;
@@ -208,7 +210,7 @@
break;
}
- Map.Entry<K, V> toEvict = map.eldest();
+ Map.Entry<K, V> toEvict = eldest();
if (toEvict == null) {
break;
}
@@ -224,6 +226,16 @@
}
}
+ @android.ravenwood.annotation.RavenwoodReplace
+ private Map.Entry<K, V> eldest() {
+ return map.eldest();
+ }
+
+ private Map.Entry<K, V> eldest$ravenwood() {
+ final Iterator<Map.Entry<K, V>> it = map.entrySet().iterator();
+ return it.hasNext() ? it.next() : null;
+ }
+
/**
* Removes the entry for {@code key} if it exists.
*
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 445ddf5..37f592f 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -66,6 +66,7 @@
"testables",
"com.android.text.flags-aconfig-java",
"flag-junit",
+ "ravenwood-junit",
],
libs: [
@@ -163,3 +164,15 @@
"framework-res",
],
}
+
+android_ravenwood_test {
+ name: "FrameworksCoreTestsRavenwood",
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.test.rules",
+ ],
+ srcs: [
+ "src/android/os/FileUtilsTest.java",
+ ],
+ auto_gen_config: true,
+}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index a0d8183..60500d5 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -51,20 +51,17 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.content.Context;
import android.os.FileUtils.MemoryPipe;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.provider.DocumentsContract.Document;
import android.util.DataUnit;
-import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.google.android.collect.Sets;
-
-import libcore.io.Streams;
-
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -74,12 +71,19 @@
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
@RunWith(AndroidJUnit4.class)
public class FileUtilsTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private static final String TEST_DATA =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
@@ -90,17 +94,13 @@
private final int[] DATA_SIZES = { 32, 32_000, 32_000_000 };
- private Context getContext() {
- return InstrumentationRegistry.getContext();
- }
-
@Before
public void setUp() throws Exception {
- mDir = getContext().getDir("testing", Context.MODE_PRIVATE);
+ mDir = Files.createTempDirectory("FileUtils").toFile();
mTestFile = new File(mDir, "test.file");
mCopyFile = new File(mDir, "copy.file");
- mTarget = getContext().getFilesDir();
+ mTarget = mDir;
FileUtils.deleteContents(mTarget);
}
@@ -152,6 +152,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class)
public void testCopy_FileToPipe() throws Exception {
for (int size : DATA_SIZES) {
final File src = new File(mTarget, "src");
@@ -172,6 +173,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class)
public void testCopy_PipeToFile() throws Exception {
for (int size : DATA_SIZES) {
final File dest = new File(mTarget, "dest");
@@ -191,6 +193,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class)
public void testCopy_PipeToPipe() throws Exception {
for (int size : DATA_SIZES) {
byte[] expected = new byte[size];
@@ -208,6 +211,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class)
public void testCopy_ShortPipeToFile() throws Exception {
byte[] source = new byte[33_000_000];
new Random().nextBytes(source);
@@ -424,6 +428,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
public void testBuildUniqueFile_normal() throws Exception {
assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test"));
assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
@@ -443,6 +448,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
public void testBuildUniqueFile_unknown() throws Exception {
assertNameEquals("test",
FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test"));
@@ -456,6 +462,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
public void testBuildUniqueFile_dir() throws Exception {
assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
new File(mTarget, "test").mkdir();
@@ -470,6 +477,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
public void testBuildUniqueFile_increment() throws Exception {
assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
new File(mTarget, "test.jpg").createNewFile();
@@ -489,6 +497,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
public void testBuildUniqueFile_mimeless() throws Exception {
assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "test.jpg"));
new File(mTarget, "test.jpg").createNewFile();
@@ -584,6 +593,7 @@
}
@Test
+ @IgnoreUnderRavenwood(reason = "Requires kernel support")
public void testTranslateMode() throws Exception {
assertTranslate("r", O_RDONLY, MODE_READ_ONLY);
@@ -603,6 +613,7 @@
}
@Test
+ @IgnoreUnderRavenwood(reason = "Requires kernel support")
public void testMalformedTransate_int() throws Exception {
try {
// The non-standard Linux access mode 3 should throw
@@ -614,6 +625,7 @@
}
@Test
+ @IgnoreUnderRavenwood(reason = "Requires kernel support")
public void testMalformedTransate_string() throws Exception {
try {
// The non-standard Linux access mode 3 should throw
@@ -625,6 +637,7 @@
}
@Test
+ @IgnoreUnderRavenwood(reason = "Requires kernel support")
public void testTranslateMode_Invalid() throws Exception {
try {
translateModeStringToPosix("rwx");
@@ -639,6 +652,7 @@
}
@Test
+ @IgnoreUnderRavenwood(reason = "Requires kernel support")
public void testTranslateMode_Access() throws Exception {
assertEquals(O_RDONLY, translateModeAccessToPosix(F_OK));
assertEquals(O_RDONLY, translateModeAccessToPosix(R_OK));
@@ -648,6 +662,7 @@
}
@Test
+ @IgnoreUnderRavenwood(reason = "Requires kernel support")
public void testConvertToModernFd() throws Exception {
final String nonce = String.valueOf(System.nanoTime());
@@ -720,13 +735,24 @@
private byte[] readFile(File file) throws Exception {
try (FileInputStream in = new FileInputStream(file);
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
- Streams.copy(in, out);
+ copy(in, out);
return out.toByteArray();
}
}
+ private static int copy(InputStream in, OutputStream out) throws IOException {
+ int total = 0;
+ byte[] buffer = new byte[8192];
+ int c;
+ while ((c = in.read(buffer)) != -1) {
+ total += c;
+ out.write(buffer, 0, c);
+ }
+ return total;
+ }
+
private void assertDirContents(String... expected) {
- final HashSet<String> expectedSet = Sets.newHashSet(expected);
+ final HashSet<String> expectedSet = new HashSet<>(Arrays.asList(expected));
String[] actual = mDir.list();
if (actual == null) actual = new String[0];
diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp
index 06340a2..967047e 100644
--- a/core/tests/utiltests/Android.bp
+++ b/core/tests/utiltests/Android.bp
@@ -57,8 +57,10 @@
static_libs: [
"androidx.annotation_annotation",
"androidx.test.rules",
+ "mockito_ravenwood",
],
srcs: [
+ "src/android/util/AtomicFileTest.java",
"src/android/util/DataUnitTest.java",
"src/android/util/EventLogTest.java",
"src/android/util/IndentingPrintWriterTest.java",
diff --git a/core/tests/utiltests/src/android/util/AtomicFileTest.java b/core/tests/utiltests/src/android/util/AtomicFileTest.java
index 6f59714..742307b 100644
--- a/core/tests/utiltests/src/android/util/AtomicFileTest.java
+++ b/core/tests/utiltests/src/android/util/AtomicFileTest.java
@@ -23,16 +23,16 @@
import static org.mockito.ArgumentMatchers.longThat;
import static org.mockito.Mockito.spy;
-import android.app.Instrumentation;
-import android.content.Context;
import android.os.SystemClock;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -46,9 +46,13 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
@RunWith(Parameterized.class)
public class AtomicFileTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private static final String BASE_NAME = "base";
private static final String NEW_NAME = BASE_NAME + ".new";
private static final String LEGACY_BACKUP_NAME = BASE_NAME + ".bak";
@@ -80,14 +84,10 @@
@Parameterized.Parameter(3)
public byte[] mExpectedBytes;
- private final Instrumentation mInstrumentation =
- InstrumentationRegistry.getInstrumentation();
- private final Context mContext = mInstrumentation.getContext();
-
- private final File mDirectory = mContext.getFilesDir();
- private final File mBaseFile = new File(mDirectory, BASE_NAME);
- private final File mNewFile = new File(mDirectory, NEW_NAME);
- private final File mLegacyBackupFile = new File(mDirectory, LEGACY_BACKUP_NAME);
+ private File mDirectory;
+ private File mBaseFile;
+ private File mNewFile;
+ private File mLegacyBackupFile;
@Parameterized.Parameters(name = "{0}")
public static Object[][] data() {
@@ -199,6 +199,13 @@
}
@Before
+ public void setUp() throws Exception {
+ mDirectory = Files.createTempDirectory("AtomicFile").toFile();
+ mBaseFile = new File(mDirectory, BASE_NAME);
+ mNewFile = new File(mDirectory, NEW_NAME);
+ mLegacyBackupFile = new File(mDirectory, LEGACY_BACKUP_NAME);
+ }
+
@After
public void deleteFiles() {
mBaseFile.delete();
@@ -274,6 +281,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = SystemConfigFileCommitEventLogger.class)
public void testTimeLogging() throws Exception {
var logger = spy(new SystemConfigFileCommitEventLogger("name"));
var file = new AtomicFile(mBaseFile, logger);
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index e294733..eba6e0b 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -2,16 +2,20 @@
com.android.internal.util.ArrayUtils
+android.util.AtomicFile
android.util.DataUnit
android.util.EventLog
android.util.IntArray
android.util.LongArray
+android.util.LruCache
android.util.Slog
android.util.TimeUtils
android.util.Xml
android.os.Binder
android.os.Binder$IdentitySupplier
+android.os.FileUtils
+android.os.FileUtils$MemoryPipe
android.os.Handler
android.os.HandlerExecutor
android.os.HandlerThread
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index 5949bca..d415dc5 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -274,6 +274,21 @@
],
}
+java_library_host {
+ name: "hoststubgen-helper-libcore-runtime",
+ defaults: ["hoststubgen-for-prototype-only-java"],
+ srcs: [
+ "helper-framework-runtime-src/libcore-fake/**/*.java",
+ ],
+}
+
+java_host_for_device {
+ name: "hoststubgen-helper-libcore-runtime.ravenwood",
+ libs: [
+ "hoststubgen-helper-libcore-runtime",
+ ],
+}
+
java_library {
name: "hoststubgen-helper-framework-runtime.ravenwood",
defaults: ["hoststubgen-for-prototype-only-java"],
@@ -286,6 +301,7 @@
],
static_libs: [
"core-xml-for-device",
+ "hoststubgen-helper-libcore-runtime.ravenwood",
],
}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/android/system/ErrnoException.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/android/system/ErrnoException.java
new file mode 100644
index 0000000..388156a
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/android/system/ErrnoException.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2011 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 android.system;
+
+import java.io.IOException;
+import java.net.SocketException;
+
+/**
+ * A checked exception thrown when {@link Os} methods fail. This exception contains the native
+ * errno value, for comparison against the constants in {@link OsConstants}, should sophisticated
+ * callers need to adjust their behavior based on the exact failure.
+ */
+public final class ErrnoException extends Exception {
+ private final String functionName;
+
+ /**
+ * The errno value, for comparison with the {@code E} constants in {@link OsConstants}.
+ */
+ public final int errno;
+
+ /**
+ * Constructs an instance with the given function name and errno value.
+ */
+ public ErrnoException(String functionName, int errno) {
+ this.functionName = functionName;
+ this.errno = errno;
+ }
+
+ /**
+ * Constructs an instance with the given function name, errno value, and cause.
+ */
+ public ErrnoException(String functionName, int errno, Throwable cause) {
+ super(cause);
+ this.functionName = functionName;
+ this.errno = errno;
+ }
+
+ /**
+ * Converts the stashed function name and errno value to a human-readable string.
+ * We do this here rather than in the constructor so that callers only pay for
+ * this if they need it.
+ */
+ @Override public String getMessage() {
+ return functionName + " failed: " + errno;
+ }
+
+ /**
+ * Throws an {@link IOException} with a message based on {@link #getMessage()} and with this
+ * instance as the cause.
+ *
+ * <p>This method always terminates by throwing the exception. Callers can write
+ * {@code throw e.rethrowAsIOException()} to make that clear to the compiler.
+ */
+ public IOException rethrowAsIOException() throws IOException {
+ IOException newException = new IOException(getMessage());
+ newException.initCause(this);
+ throw newException;
+ }
+
+ /**
+ * Throws a {@link SocketException} with a message based on {@link #getMessage()} and with this
+ * instance as the cause.
+ *
+ * <p>This method always terminates by throwing the exception. Callers can write
+ * {@code throw e.rethrowAsIOException()} to make that clear to the compiler.
+ */
+ public SocketException rethrowAsSocketException() throws SocketException {
+ throw new SocketException(getMessage());
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/io/IoUtils.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/io/IoUtils.java
new file mode 100644
index 0000000..65c285e
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/io/IoUtils.java
@@ -0,0 +1,61 @@
+/*
+ * 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 java.io.File;
+import java.io.IOException;
+import java.net.Socket;
+
+/** @hide */
+public final class IoUtils {
+ private IoUtils() {
+ }
+
+ public static void closeQuietly(AutoCloseable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ public static void closeQuietly(Socket socket) {
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ 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();
+ }
+ }
+ }
+}