diff options
10 files changed, 154 insertions, 165 deletions
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index e6b1c07846f9..14005b31903a 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -54,7 +54,6 @@ import android.provider.DocumentsContract.Document; 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; @@ -1535,7 +1534,6 @@ public final class FileUtils { } /** {@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++) { @@ -1570,7 +1568,6 @@ public final class FileUtils { } /** {@hide} */ - @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class) public static String translateModePosixToString(int mode) { String res = ""; if ((mode & O_ACCMODE) == O_RDWR) { @@ -1592,7 +1589,6 @@ public final class FileUtils { } /** {@hide} */ - @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class) public static int translateModePosixToPfd(int mode) { int res = 0; if ((mode & O_ACCMODE) == O_RDWR) { @@ -1617,7 +1613,6 @@ public final class FileUtils { } /** {@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) { @@ -1642,7 +1637,6 @@ public final class FileUtils { } /** {@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 diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 464df239b8fd..4cc057a8e0ed 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -340,7 +340,6 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { return pfd; } - @RavenwoodReplace private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException { if ((mode & MODE_WRITE_ONLY) != 0 && (mode & MODE_APPEND) == 0 && (mode & MODE_TRUNCATE) == 0 && ((mode & MODE_READ_ONLY) == 0) @@ -364,26 +363,16 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { } } - private static FileDescriptor openInternal$ravenwood(File file, int mode) - throws FileNotFoundException { - try { - return native_open$ravenwood(file, mode); - } catch (FileNotFoundException e) { - throw e; - } catch (IOException e) { - throw new FileNotFoundException(e.getMessage()); - } - } - @RavenwoodReplace private static void closeInternal(FileDescriptor fd) { IoUtils.closeQuietly(fd); } private static void closeInternal$ravenwood(FileDescriptor fd) { - // Desktop JVM doesn't have FileDescriptor.close(), so we'll need to go to the ravenwood - // side to close it. - native_close$ravenwood(fd); + try { + Os.close(fd); + } catch (ErrnoException ignored) { + } } /** @@ -743,7 +732,6 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { * Return the total size of the file representing this fd, as determined by * {@code stat()}. Returns -1 if the fd is not a file. */ - @RavenwoodThrow(reason = "Os.readlink() and Os.stat()") public long getStatSize() { if (mWrapped != null) { return mWrapped.getStatSize(); @@ -1277,32 +1265,19 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { } } - // These native methods are currently only implemented by Ravenwood, as it's the only - // mechanism we have to jump to our RavenwoodNativeSubstitutionClass - private static native void native_setFdInt$ravenwood(FileDescriptor fd, int fdInt); - private static native int native_getFdInt$ravenwood(FileDescriptor fd); - private static native FileDescriptor native_open$ravenwood(File file, int pfdMode) - throws IOException; - private static native void native_close$ravenwood(FileDescriptor fd); + private static native void setFdInt$ravenwood(FileDescriptor fd, int fdInt); + private static native int getFdInt$ravenwood(FileDescriptor fd); @RavenwoodReplace private static void setFdInt(FileDescriptor fd, int fdInt) { fd.setInt$(fdInt); } - private static void setFdInt$ravenwood(FileDescriptor fd, int fdInt) { - native_setFdInt$ravenwood(fd, fdInt); - } - @RavenwoodReplace private static int getFdInt(FileDescriptor fd) { return fd.getInt$(); } - private static int getFdInt$ravenwood(FileDescriptor fd) { - return native_getFdInt$ravenwood(fd); - } - @RavenwoodReplace private void setFdOwner(FileDescriptor fd) { IoUtils.setFdOwner(fd, this); @@ -1320,7 +1295,6 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { private int acquireRawFd$ravenwood(FileDescriptor fd) { // FD owners currently unsupported under Ravenwood; return FD directly return getFdInt(fd); - } @RavenwoodReplace diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java index 78a2c1c4ddbe..b25263c138b3 100644 --- a/core/tests/coretests/src/android/os/FileUtilsTest.java +++ b/core/tests/coretests/src/android/os/FileUtilsTest.java @@ -54,7 +54,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.os.FileUtils.MemoryPipe; -import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.annotations.DisabledOnRavenwood; import android.platform.test.ravenwood.RavenwoodRule; import android.provider.DocumentsContract.Document; import android.system.Os; @@ -158,7 +158,7 @@ public class FileUtilsTest { } @Test - @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class) + @DisabledOnRavenwood(blockedBy = MemoryPipe.class) public void testCopy_FileToPipe() throws Exception { for (int size : DATA_SIZES) { final File src = new File(mTarget, "src"); @@ -179,7 +179,7 @@ public class FileUtilsTest { } @Test - @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class) + @DisabledOnRavenwood(blockedBy = MemoryPipe.class) public void testCopy_PipeToFile() throws Exception { for (int size : DATA_SIZES) { final File dest = new File(mTarget, "dest"); @@ -199,7 +199,7 @@ public class FileUtilsTest { } @Test - @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class) + @DisabledOnRavenwood(blockedBy = MemoryPipe.class) public void testCopy_PipeToPipe() throws Exception { for (int size : DATA_SIZES) { byte[] expected = new byte[size]; @@ -217,7 +217,7 @@ public class FileUtilsTest { } @Test - @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class) + @DisabledOnRavenwood(blockedBy = MemoryPipe.class) public void testCopy_ShortPipeToFile() throws Exception { byte[] source = new byte[33_000_000]; new Random().nextBytes(source); @@ -259,9 +259,9 @@ public class FileUtilsTest { assertArrayEquals(expected, actual); } - //TODO(ravenwood) Remove the _$noRavenwood suffix and add @RavenwoodIgnore instead @Test - public void testCopy_SocketToFile_FileToSocket$noRavenwood() throws Exception { + @DisabledOnRavenwood(reason = "Missing Os methods in Ravenwood") + public void testCopy_SocketToFile_FileToSocket() throws Exception { for (int size : DATA_SIZES ) { final File src = new File(mTarget, "src"); final File dest = new File(mTarget, "dest"); @@ -512,7 +512,7 @@ public class FileUtilsTest { } @Test - @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class) + @DisabledOnRavenwood(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")); @@ -532,7 +532,7 @@ public class FileUtilsTest { } @Test - @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class) + @DisabledOnRavenwood(blockedBy = android.webkit.MimeTypeMap.class) public void testBuildUniqueFile_unknown() throws Exception { assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test")); @@ -546,7 +546,7 @@ public class FileUtilsTest { } @Test - @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class) + @DisabledOnRavenwood(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(); @@ -561,7 +561,7 @@ public class FileUtilsTest { } @Test - @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class) + @DisabledOnRavenwood(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(); @@ -581,7 +581,7 @@ public class FileUtilsTest { } @Test - @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class) + @DisabledOnRavenwood(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(); @@ -677,8 +677,7 @@ public class FileUtilsTest { } @Test - @IgnoreUnderRavenwood(reason = "Requires kernel support") - public void testTranslateMode() throws Exception { + public void testTranslateMode() { assertTranslate("r", O_RDONLY, MODE_READ_ONLY); assertTranslate("rw", O_RDWR | O_CREAT, @@ -697,8 +696,7 @@ public class FileUtilsTest { } @Test - @IgnoreUnderRavenwood(reason = "Requires kernel support") - public void testMalformedTransate_int() throws Exception { + public void testMalformedTransate_int() { try { // The non-standard Linux access mode 3 should throw // an IllegalArgumentException. @@ -709,8 +707,7 @@ public class FileUtilsTest { } @Test - @IgnoreUnderRavenwood(reason = "Requires kernel support") - public void testMalformedTransate_string() throws Exception { + public void testMalformedTransate_string() { try { // The non-standard Linux access mode 3 should throw // an IllegalArgumentException. @@ -721,8 +718,7 @@ public class FileUtilsTest { } @Test - @IgnoreUnderRavenwood(reason = "Requires kernel support") - public void testTranslateMode_Invalid() throws Exception { + public void testTranslateMode_Invalid() { try { translateModeStringToPosix("rwx"); fail(); @@ -736,8 +732,7 @@ public class FileUtilsTest { } @Test - @IgnoreUnderRavenwood(reason = "Requires kernel support") - public void testTranslateMode_Access() throws Exception { + public void testTranslateMode_Access() { assertEquals(O_RDONLY, translateModeAccessToPosix(F_OK)); assertEquals(O_RDONLY, translateModeAccessToPosix(R_OK)); assertEquals(O_WRONLY, translateModeAccessToPosix(W_OK)); @@ -746,7 +741,7 @@ public class FileUtilsTest { } @Test - @IgnoreUnderRavenwood(reason = "Requires kernel support") + @DisabledOnRavenwood(reason = "Requires kernel support") public void testConvertToModernFd() throws Exception { final String nonce = String.valueOf(System.nanoTime()); diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java index ee280991216a..0238baa2dcbf 100644 --- a/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java +++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java @@ -16,6 +16,7 @@ package com.android.ravenwood.common; import java.io.FileDescriptor; +import java.io.IOException; /** * Collection of methods to workaround limitation in the hostside JVM. @@ -44,6 +45,11 @@ public abstract class JvmWorkaround { public abstract int getFdInt(FileDescriptor fd); /** + * Equivalent to Android's Os.close(fd). + */ + public abstract void closeFd(FileDescriptor fd) throws IOException; + + /** * Placeholder implementation for the host side. * * Even on the host side, we don't want to throw just because the class is loaded, @@ -64,5 +70,10 @@ public abstract class JvmWorkaround { public int getFdInt(FileDescriptor fd) { throw calledOnHostside(); } + + @Override + public void closeFd(FileDescriptor fd) { + throw calledOnHostside(); + } } } diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java index 9aedaab5b911..a260147654cd 100644 --- a/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java +++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java @@ -16,6 +16,8 @@ package com.android.ravenwood.common; import java.io.FileDescriptor; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; class OpenJdkWorkaround extends JvmWorkaround { @Override @@ -43,4 +45,19 @@ class OpenJdkWorkaround extends JvmWorkaround { + " perhaps JRE has changed?", e); } } + + @Override + public void closeFd(FileDescriptor fd) throws IOException { + try { + final Object obj = Class.forName("jdk.internal.access.SharedSecrets").getMethod( + "getJavaIOFileDescriptorAccess").invoke(null); + Class.forName("jdk.internal.access.JavaIOFileDescriptorAccess").getMethod( + "close", FileDescriptor.class).invoke(obj, fd); + } catch (InvocationTargetException e) { + SneakyThrow.sneakyThrow(e.getTargetException()); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to interact with raw FileDescriptor internals;" + + " perhaps JRE has changed?", e); + } + } } diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/SneakyThrow.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/SneakyThrow.java new file mode 100644 index 000000000000..0dbf7df12bce --- /dev/null +++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/SneakyThrow.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 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 com.android.ravenwood.common; + +public class SneakyThrow { + + private SneakyThrow() { + } + + /** + * Throw checked exceptions without the need to declare in method signature + */ + public static void sneakyThrow(Throwable t) { + SneakyThrow.<RuntimeException>sneakyThrow_(t); + } + + private static <T extends Throwable> void sneakyThrow_(Throwable t) throws T { + throw (T) t; + } +} diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java index 1a15d7a8c19e..5a3589dae43a 100644 --- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java +++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java @@ -16,105 +16,16 @@ package com.android.platform.test.ravenwood.nativesubstitution; -import static android.os.ParcelFileDescriptor.MODE_APPEND; -import static android.os.ParcelFileDescriptor.MODE_CREATE; -import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; -import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; -import static android.os.ParcelFileDescriptor.MODE_TRUNCATE; -import static android.os.ParcelFileDescriptor.MODE_WORLD_READABLE; -import static android.os.ParcelFileDescriptor.MODE_WORLD_WRITEABLE; -import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY; - -import android.system.ErrnoException; -import android.system.Os; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; import com.android.ravenwood.common.JvmWorkaround; -import java.io.File; import java.io.FileDescriptor; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.HashMap; -import java.util.Map; public class ParcelFileDescriptor_host { - private static final String TAG = "ParcelFileDescriptor_host"; - - /** - * Since we don't have a great way to keep an unmanaged {@code FileDescriptor} reference - * alive, we keep a strong reference to the {@code RandomAccessFile} we used to open it. This - * gives us a way to look up the original parent object when closing later. - */ - @GuardedBy("sActive") - private static final Map<FileDescriptor, RandomAccessFile> sActive = new HashMap<>(); - - public static void native_setFdInt$ravenwood(FileDescriptor fd, int fdInt) { + public static void setFdInt(FileDescriptor fd, int fdInt) { JvmWorkaround.getInstance().setFdInt(fd, fdInt); } - public static int native_getFdInt$ravenwood(FileDescriptor fd) { + public static int getFdInt(FileDescriptor fd) { return JvmWorkaround.getInstance().getFdInt(fd); } - - public static FileDescriptor native_open$ravenwood(File file, int pfdMode) throws IOException { - if ((pfdMode & MODE_CREATE) != 0 && !file.exists()) { - throw new FileNotFoundException(); - } - - final String modeString; - if ((pfdMode & MODE_READ_WRITE) == MODE_READ_WRITE) { - modeString = "rw"; - } else if ((pfdMode & MODE_WRITE_ONLY) == MODE_WRITE_ONLY) { - modeString = "rw"; - } else if ((pfdMode & MODE_READ_ONLY) == MODE_READ_ONLY) { - modeString = "r"; - } else { - throw new IllegalArgumentException(); - } - - final RandomAccessFile raf = new RandomAccessFile(file, modeString); - - // Now that we have a real file on disk, match requested flags - if ((pfdMode & MODE_TRUNCATE) != 0) { - raf.setLength(0); - } - if ((pfdMode & MODE_APPEND) != 0) { - raf.seek(raf.length()); - } - if ((pfdMode & MODE_WORLD_READABLE) != 0) { - file.setReadable(true, false); - } - if ((pfdMode & MODE_WORLD_WRITEABLE) != 0) { - file.setWritable(true, false); - } - - final FileDescriptor fd = raf.getFD(); - synchronized (sActive) { - sActive.put(fd, raf); - } - return fd; - } - - public static void native_close$ravenwood(FileDescriptor fd) { - final RandomAccessFile raf; - synchronized (sActive) { - raf = sActive.remove(fd); - } - int fdInt = JvmWorkaround.getInstance().getFdInt(fd); - try { - if (raf != null) { - raf.close(); - } else { - // This FD wasn't created by native_open$ravenwood(). - // The FD was passed to the PFD ctor. Just close it. - Os.close(fd); - } - } catch (IOException | ErrnoException e) { - Log.w(TAG, "Exception thrown while closing fd " + fdInt, e); - } - } } -;
\ No newline at end of file diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java index 825ab72e773a..ecaa8161ee46 100644 --- a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java +++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java @@ -15,9 +15,11 @@ */ package android.system; +import com.android.ravenwood.common.JvmWorkaround; import com.android.ravenwood.common.RavenwoodRuntimeNative; import java.io.FileDescriptor; +import java.io.IOException; /** * OS class replacement used on Ravenwood. For now, we just implement APIs as we need them... @@ -56,6 +58,15 @@ public final class Os { /** Ravenwood version of the OS API. */ public static void close(FileDescriptor fd) throws ErrnoException { - RavenwoodRuntimeNative.close(fd); + try { + JvmWorkaround.getInstance().closeFd(fd); + } catch (IOException e) { + // The only valid error on Linux that can happen is EIO + throw new ErrnoException("close", OsConstants.EIO); + } + } + + public static FileDescriptor open(String path, int flags, int mode) throws ErrnoException { + return RavenwoodRuntimeNative.open(path, flags, mode); } } diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/common/RavenwoodRuntimeNative.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/common/RavenwoodRuntimeNative.java index 2bc8e7123aad..beba83391652 100644 --- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/common/RavenwoodRuntimeNative.java +++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/common/RavenwoodRuntimeNative.java @@ -48,7 +48,7 @@ public class RavenwoodRuntimeNative { public static native StructStat stat(String path) throws ErrnoException; - private static native void nClose(int fd) throws ErrnoException; + private static native int nOpen(String path, int flags, int mode) throws ErrnoException; public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException { return nLseek(JvmWorkaround.getInstance().getFdInt(fd), offset, whence); @@ -69,7 +69,7 @@ public class RavenwoodRuntimeNative { public static FileDescriptor dup(FileDescriptor fd) throws ErrnoException { var fdInt = nDup(JvmWorkaround.getInstance().getFdInt(fd)); - var retFd = new java.io.FileDescriptor(); + var retFd = new FileDescriptor(); JvmWorkaround.getInstance().setFdInt(retFd, fdInt); return retFd; } @@ -86,10 +86,11 @@ public class RavenwoodRuntimeNative { return nFstat(fdInt); } - /** See close(2) */ - public static void close(FileDescriptor fd) throws ErrnoException { - var fdInt = JvmWorkaround.getInstance().getFdInt(fd); - - nClose(fdInt); + public static FileDescriptor open(String path, int flags, int mode) throws ErrnoException { + int fd = nOpen(path, flags, mode); + if (fd < 0) return null; + var retFd = new FileDescriptor(); + JvmWorkaround.getInstance().setFdInt(retFd, fd); + return retFd; } } diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp index ee84954a5c2a..c8049281bc53 100644 --- a/ravenwood/runtime-jni/ravenwood_runtime.cpp +++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp @@ -18,9 +18,11 @@ #include <sys/stat.h> #include <string.h> #include <unistd.h> +#include <string> #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedLocalRef.h> #include <nativehelper/ScopedUtfChars.h> +#include <nativehelper/ScopedPrimitiveArray.h> #include "jni.h" #include "utils/Log.h" @@ -49,6 +51,43 @@ static rc_t throwIfMinusOne(JNIEnv* env, const char* name, rc_t rc) { static jclass g_StructStat; static jclass g_StructTimespecClass; +// We have to explicitly decode the string to real UTF-8, because when using GetStringUTFChars +// we only get modified UTF-8, which is not the platform string type used in host JVM. +struct ScopedRealUtf8Chars { + ScopedRealUtf8Chars(JNIEnv* env, jstring s) : valid_(false) { + if (s == nullptr) { + jniThrowNullPointerException(env); + return; + } + jclass clazz = env->GetObjectClass(s); + jmethodID getBytes = env->GetMethodID(clazz, "getBytes", "(Ljava/lang/String;)[B"); + + ScopedLocalRef<jstring> utf8(env, env->NewStringUTF("UTF-8")); + ScopedLocalRef<jbyteArray> jbytes(env, + (jbyteArray) env->CallObjectMethod(s, getBytes, utf8.get())); + + ScopedByteArrayRO bytes(env, jbytes.get()); + string_.append((const char *) bytes.get(), bytes.size()); + valid_ = true; + } + + const char* c_str() const { + return valid_ ? string_.c_str() : nullptr; + } + + size_t size() const { + return string_.size(); + } + + const char& operator[](size_t n) const { + return string_[n]; + } + +private: + std::string string_; + bool valid_; +}; + static jclass findClass(JNIEnv* env, const char* name) { ScopedLocalRef<jclass> localClass(env, env->FindClass(name)); jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get())); @@ -99,7 +138,7 @@ static jobject makeStructStat(JNIEnv* env, const struct stat64& sb) { } static jobject doStat(JNIEnv* env, jstring javaPath, bool isLstat) { - ScopedUtfChars path(env, javaPath); + ScopedRealUtf8Chars path(env, javaPath); if (path.c_str() == NULL) { return NULL; } @@ -167,9 +206,12 @@ static jobject Linux_stat(JNIEnv* env, jobject, jstring javaPath) { return doStat(env, javaPath, false); } -static void nClose(JNIEnv* env, jclass, jint fd) { - // Don't use TEMP_FAILURE_RETRY() on close(): https://lkml.org/lkml/2005/9/10/129 - throwIfMinusOne(env, "close", close(fd)); +static jint Linux_open(JNIEnv* env, jobject, jstring javaPath, jint flags, jint mode) { + ScopedRealUtf8Chars path(env, javaPath); + if (path.c_str() == NULL) { + return -1; + } + return throwIfMinusOne(env, "open", TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode))); } // ---- Registration ---- @@ -184,7 +226,7 @@ static const JNINativeMethod sMethods[] = { "nFstat", "(I)Landroid/system/StructStat;", (void*)nFstat }, { "lstat", "(Ljava/lang/String;)Landroid/system/StructStat;", (void*)Linux_lstat }, { "stat", "(Ljava/lang/String;)Landroid/system/StructStat;", (void*)Linux_stat }, - { "nClose", "(I)V", (void*)nClose }, + { "nOpen", "(Ljava/lang/String;II)I", (void*)Linux_open }, }; extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) |