Support Parcel.write/readFileDescriptor
Flag: EXEMPT host side test change only
Bug: 292141694
Test: atest CtsOsTestCasesRavenwood
Change-Id: I848dc1ebc89c2beebce75ab8698066fc3b0bf72c
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 136c45d..47096db 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -434,7 +434,6 @@
@RavenwoodThrow
private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
@FastNative
- @RavenwoodThrow
private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
private static native byte[] nativeCreateByteArray(long nativePtr);
@@ -456,7 +455,6 @@
@RavenwoodThrow
private static native IBinder nativeReadStrongBinder(long nativePtr);
@FastNative
- @RavenwoodThrow
private static native FileDescriptor nativeReadFileDescriptor(long nativePtr);
private static native long nativeCreate();
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 71957ee..464df23 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -381,6 +381,8 @@
}
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);
}
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 8fe6853..1a15d7a 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
@@ -25,19 +25,24 @@
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.FileOutputStream;
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
@@ -98,16 +103,18 @@
synchronized (sActive) {
raf = sActive.remove(fd);
}
+ int fdInt = JvmWorkaround.getInstance().getFdInt(fd);
try {
if (raf != null) {
raf.close();
} else {
- // Odd, we don't remember opening this ourselves, but let's release the
- // underlying resource as requested
- System.err.println("Closing unknown FileDescriptor: " + fd);
- new FileOutputStream(fd).close();
+ // 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 ignored) {
+ } 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/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java
index 22e11e1..2df93cd 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java
@@ -15,6 +15,11 @@
*/
package com.android.platform.test.ravenwood.nativesubstitution;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import java.io.FileDescriptor;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@@ -31,6 +36,8 @@
* {@link ByteBuffer} wouldn't allow...)
*/
public class Parcel_host {
+ private static final String TAG = "Parcel";
+
private Parcel_host() {
}
@@ -50,6 +57,11 @@
// TODO Use the actual value from Parcel.java.
private static final int OK = 0;
+ private final Map<Integer, FileDescriptor> mFdMap = new ConcurrentHashMap<>();
+
+ private static final int FD_PLACEHOLDER = 0xDEADBEEF;
+ private static final int FD_PAYLOAD_SIZE = 8;
+
private void validate() {
if (mDeleted) {
// TODO: Put more info
@@ -67,6 +79,7 @@
return p;
}
+ /** Native method substitution */
public static long nativeCreate() {
final long id = sNextId.getAndIncrement();
final Parcel_host p = new Parcel_host();
@@ -80,7 +93,8 @@
mSize = 0;
mPos = 0;
mSensitive = false;
- mAllowFds = false;
+ mAllowFds = true;
+ mFdMap.clear();
}
private void updateSize() {
@@ -89,16 +103,19 @@
}
}
+ /** Native method substitution */
public static void nativeDestroy(long nativePtr) {
getInstance(nativePtr).mDeleted = true;
sInstances.remove(nativePtr);
}
+ /** Native method substitution */
public static void nativeFreeBuffer(long nativePtr) {
getInstance(nativePtr).freeBuffer();
}
- public void freeBuffer() {
+ /** Native method substitution */
+ private void freeBuffer() {
init();
}
@@ -137,32 +154,47 @@
}
}
+ /** Native method substitution */
public static void nativeMarkSensitive(long nativePtr) {
getInstance(nativePtr).mSensitive = true;
}
+
+ /** Native method substitution */
public static int nativeDataSize(long nativePtr) {
return getInstance(nativePtr).mSize;
}
+
+ /** Native method substitution */
public static int nativeDataAvail(long nativePtr) {
var p = getInstance(nativePtr);
return p.mSize - p.mPos;
}
+
+ /** Native method substitution */
public static int nativeDataPosition(long nativePtr) {
return getInstance(nativePtr).mPos;
}
+
+ /** Native method substitution */
public static int nativeDataCapacity(long nativePtr) {
return getInstance(nativePtr).mBuffer.length;
}
+
+ /** Native method substitution */
public static void nativeSetDataSize(long nativePtr, int size) {
var p = getInstance(nativePtr);
p.ensureCapacity(size);
getInstance(nativePtr).mSize = size;
}
+
+ /** Native method substitution */
public static void nativeSetDataPosition(long nativePtr, int pos) {
var p = getInstance(nativePtr);
// TODO: Should this change the size or the capacity??
p.mPos = pos;
}
+
+ /** Native method substitution */
public static void nativeSetDataCapacity(long nativePtr, int size) {
if (size < 0) {
throw new IllegalArgumentException("size < 0: size=" + size);
@@ -173,20 +205,25 @@
}
}
+ /** Native method substitution */
public static boolean nativePushAllowFds(long nativePtr, boolean allowFds) {
var p = getInstance(nativePtr);
var prev = p.mAllowFds;
p.mAllowFds = allowFds;
return prev;
}
+
+ /** Native method substitution */
public static void nativeRestoreAllowFds(long nativePtr, boolean lastValue) {
getInstance(nativePtr).mAllowFds = lastValue;
}
+ /** Native method substitution */
public static void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len) {
nativeWriteBlob(nativePtr, b, offset, len);
}
+ /** Native method substitution */
public static void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len) {
var p = getInstance(nativePtr);
@@ -205,6 +242,7 @@
}
}
+ /** Native method substitution */
public static int nativeWriteInt(long nativePtr, int value) {
var p = getInstance(nativePtr);
p.ensureMoreCapacity(Integer.BYTES);
@@ -219,14 +257,19 @@
return OK;
}
+ /** Native method substitution */
public static int nativeWriteLong(long nativePtr, long value) {
nativeWriteInt(nativePtr, (int) (value >>> 32));
nativeWriteInt(nativePtr, (int) (value));
return OK;
}
+
+ /** Native method substitution */
public static int nativeWriteFloat(long nativePtr, float val) {
return nativeWriteInt(nativePtr, Float.floatToIntBits(val));
}
+
+ /** Native method substitution */
public static int nativeWriteDouble(long nativePtr, double val) {
return nativeWriteLong(nativePtr, Double.doubleToLongBits(val));
}
@@ -235,6 +278,7 @@
return ((val + 3) / 4) * 4;
}
+ /** Native method substitution */
public static void nativeWriteString8(long nativePtr, String val) {
if (val == null) {
nativeWriteBlob(nativePtr, null, 0, 0);
@@ -243,15 +287,19 @@
nativeWriteBlob(nativePtr, bytes, 0, bytes.length);
}
}
+
+ /** Native method substitution */
public static void nativeWriteString16(long nativePtr, String val) {
// Just reuse String8
nativeWriteString8(nativePtr, val);
}
+ /** Native method substitution */
public static byte[] nativeCreateByteArray(long nativePtr) {
return nativeReadBlob(nativePtr);
}
+ /** Native method substitution */
public static boolean nativeReadByteArray(long nativePtr, byte[] dest, int destLen) {
if (dest == null) {
return false;
@@ -271,6 +319,7 @@
return true;
}
+ /** Native method substitution */
public static byte[] nativeReadBlob(long nativePtr) {
var p = getInstance(nativePtr);
if (p.mSize - p.mPos < 4) {
@@ -295,6 +344,8 @@
return bytes;
}
+
+ /** Native method substitution */
public static int nativeReadInt(long nativePtr) {
var p = getInstance(nativePtr);
@@ -310,19 +361,24 @@
return ret;
}
+
+ /** Native method substitution */
public static long nativeReadLong(long nativePtr) {
return (((long) nativeReadInt(nativePtr)) << 32)
| (((long) nativeReadInt(nativePtr)) & 0xffff_ffffL);
}
+ /** Native method substitution */
public static float nativeReadFloat(long nativePtr) {
return Float.intBitsToFloat(nativeReadInt(nativePtr));
}
+ /** Native method substitution */
public static double nativeReadDouble(long nativePtr) {
return Double.longBitsToDouble(nativeReadLong(nativePtr));
}
+ /** Native method substitution */
public static String nativeReadString8(long nativePtr) {
final var bytes = nativeReadBlob(nativePtr);
if (bytes == null) {
@@ -334,10 +390,13 @@
return nativeReadString8(nativePtr);
}
+ /** Native method substitution */
public static byte[] nativeMarshall(long nativePtr) {
var p = getInstance(nativePtr);
return Arrays.copyOf(p.mBuffer, p.mSize);
}
+
+ /** Native method substitution */
public static void nativeUnmarshall(
long nativePtr, byte[] data, int offset, int length) {
var p = getInstance(nativePtr);
@@ -346,6 +405,8 @@
p.mPos += length;
p.updateSize();
}
+
+ /** Native method substitution */
public static int nativeCompareData(long thisNativePtr, long otherNativePtr) {
var a = getInstance(thisNativePtr);
var b = getInstance(otherNativePtr);
@@ -355,6 +416,8 @@
return -1;
}
}
+
+ /** Native method substitution */
public static boolean nativeCompareDataInRange(
long ptrA, int offsetA, long ptrB, int offsetB, int length) {
var a = getInstance(ptrA);
@@ -368,6 +431,8 @@
return Arrays.equals(Arrays.copyOfRange(a.mBuffer, offsetA, offsetA + length),
Arrays.copyOfRange(b.mBuffer, offsetB, offsetB + length));
}
+
+ /** Native method substitution */
public static void nativeAppendFrom(
long thisNativePtr, long otherNativePtr, int srcOffset, int length) {
var dst = getInstance(thisNativePtr);
@@ -382,25 +447,83 @@
// TODO: Update the other's position?
}
- public static boolean nativeHasFileDescriptors(long nativePtr) {
- // Assume false for now, because we don't support writing FDs yet.
- return false;
- }
-
- public static boolean nativeHasFileDescriptorsInRange(
- long nativePtr, int offset, int length) {
- // Assume false for now, because we don't support writing FDs yet.
- return false;
- }
-
+ /** Native method substitution */
public static boolean nativeHasBinders(long nativePtr) {
// Assume false for now, because we don't support adding binders.
return false;
}
+ /** Native method substitution */
public static boolean nativeHasBindersInRange(
long nativePtr, int offset, int length) {
// Assume false for now, because we don't support writing FDs yet.
return false;
}
-}
+
+ /** Native method substitution */
+ public static void nativeWriteFileDescriptor(long nativePtr, java.io.FileDescriptor val) {
+ var p = getInstance(nativePtr);
+
+ if (!p.mAllowFds) {
+ // Simulate the FDS_NOT_ALLOWED case in frameworks/base/core/jni/android_util_Binder.cpp
+ throw new RuntimeException("Not allowed to write file descriptors here");
+ }
+
+ FileDescriptor dup = null;
+ try {
+ dup = Os.dup(val);
+ } catch (ErrnoException e) {
+ throw new RuntimeException(e);
+ }
+ p.mFdMap.put(p.mPos, dup);
+
+ // Parcel.cpp writes two int32s for a FD.
+ // Make sure FD_PAYLOAD_SIZE is in sync with this code.
+ nativeWriteInt(nativePtr, FD_PLACEHOLDER);
+ nativeWriteInt(nativePtr, FD_PLACEHOLDER);
+ }
+
+ /** Native method substitution */
+ public static java.io.FileDescriptor nativeReadFileDescriptor(long nativePtr) {
+ var p = getInstance(nativePtr);
+
+ var pos = p.mPos;
+ var fd = p.mFdMap.get(pos);
+
+ if (fd == null) {
+ Log.w(TAG, "nativeReadFileDescriptor: Not a FD at pos #" + pos);
+ return null;
+ }
+ nativeReadInt(nativePtr);
+ return fd;
+ }
+
+ /** Native method substitution */
+ public static boolean nativeHasFileDescriptors(long nativePtr) {
+ var p = getInstance(nativePtr);
+ return p.mFdMap.size() > 0;
+ }
+
+ /** Native method substitution */
+ public static boolean nativeHasFileDescriptorsInRange(long nativePtr, int offset, int length) {
+ var p = getInstance(nativePtr);
+
+ // Original code: hasFileDescriptorsInRange() in frameworks/native/libs/binder/Parcel.cpp
+ if (offset < 0 || length < 0) {
+ throw new IllegalArgumentException("Negative value not allowed: offset=" + offset
+ + " length=" + length);
+ }
+ long limit = (long) offset + (long) length;
+ if (limit > p.mSize) {
+ throw new IllegalArgumentException("Out of range: offset=" + offset
+ + " length=" + length + " dataSize=" + p.mSize);
+ }
+
+ for (var pos : p.mFdMap.keySet()) {
+ if (offset <= pos && (pos + FD_PAYLOAD_SIZE - 1) < (offset + length)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
\ 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 8a1fe62..825ab72 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
@@ -53,4 +53,9 @@
public static StructStat stat(String path) throws ErrnoException {
return RavenwoodRuntimeNative.stat(path);
}
+
+ /** Ravenwood version of the OS API. */
+ public static void close(FileDescriptor fd) throws ErrnoException {
+ RavenwoodRuntimeNative.close(fd);
+ }
}
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 e9b305e..2bc8e71 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,6 +48,8 @@
public static native StructStat stat(String path) throws ErrnoException;
+ private static native void nClose(int fd) throws ErrnoException;
+
public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
return nLseek(JvmWorkaround.getInstance().getFdInt(fd), offset, whence);
}
@@ -83,4 +85,11 @@
return nFstat(fdInt);
}
+
+ /** See close(2) */
+ public static void close(FileDescriptor fd) throws ErrnoException {
+ var fdInt = JvmWorkaround.getInstance().getFdInt(fd);
+
+ nClose(fdInt);
+ }
}
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index e0a3e1c..ee84954 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -167,6 +167,11 @@
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));
+}
+
// ---- Registration ----
static const JNINativeMethod sMethods[] =
@@ -179,6 +184,7 @@
{ "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 },
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)