[Ravenwood] Support NativeAllocationRegistry

We don't track native allocation size, but we still need to
release native allocations.

Bug: 337110712
Test: atest RavenwoodBivalentTest
Test: atest RavenwoodBivalentTest_device
Test: ./ravenwood/scripts/run-ravenwood-tests.sh
Change-Id: Ia50c963731a26fc951a8040cbf353a5c56505b6a
diff --git a/ravenwood/runtime-helper-src/jni/ravenwood_runtime.cpp b/ravenwood/runtime-helper-src/jni/ravenwood_runtime.cpp
new file mode 100644
index 0000000..8e3a21d
--- /dev/null
+++ b/ravenwood/runtime-helper-src/jni/ravenwood_runtime.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+
+
+typedef void (*FreeFunction)(void*);
+
+static void NativeAllocationRegistry_applyFreeFunction(JNIEnv*,
+                                                       jclass,
+                                                       jlong freeFunction,
+                                                       jlong ptr) {
+    void* nativePtr = reinterpret_cast<void*>(static_cast<uintptr_t>(ptr));
+    FreeFunction nativeFreeFunction
+        = reinterpret_cast<FreeFunction>(static_cast<uintptr_t>(freeFunction));
+    nativeFreeFunction(nativePtr);
+}
+
+static const JNINativeMethod sMethods_NAR[] =
+{
+    { "applyFreeFunction", "(JJ)V", (void*)NativeAllocationRegistry_applyFreeFunction },
+};
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
+{
+    JNIEnv* env = NULL;
+    jint result = -1;
+
+    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        ALOGE("GetEnv failed!");
+        return result;
+    }
+    ALOG_ASSERT(env, "Could not retrieve the env!");
+
+    ALOGI("%s: JNI_OnLoad", __FILE__);
+
+    // Initialize the Ravenwood version of NativeAllocationRegistry.
+    // We don't use this JNI on the device side, but if we ever have to do, skip this part.
+#ifndef __ANDROID__
+    int res = jniRegisterNativeMethods(env, "libcore/util/NativeAllocationRegistry",
+            sMethods_NAR, NELEM(sMethods_NAR));
+    if (res < 0) {
+        return res;
+    }
+#endif
+
+    return JNI_VERSION_1_4;
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/ravenwood/LibcoreRavenwoodUtils.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/ravenwood/LibcoreRavenwoodUtils.java
new file mode 100644
index 0000000..839b62a
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/ravenwood/LibcoreRavenwoodUtils.java
@@ -0,0 +1,35 @@
+/*
+ * 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 libcore.ravenwood;
+
+public class LibcoreRavenwoodUtils {
+    private LibcoreRavenwoodUtils() {
+    }
+
+    public static void loadRavenwoodNativeRuntime() {
+        // TODO Stop using reflections.
+        // We need to call RavenwoodUtils.loadRavenwoodNativeRuntime(), but due to the build
+        // structure complexity, we can't refer to to this method directly from here,
+        // so let's use reflections for now...
+        try {
+            final var clazz = Class.forName("android.platform.test.ravenwood.RavenwoodUtils");
+            final var method = clazz.getMethod("loadRavenwoodNativeRuntime");
+            method.invoke(null);
+        } catch (Throwable th) {
+            throw new IllegalStateException("Failed to load Ravenwood native runtime", th);
+        }
+    }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
new file mode 100644
index 0000000..93861e8
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
@@ -0,0 +1,88 @@
+/*
+ * 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 libcore.util;
+
+import libcore.ravenwood.LibcoreRavenwoodUtils;
+
+import java.lang.ref.Cleaner;
+import java.lang.ref.Reference;
+
+/**
+ * Re-implementation of ART's NativeAllocationRegistry for Ravenwood.
+ * - We don't track the native allocation size on Ravenwood.
+ * - sun.misc.Cleaner isn't available on the desktop JVM, so we use java.lang.ref.Cleaner.
+ *   (Should ART switch to java.lang.ref.Cleaner?)
+ */
+public class NativeAllocationRegistry {
+    static {
+        // Initialize the JNI method.
+        LibcoreRavenwoodUtils.loadRavenwoodNativeRuntime();
+    }
+
+    private final long mFreeFunction;
+    private static final Cleaner sCleaner = Cleaner.create();
+
+    public static NativeAllocationRegistry createNonmalloced(
+            ClassLoader classLoader, long freeFunction, long size) {
+        return new NativeAllocationRegistry(classLoader, freeFunction, size, false);
+    }
+
+    public static NativeAllocationRegistry createMalloced(
+            ClassLoader classLoader, long freeFunction, long size) {
+        return new NativeAllocationRegistry(classLoader, freeFunction, size, true);
+    }
+
+    public static NativeAllocationRegistry createMalloced(
+            ClassLoader classLoader, long freeFunction) {
+        return new NativeAllocationRegistry(classLoader, freeFunction, 0, true);
+    }
+
+    public NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size) {
+        this(classLoader, freeFunction, size, size == 0);
+    }
+
+    private NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size,
+            boolean mallocAllocation) {
+        if (size < 0) {
+            throw new IllegalArgumentException("Invalid native allocation size: " + size);
+        }
+        mFreeFunction = freeFunction;
+    }
+
+    public Runnable registerNativeAllocation(Object referent, long nativePtr) {
+        if (referent == null) {
+            throw new IllegalArgumentException("referent is null");
+        }
+        if (nativePtr == 0) {
+            throw new IllegalArgumentException("nativePtr is null");
+        }
+
+        final Runnable releaser = () -> {
+            applyFreeFunction(mFreeFunction, nativePtr);
+        };
+        sCleaner.register(referent, releaser);
+
+        // Ensure that cleaner doesn't get invoked before we enable it.
+        Reference.reachabilityFence(referent);
+        return releaser;
+    }
+
+    /**
+     * Calls {@code freeFunction}({@code nativePtr}).
+     */
+    public static native void applyFreeFunction(long freeFunction, long nativePtr);
+}
+