From 7a5a0f9aaf28543b52bc31556415fa9a0491ceac Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Mon, 9 Jun 2025 02:33:22 -0400 Subject: [PATCH 1/6] [JavaRuntime] Add Android shims --- Sources/JavaRuntime/AndroidSupport.cpp | 98 ++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 Sources/JavaRuntime/AndroidSupport.cpp diff --git a/Sources/JavaRuntime/AndroidSupport.cpp b/Sources/JavaRuntime/AndroidSupport.cpp new file mode 100644 index 00000000..3556b371 --- /dev/null +++ b/Sources/JavaRuntime/AndroidSupport.cpp @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +#ifdef __ANDROID__ + +#include +#include +#include + +// these are not exported by the Android SDK + +extern "C" { + using JavaRuntime_GetDefaultJavaVMInitArgs_fn = jint (*)(void *vm_args); + using JavaRuntime_CreateJavaVM_fn = jint (*)(JavaVM **, JNIEnv **, void *); + using JavaRuntime_GetCreatedJavaVMs_fn = jint (*)(JavaVM **, jsize, jsize *); +} + +static JavaRuntime_GetDefaultJavaVMInitArgs_fn + JavaRuntime_GetDefaultJavaVMInitArgs; +static JavaRuntime_CreateJavaVM_fn JavaRuntime_CreateJavaVM; +static JavaRuntime_GetCreatedJavaVMs_fn JavaRuntime_GetCreatedJavaVMs; + +static void *JavaRuntime_dlhandle; + +__attribute__((constructor)) static void JavaRuntime_init(void) { + JavaRuntime_dlhandle = dlopen("libnativehelper.so", RTLD_NOW | RTLD_LOCAL); + if (JavaRuntime_dlhandle == nullptr) { + __android_log_print(ANDROID_LOG_FATAL, "JavaRuntime", + "failed to open libnativehelper.so: %s", dlerror()); + return; + } + + JavaRuntime_GetDefaultJavaVMInitArgs = + reinterpret_cast( + dlsym(JavaRuntime_dlhandle, "JNI_GetDefaultJavaVMInitArgs")); + if (JavaRuntime_GetDefaultJavaVMInitArgs == nullptr) + __android_log_print(ANDROID_LOG_FATAL, "JavaRuntime", + "JNI_GetDefaultJavaVMInitArgs not found: %s", + dlerror()); + + JavaRuntime_CreateJavaVM = reinterpret_cast( + dlsym(JavaRuntime_dlhandle, "JNI_CreateJavaVM")); + if (JavaRuntime_CreateJavaVM == nullptr) + __android_log_print(ANDROID_LOG_FATAL, "JavaRuntime", + "JNI_CreateJavaVM not found: %s", dlerror()); + + JavaRuntime_GetCreatedJavaVMs = + reinterpret_cast( + dlsym(JavaRuntime_dlhandle, "JNI_GetCreatedJavaVMs")); + if (JavaRuntime_GetCreatedJavaVMs == nullptr) + __android_log_print(ANDROID_LOG_FATAL, "JavaRuntime", + "JNI_GetCreatedJavaVMs not found: %s", dlerror()); +} + +__attribute__((destructor)) static void JavaRuntime_deinit(void) { + if (JavaRuntime_dlhandle) { + dlclose(JavaRuntime_dlhandle); + JavaRuntime_dlhandle = nullptr; + } + + JavaRuntime_GetDefaultJavaVMInitArgs = nullptr; + JavaRuntime_CreateJavaVM = nullptr; + JavaRuntime_GetCreatedJavaVMs = nullptr; +} + +jint JNI_GetDefaultJavaVMInitArgs(void *vm_args) { + if (JavaRuntime_GetDefaultJavaVMInitArgs == nullptr) + return JNI_ERR; + + return (*JavaRuntime_GetDefaultJavaVMInitArgs)(vm_args); +} + +jint JNI_CreateJavaVM(JavaVM **vm, JNIEnv **env, void *vm_args) { + if (JavaRuntime_CreateJavaVM == nullptr) + return JNI_ERR; + + return (*JavaRuntime_CreateJavaVM)(vm, env, vm_args); +} + +jint JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen, jsize *nVMs) { + if (JavaRuntime_GetCreatedJavaVMs == nullptr) + return JNI_ERR; + + return (*JavaRuntime_GetCreatedJavaVMs)(vmBuf, bufLen, nVMs); +} + +#endif From 017a0af30bfbd1db5d223fe8804234482976edbe Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Fri, 8 Nov 2024 10:53:04 +1100 Subject: [PATCH 2/6] JavaKit: always use ambient javaEnvironment on Android This is not a real fix, I suspect the real fix is to remove javaEnvironment as an instance variable on all platforms and always use the ambient environment (as the JNI specification clearly states the environment cannot be shared between threads). Works around: #157 --- Sources/JavaKit/JavaObjectHolder.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Sources/JavaKit/JavaObjectHolder.swift b/Sources/JavaKit/JavaObjectHolder.swift index 173991c9..75adbfed 100644 --- a/Sources/JavaKit/JavaObjectHolder.swift +++ b/Sources/JavaKit/JavaObjectHolder.swift @@ -19,13 +19,21 @@ import JavaRuntime /// while this instance is live. public class JavaObjectHolder { public private(set) var object: jobject? +#if canImport(Android) + public var environment: JNIEnvironment { + try! JavaVirtualMachine.shared().environment() + } +#else public let environment: JNIEnvironment +#endif /// Take a reference to a Java object and promote it to a global reference /// so that the Java virtual machine will not garbage-collect it. public init(object: jobject, environment: JNIEnvironment) { self.object = environment.interface.NewGlobalRef(environment, object) +#if !canImport(Android) self.environment = environment +#endif } /// Forget this Java object, meaning that it is no longer used from anywhere From bdfe19af7e9a9a6bc7cd240c4c17c558ea48730c Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Sat, 14 Jun 2025 04:48:17 -0400 Subject: [PATCH 3/6] [JavaKit] Add `JNIEnvPointer` for Android --- .../JavaKitVM/JavaVirtualMachine.swift | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/Sources/JavaKit/JavaKitVM/JavaVirtualMachine.swift b/Sources/JavaKit/JavaKitVM/JavaVirtualMachine.swift index 0800a89e..edbddbb7 100644 --- a/Sources/JavaKit/JavaKitVM/JavaVirtualMachine.swift +++ b/Sources/JavaKit/JavaKitVM/JavaVirtualMachine.swift @@ -19,6 +19,11 @@ import Foundation #endif public typealias JavaVMPointer = UnsafeMutablePointer +#if canImport(Android) +typealias JNIEnvPointer = UnsafeMutablePointer +#else +typealias JNIEnvPointer = UnsafeMutableRawPointer +#endif public final class JavaVirtualMachine: @unchecked Sendable { /// The JNI version that we depend on. @@ -61,7 +66,7 @@ public final class JavaVirtualMachine: @unchecked Sendable { ) throws { self.classpath = classpath var jvm: JavaVMPointer? = nil - var environment: UnsafeMutableRawPointer? = nil + var environment: JNIEnvPointer? = nil var vmArgs = JavaVMInitArgs() vmArgs.version = JavaVirtualMachine.jniVersion vmArgs.ignoreUnrecognized = jboolean(ignoreUnrecognized ? JNI_TRUE : JNI_FALSE) @@ -161,12 +166,18 @@ extension JavaVirtualMachine { return environment.assumingMemoryBound(to: JNIEnv?.self) } +#if canImport(Android) + var jniEnv = environment?.assumingMemoryBound(to: JNIEnv?.self) +#else + var jniEnv = environment +#endif + // Attach the current thread to the JVM. let attachResult: jint if asDaemon { - attachResult = jvm.pointee!.pointee.AttachCurrentThreadAsDaemon(jvm, &environment, nil) + attachResult = jvm.pointee!.pointee.AttachCurrentThreadAsDaemon(jvm, &jniEnv, nil) } else { - attachResult = jvm.pointee!.pointee.AttachCurrentThread(jvm, &environment, nil) + attachResult = jvm.pointee!.pointee.AttachCurrentThread(jvm, &jniEnv, nil) } // If we failed to attach, report that. @@ -175,9 +186,13 @@ extension JavaVirtualMachine { throw attachError } - JavaVirtualMachine.destroyTLS.set(environment!) + JavaVirtualMachine.destroyTLS.set(jniEnv!) - return environment!.assumingMemoryBound(to: JNIEnv?.self) +#if canImport(Android) + return jniEnv! +#else + return jniEnv!.assumingMemoryBound(to: JNIEnv?.self) +#endif } /// Detach the current thread from the Java Virtual Machine. All Java From 670030520c34dcd6343f8b2cfb0e1f5ecf823f03 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Sat, 14 Jun 2025 04:48:42 -0400 Subject: [PATCH 4/6] [JavaKit] Fix `JNINativeInterface_` for Android --- Sources/JavaKit/JavaEnvironment.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/JavaKit/JavaEnvironment.swift b/Sources/JavaKit/JavaEnvironment.swift index d74146ab..7dcc242f 100644 --- a/Sources/JavaKit/JavaEnvironment.swift +++ b/Sources/JavaKit/JavaEnvironment.swift @@ -14,6 +14,10 @@ import JavaRuntime +#if canImport(Android) +typealias JNINativeInterface_ = JNINativeInterface +#endif + extension UnsafeMutablePointer { var interface: JNINativeInterface_ { self.pointee!.pointee } } From 2d41aff20494e4c431d48fbe20e596753d124891 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Mon, 7 Jul 2025 11:30:18 -0400 Subject: [PATCH 5/6] [JavaKit] Don't include JVM path for Android --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 403617da..81d53b1d 100644 --- a/Package.swift +++ b/Package.swift @@ -226,7 +226,7 @@ let package = Package( exclude: ["swift-java.config"], swiftSettings: [ .swiftLanguageMode(.v5), - .unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"]) + .unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"], .when(platforms: [.macOS, .linux, .windows])) ], linkerSettings: [ .unsafeFlags( From fdba230556000deff5950fcc6a37aa9347ab15b6 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Wed, 9 Jul 2025 02:06:53 -0400 Subject: [PATCH 6/6] Bump minimum Swift version to 6.1 --- Package.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 81d53b1d..b5a81a0b 100644 --- a/Package.swift +++ b/Package.swift @@ -1,6 +1,5 @@ -// swift-tools-version: 6.0 +// swift-tools-version: 6.1 // The swift-tools-version declares the minimum version of Swift required to build this package. - import CompilerPluginSupport import PackageDescription