diff options
author | 2024-02-01 15:34:37 -0800 | |
---|---|---|
committer | 2024-02-12 14:22:10 -0800 | |
commit | be841eded00fc882678a395ee66e1242158dd969 (patch) | |
tree | 35b77389684054267096e6dd5aa2a6b0a6846463 /ravenwood | |
parent | 8e4eb7ed4d16c7ea847d5e21717389db8dd021de (diff) |
Adding test for loading JNI libraries in individual tests
Add a bivalent test that uses a native library.
Also added a utility method to load it in the same way on both
sides.
Bug: 318393625
Bug: 323931246
Test: run-ravenwood-tests.sh
Test: atest RavenwoodBivalentTest_device
Change-Id: I270058c15f718ff20640681742035e33e5e015a1
Diffstat (limited to 'ravenwood')
4 files changed, 253 insertions, 0 deletions
diff --git a/ravenwood/bivalenttest/Android.bp b/ravenwood/bivalenttest/Android.bp index acf85331601e..a6b6ed934ce7 100644 --- a/ravenwood/bivalenttest/Android.bp +++ b/ravenwood/bivalenttest/Android.bp @@ -7,6 +7,30 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +cc_library_shared { + name: "libravenwoodbivalenttest_jni", + host_supported: true, + + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + "-Wthread-safety", + ], + + srcs: [ + "jni/*.cpp", + ], + + shared_libs: [ + "libbase", + "liblog", + "libnativehelper", + "libutils", + "libcutils", + ], +} + android_ravenwood_test { name: "RavenwoodBivalentTest", @@ -18,6 +42,9 @@ android_ravenwood_test { srcs: [ "test/**/*.java", ], + jni_libs: [ + "libravenwoodbivalenttest_jni", + ], sdk_version: "test_current", auto_gen_config: true, } @@ -38,6 +65,9 @@ android_test { "ravenwood-junit", ], + jni_libs: [ + "libravenwoodbivalenttest_jni", + ], test_suites: [ "device-tests", ], diff --git a/ravenwood/bivalenttest/jni/ravenwood_core_test_jni.cpp b/ravenwood/bivalenttest/jni/ravenwood_core_test_jni.cpp new file mode 100644 index 000000000000..5e66b29b370a --- /dev/null +++ b/ravenwood/bivalenttest/jni/ravenwood_core_test_jni.cpp @@ -0,0 +1,52 @@ +/* + * 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" + +static jint add(JNIEnv* env, jclass clazz, jint a, jint b) { + return a + b; +} + +static const JNINativeMethod sMethods[] = +{ + { "add", "(II)I", (void*)add }, +}; + +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__); + + int res = jniRegisterNativeMethods(env, + "com/android/platform/test/ravenwood/bivalenttest/RavenwoodJniTest", + sMethods, NELEM(sMethods)); + if (res < 0) { + return res; + } + + return JNI_VERSION_1_4; +} diff --git a/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodJniTest.java b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodJniTest.java new file mode 100644 index 000000000000..3b106da74ed4 --- /dev/null +++ b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodJniTest.java @@ -0,0 +1,44 @@ +/* + * 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.platform.test.ravenwood.bivalenttest; + +import static junit.framework.Assert.assertEquals; + +import android.platform.test.ravenwood.RavenwoodRule; +import android.platform.test.ravenwood.RavenwoodUtils; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public final class RavenwoodJniTest { + static { + RavenwoodUtils.loadJniLibrary("ravenwoodbivalenttest_jni"); + } + + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().build(); + + private static native int add(int a, int b); + + @Test + public void testNativeMethod() { + assertEquals(5, add(2, 3)); + } +} diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java new file mode 100644 index 000000000000..b736a7662bd4 --- /dev/null +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2023 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.platform.test.ravenwood; + +import java.io.File; +import java.io.PrintStream; +import java.util.Arrays; + +/** + * Utilities for writing (bivalent) ravenwood tests. + */ +public class RavenwoodUtils { + private RavenwoodUtils() { + } + + /** + * Load a JNI library respecting {@code java.library.path} + * (which reflects {@code LD_LIBRARY_PATH}). + * + * <p>{@code libname} must be the library filename without: + * - directory + * - "lib" prefix + * - and the ".so" extension + * + * <p>For example, in order to load "libmyjni.so", then pass "myjni". + * + * <p>This is basically the same thing as Java's {@link System#loadLibrary(String)}, + * but this API works slightly different on ART and on the desktop Java, namely + * the desktop Java version uses a different entry point method name + * {@code JNI_OnLoad_libname()} (note the included "libname") + * while ART always seems to use {@code JNI_OnLoad()}. + * + * <p>This method provides the same behavior on both the device side and on Ravenwood -- + * it uses {@code JNI_OnLoad()} as the entry point name on both. + */ + public static void loadJniLibrary(String libname) { + if (RavenwoodRule.isOnRavenwood()) { + loadLibraryOnRavenwood(libname); + } else { + // Just delegate to the loadLibrary(). + System.loadLibrary(libname); + } + } + + private static void loadLibraryOnRavenwood(String libname) { + var path = System.getProperty("java.library.path"); + var filename = "lib" + libname + ".so"; + + System.out.println("Looking for library " + libname + ".so in java.library.path:" + path); + + try { + if (path == null) { + throw new UnsatisfiedLinkError("Cannot load library " + libname + "." + + " Property java.library.path not set!"); + } + for (var dir : path.split(":")) { + var file = new File(dir + "/" + filename); + if (file.exists()) { + System.load(file.getAbsolutePath()); + return; + } + } + throw new UnsatisfiedLinkError("Library " + libname + " no found in " + + "java.library.path: " + path); + } catch (Exception e) { + dumpFiles(System.out); + throw e; + } + } + + private static void dumpFiles(PrintStream out) { + try { + var path = System.getProperty("java.library.path"); + out.println("# java.library.path=" + path); + + for (var dir : path.split(":")) { + listFiles(out, new File(dir), ""); + + var gparent = new File((new File(dir)).getAbsolutePath() + "../../..") + .getCanonicalFile(); + if (gparent.getName().contains("testcases")) { + // Special case: if we found this directory, dump its contents too. + listFiles(out, gparent, ""); + } + } + } catch (Throwable th) { + out.println("Error: " + th.toString()); + th.printStackTrace(out); + } + } + + private static void listFiles(PrintStream out, File dir, String prefix) { + if (!dir.isDirectory()) { + out.println(prefix + dir.getAbsolutePath() + " is not a directory!"); + return; + } + out.println(prefix + ":" + dir.getAbsolutePath() + "/"); + // First, list the files. + for (var file : Arrays.stream(dir.listFiles()).sorted().toList()) { + out.println(prefix + " " + file.getName() + "" + (file.isDirectory() ? "/" : "")); + } + + // Then recurse. + if (dir.getAbsolutePath().startsWith("/usr") || dir.getAbsolutePath().startsWith("/lib")) { + // There would be too many files, so don't recurse. + return; + } + for (var file : Arrays.stream(dir.listFiles()).sorted().toList()) { + if (file.isDirectory()) { + listFiles(out, file, prefix + " "); + } + } + } +} |