From bdfb6052749489cb4d926973551809d1be92f501 Mon Sep 17 00:00:00 2001 From: Konrad Kollnig <5175206+kasnder@users.noreply.github.com> Date: Sun, 12 Jul 2026 09:30:43 +0200 Subject: [PATCH] Upgrade jni to 0.22 --- wgbridge-rs/Cargo.lock | 153 +++++-------- wgbridge-rs/Cargo.toml | 2 +- wgbridge-rs/src/jni_bindings.rs | 384 ++++++++++++++++++-------------- 3 files changed, 280 insertions(+), 259 deletions(-) diff --git a/wgbridge-rs/Cargo.lock b/wgbridge-rs/Cargo.lock index de7af9501..925f7bda8 100644 --- a/wgbridge-rs/Cargo.lock +++ b/wgbridge-rs/Cargo.lock @@ -101,12 +101,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cfg-if" version = "1.0.4" @@ -423,7 +417,7 @@ dependencies = [ "rand_core 0.6.4", "ring", "socket2", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-util", "typed-builder", @@ -492,27 +486,32 @@ checksum = "cf370abdafd54d13e54a620e8c3e1145f28e46cc9d704bc6d94414559df41763" [[package]] name = "jni" -version = "0.21.1" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" dependencies = [ - "cesu8", "cfg-if", "combine", - "jni-sys 0.3.1", + "jni-macros", + "jni-sys", "log", - "thiserror", + "simd_cesu8", + "thiserror 2.0.18", "walkdir", - "windows-sys 0.45.0", + "windows-link", ] [[package]] -name = "jni-sys" -version = "0.3.1" +name = "jni-macros" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" dependencies = [ - "jni-sys 0.4.1", + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn", ] [[package]] @@ -835,6 +834,22 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "slab" version = "0.4.12" @@ -880,7 +895,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", ] [[package]] @@ -894,6 +918,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio" version = "1.52.3" @@ -1043,22 +1078,13 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1070,67 +1096,34 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -1143,48 +1136,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/wgbridge-rs/Cargo.toml b/wgbridge-rs/Cargo.toml index 81cc21799..0ecad25a6 100644 --- a/wgbridge-rs/Cargo.toml +++ b/wgbridge-rs/Cargo.toml @@ -19,7 +19,7 @@ libc = "0.2" log = "0.4" [target.'cfg(target_os = "android")'.dependencies] -jni = "0.21" +jni = "0.22" android_logger = "0.14" [profile.release] diff --git a/wgbridge-rs/src/jni_bindings.rs b/wgbridge-rs/src/jni_bindings.rs index 37c95cd91..c3db2cbc6 100644 --- a/wgbridge-rs/src/jni_bindings.rs +++ b/wgbridge-rs/src/jni_bindings.rs @@ -5,9 +5,10 @@ use std::sync::Arc; use std::sync::Once; -use jni::objects::{GlobalRef, JClass, JObject, JString, JValue}; +use jni::objects::{Global, JClass, JObject, JString, JValue}; +use jni::strings::JNIString; use jni::sys::{jboolean, jint, jlong, jlongArray, jstring}; -use jni::{JNIEnv, JavaVM}; +use jni::{jni_sig, jni_str, Env, EnvUnowned, JavaVM}; use crate::callbacks::{BridgeLogger, DnsSink, NullLogger, SocketProtector}; use crate::keys; @@ -15,6 +16,18 @@ use crate::tunnel::{start_tunnel, Tunnel}; static LOGGER_INIT: Once = Once::new(); +macro_rules! with_native_env { + ($unowned_env:ident, $env:ident, $body:block) => {{ + $unowned_env + .with_env(|$env| -> jni::errors::Result<_> { + #[allow(clippy::redundant_closure_call)] + let result = (|| $body)(); + Ok(result) + }) + .resolve::() + }}; +} + fn init_android_logger() { LOGGER_INIT.call_once(|| { android_logger::init_once( @@ -25,24 +38,24 @@ fn init_android_logger() { }); } -fn throw(env: &mut JNIEnv, msg: &str) { +fn throw(env: &mut Env, msg: &str) { // Don't clobber an exception already in flight. - if !env.exception_check().unwrap_or(false) { - let _ = env.throw_new("java/lang/RuntimeException", msg); + if !env.exception_check() { + let _ = env.throw_new(jni_str!("java/lang/RuntimeException"), JNIString::new(msg)); } } -fn get_string(env: &mut JNIEnv, s: &JString) -> Option { - env.get_string(s).ok().map(|s| s.into()) +fn get_string(env: &Env, s: &JString) -> Option { + s.try_to_string(env).ok() } struct JavaCallback { vm: JavaVM, - obj: GlobalRef, + obj: Global>, } impl JavaCallback { - fn new(env: &JNIEnv, obj: JObject) -> Option { + fn new(env: &Env, obj: JObject) -> Option { if obj.is_null() { return None; } @@ -51,14 +64,22 @@ impl JavaCallback { Some(Self { vm, obj }) } - /// Runs `f` with a JNIEnv attached to the current thread. Worker threads + /// Runs `f` with an Env attached to the current thread. Worker threads /// stay attached for their lifetime (they are long-lived tokio threads). - fn with_env(&self, f: impl FnOnce(&mut JNIEnv, &GlobalRef) -> jni::errors::Result) -> Option { - let mut env = self.vm.attach_current_thread_permanently().ok()?; - match f(&mut env, &self.obj) { + fn with_env( + &self, + f: impl FnOnce(&mut Env, &JObject) -> jni::errors::Result, + ) -> Option { + match self + .vm + .attach_current_thread(|env| f(env, self.obj.as_ref())) + { Ok(r) => Some(r), Err(_) => { - let _ = env.exception_clear(); + let _ = self.vm.attach_current_thread(|env| { + env.exception_clear(); + Ok::<_, jni::errors::Error>(()) + }); None } } @@ -71,8 +92,13 @@ impl SocketProtector for JavaProtector { fn protect(&self, fd: i32) -> bool { self.0 .with_env(|env, obj| { - env.call_method(obj, "protect", "(I)Z", &[JValue::Int(fd)])? - .z() + env.call_method( + obj, + jni_str!("protect"), + jni_sig!((fd: jint) -> jboolean), + &[JValue::Int(fd)], + )? + .z() }) .unwrap_or(false) } @@ -86,8 +112,8 @@ impl BridgeLogger for JavaLogger { let s = env.new_string(msg)?; env.call_method( obj, - "verbosef", - "(Ljava/lang/String;)V", + jni_str!("verbosef"), + jni_sig!((message: JString) -> void), &[JValue::Object(&s)], ) .map(|_| ()) @@ -97,8 +123,13 @@ impl BridgeLogger for JavaLogger { fn error(&self, msg: &str) { self.0.with_env(|env, obj| { let s = env.new_string(msg)?; - env.call_method(obj, "errorf", "(Ljava/lang/String;)V", &[JValue::Object(&s)]) - .map(|_| ()) + env.call_method( + obj, + jni_str!("errorf"), + jni_sig!((message: JString) -> void), + &[JValue::Object(&s)], + ) + .map(|_| ()) }); } } @@ -113,8 +144,8 @@ impl DnsSink for JavaDnsSink { let r = env.new_string(resource)?; env.call_method( obj, - "recordDns", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V", + jni_str!("recordDns"), + jni_sig!((qname: JString, aname: JString, resource: JString, ttl: jint) -> void), &[ JValue::Object(&q), JValue::Object(&a), @@ -139,46 +170,50 @@ fn tunnel_from_handle<'a>(handle: jlong) -> Option<&'a Tunnel> { #[no_mangle] pub extern "system" fn Java_net_kollnig_missioncontrol_wgbridge_Wgbridge_generatePrivateKey( - mut env: JNIEnv, + mut unowned_env: EnvUnowned, _class: JClass, ) -> jstring { - match keys::generate_private_key() { - Ok(key) => env - .new_string(key) - .map(|s| s.into_raw()) - .unwrap_or(std::ptr::null_mut()), - Err(e) => { - throw(&mut env, &e); - std::ptr::null_mut() + with_native_env!(unowned_env, env, { + match keys::generate_private_key() { + Ok(key) => env + .new_string(key) + .map(|s| s.into_raw()) + .unwrap_or(std::ptr::null_mut()), + Err(e) => { + throw(env, &e); + std::ptr::null_mut() + } } - } + }) } #[no_mangle] pub extern "system" fn Java_net_kollnig_missioncontrol_wgbridge_Wgbridge_publicKey( - mut env: JNIEnv, + mut unowned_env: EnvUnowned, _class: JClass, private_key: JString, ) -> jstring { - let Some(private_key) = get_string(&mut env, &private_key) else { - throw(&mut env, "privateKey must not be null"); - return std::ptr::null_mut(); - }; - match keys::public_key(&private_key) { - Ok(key) => env - .new_string(key) - .map(|s| s.into_raw()) - .unwrap_or(std::ptr::null_mut()), - Err(e) => { - throw(&mut env, &e); - std::ptr::null_mut() + with_native_env!(unowned_env, env, { + let Some(private_key) = get_string(env, &private_key) else { + throw(env, "privateKey must not be null"); + return std::ptr::null_mut(); + }; + match keys::public_key(&private_key) { + Ok(key) => env + .new_string(key) + .map(|s| s.into_raw()) + .unwrap_or(std::ptr::null_mut()), + Err(e) => { + throw(env, &e); + std::ptr::null_mut() + } } - } + }) } #[no_mangle] pub extern "system" fn Java_net_kollnig_missioncontrol_wgbridge_Wgbridge_nativeStartTunnel( - mut env: JNIEnv, + mut unowned_env: EnvUnowned, _class: JClass, uapi_config: JString, outbound_rx_fd: jint, @@ -188,180 +223,197 @@ pub extern "system" fn Java_net_kollnig_missioncontrol_wgbridge_Wgbridge_nativeS logger: JObject, dns_recorder: JObject, ) -> jlong { - init_android_logger(); - - let Some(uapi) = get_string(&mut env, &uapi_config) else { - throw(&mut env, "uapiConfig must not be null"); - return 0; - }; - let Some(protector) = JavaCallback::new(&env, protector) else { - throw(&mut env, "protector must not be null"); - return 0; - }; - let logger: Arc = match JavaCallback::new(&env, logger) { - Some(cb) => Arc::new(JavaLogger(cb)), - None => Arc::new(NullLogger), - }; - let dns: Option> = - JavaCallback::new(&env, dns_recorder).map(|cb| Arc::new(JavaDnsSink(cb)) as _); - - // Attach each tokio worker to the JVM as it spins up, so the Java - // GlobalRefs the callbacks hold are dropped on attached threads at - // teardown (avoids the jni "detached thread" GlobalRef warning). - let Ok(vm) = env.get_java_vm() else { - throw(&mut env, "could not obtain JavaVM"); - return 0; - }; - let on_worker_start: Arc = Arc::new(move || { - let _ = vm.attach_current_thread_permanently(); - }); + with_native_env!(unowned_env, env, { + init_android_logger(); - match start_tunnel( - &uapi, - outbound_rx_fd, - tun_write_fd, - mtu.clamp(576, 65535) as u16, - Arc::new(JavaProtector(protector)), - logger, - dns, - on_worker_start, - ) { - Ok(tunnel) => Box::into_raw(Box::new(tunnel)) as jlong, - Err(e) => { - throw(&mut env, &e); - 0 + let Some(uapi) = get_string(env, &uapi_config) else { + throw(env, "uapiConfig must not be null"); + return 0; + }; + let Some(protector) = JavaCallback::new(env, protector) else { + throw(env, "protector must not be null"); + return 0; + }; + let logger: Arc = match JavaCallback::new(env, logger) { + Some(cb) => Arc::new(JavaLogger(cb)), + None => Arc::new(NullLogger), + }; + let dns: Option> = + JavaCallback::new(env, dns_recorder).map(|cb| Arc::new(JavaDnsSink(cb)) as _); + + // Attach each tokio worker to the JVM as it spins up, so the Java + // GlobalRefs the callbacks hold are dropped on attached threads at + // teardown (avoids the jni "detached thread" GlobalRef warning). + let Ok(vm) = env.get_java_vm() else { + throw(env, "could not obtain JavaVM"); + return 0; + }; + let on_worker_start: Arc = Arc::new(move || { + let _ = vm.attach_current_thread(|_| Ok::<_, jni::errors::Error>(())); + }); + + match start_tunnel( + &uapi, + outbound_rx_fd, + tun_write_fd, + mtu.clamp(576, 65535) as u16, + Arc::new(JavaProtector(protector)), + logger, + dns, + on_worker_start, + ) { + Ok(tunnel) => Box::into_raw(Box::new(tunnel)) as jlong, + Err(e) => { + throw(env, &e); + 0 + } } - } + }) } #[no_mangle] pub extern "system" fn Java_net_kollnig_missioncontrol_wgbridge_Tunnel_nativeSetConfig( - mut env: JNIEnv, + mut unowned_env: EnvUnowned, _class: JClass, handle: jlong, uapi_config: JString, ) { - let Some(tunnel) = tunnel_from_handle(handle) else { - throw(&mut env, "tunnel stopped"); - return; - }; - let Some(uapi) = get_string(&mut env, &uapi_config) else { - throw(&mut env, "uapiConfig must not be null"); - return; - }; - if let Err(e) = tunnel.set_config(&uapi) { - throw(&mut env, &e); - } + with_native_env!(unowned_env, env, { + let Some(tunnel) = tunnel_from_handle(handle) else { + throw(env, "tunnel stopped"); + return; + }; + let Some(uapi) = get_string(env, &uapi_config) else { + throw(env, "uapiConfig must not be null"); + return; + }; + if let Err(e) = tunnel.set_config(&uapi) { + throw(env, &e); + } + }) } #[no_mangle] pub extern "system" fn Java_net_kollnig_missioncontrol_wgbridge_Tunnel_nativeStats( - mut env: JNIEnv, + mut unowned_env: EnvUnowned, _class: JClass, handle: jlong, ) -> jlongArray { - let Some(tunnel) = tunnel_from_handle(handle) else { - throw(&mut env, "tunnel stopped"); - return std::ptr::null_mut(); - }; - match tunnel.stats() { - Ok(stats) => { - let values = [stats.rx_bytes, stats.tx_bytes, stats.latest_handshake_millis]; - match env.new_long_array(3) { - Ok(array) => { - let _ = env.set_long_array_region(&array, 0, &values); - array.into_raw() - } - Err(e) => { - throw(&mut env, &e.to_string()); - std::ptr::null_mut() + with_native_env!(unowned_env, env, { + let Some(tunnel) = tunnel_from_handle(handle) else { + throw(env, "tunnel stopped"); + return std::ptr::null_mut(); + }; + match tunnel.stats() { + Ok(stats) => { + let values = [ + stats.rx_bytes, + stats.tx_bytes, + stats.latest_handshake_millis, + ]; + match env.new_long_array(3) { + Ok(array) => { + let _ = array.set_region(env, 0, &values); + array.into_raw() + } + Err(e) => { + throw(env, &e.to_string()); + std::ptr::null_mut() + } } } + Err(e) => { + throw(env, &e); + std::ptr::null_mut() + } } - Err(e) => { - throw(&mut env, &e); - std::ptr::null_mut() - } - } + }) } #[no_mangle] pub extern "system" fn Java_net_kollnig_missioncontrol_wgbridge_Tunnel_nativeSendKeepalive( - mut env: JNIEnv, + mut unowned_env: EnvUnowned, _class: JClass, handle: jlong, ) { - let Some(tunnel) = tunnel_from_handle(handle) else { - throw(&mut env, "tunnel stopped"); - return; - }; - tunnel.send_keepalive(); + with_native_env!(unowned_env, env, { + let Some(tunnel) = tunnel_from_handle(handle) else { + throw(env, "tunnel stopped"); + return; + }; + tunnel.send_keepalive(); + }) } #[no_mangle] pub extern "system" fn Java_net_kollnig_missioncontrol_wgbridge_Tunnel_nativeRebind( - mut env: JNIEnv, + mut unowned_env: EnvUnowned, _class: JClass, handle: jlong, ) { - let Some(tunnel) = tunnel_from_handle(handle) else { - throw(&mut env, "tunnel stopped"); - return; - }; - if let Err(e) = tunnel.rebind() { - throw(&mut env, &e); - } + with_native_env!(unowned_env, env, { + let Some(tunnel) = tunnel_from_handle(handle) else { + throw(env, "tunnel stopped"); + return; + }; + if let Err(e) = tunnel.rebind() { + throw(env, &e); + } + }) } #[no_mangle] pub extern "system" fn Java_net_kollnig_missioncontrol_wgbridge_Tunnel_nativeUpdateEndpoint( - mut env: JNIEnv, + mut unowned_env: EnvUnowned, _class: JClass, handle: jlong, public_key: JString, endpoint: JString, ) { - let Some(tunnel) = tunnel_from_handle(handle) else { - throw(&mut env, "tunnel stopped"); - return; - }; - let (Some(public_key), Some(endpoint)) = ( - get_string(&mut env, &public_key), - get_string(&mut env, &endpoint), - ) else { - throw(&mut env, "publicKey and endpoint must not be null"); - return; - }; - let key = match keys::parse_public_key_b64(&public_key) { - Ok(key) => key, - Err(e) => { - throw(&mut env, &e); + with_native_env!(unowned_env, env, { + let Some(tunnel) = tunnel_from_handle(handle) else { + throw(env, "tunnel stopped"); + return; + }; + let (Some(public_key), Some(endpoint)) = + (get_string(env, &public_key), get_string(env, &endpoint)) + else { + throw(env, "publicKey and endpoint must not be null"); return; + }; + let key = match keys::parse_public_key_b64(&public_key) { + Ok(key) => key, + Err(e) => { + throw(env, &e); + return; + } + }; + if let Err(e) = tunnel.update_endpoint(&key, &endpoint) { + throw(env, &e); } - }; - if let Err(e) = tunnel.update_endpoint(&key, &endpoint) { - throw(&mut env, &e); - } + }) } #[no_mangle] pub extern "system" fn Java_net_kollnig_missioncontrol_wgbridge_Tunnel_nativeStop( - _env: JNIEnv, + mut unowned_env: EnvUnowned, _class: JClass, handle: jlong, ) { - if handle == 0 { - return; - } - // SAFETY: reclaims the Box created by nativeStartTunnel. The Java side - // zeroes its handle before calling, so this runs at most once. - let tunnel = unsafe { Box::from_raw(handle as *mut Tunnel) }; - tunnel.stop(); - drop(tunnel); + with_native_env!(unowned_env, _env, { + if handle == 0 { + return; + } + // SAFETY: reclaims the Box created by nativeStartTunnel. The Java side + // zeroes its handle before calling, so this runs at most once. + let tunnel = unsafe { Box::from_raw(handle as *mut Tunnel) }; + tunnel.stop(); + drop(tunnel); + }) } /// Returns whether a `jboolean` from Java is true; kept for future natives. #[allow(dead_code)] fn jbool(b: jboolean) -> bool { - b != 0 + b }