From 8172e819a4d247f50fd9c8f30c40f381951ff2df Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Wed, 29 Jul 2026 01:41:03 +0200 Subject: [PATCH] fix(client/android): the JNI crate wouldn't build under the unsafe-op lint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `unsafe_op_in_unsafe_fn` is denied workspace-wide, and two operations in the render-callback path were still bare inside their `unsafe fn` — so every Android build failed at `cargo ndk`, before any Kotlin work could reach a device. Pre-existing on main and unrelated to the Android settings/profiles work; found by building the APK for it. Both get the explicit block and the SAFETY note their neighbours in the same file already carry: the reclaimed pointer is the one `install_render_callback` leaked, and the callback's `userdata` is that same pointer, alive for as long as the codec that delivers the call. --- clients/android/native/src/decode/display.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/clients/android/native/src/decode/display.rs b/clients/android/native/src/decode/display.rs index 26794a2e..80c67649 100644 --- a/clients/android/native/src/decode/display.rs +++ b/clients/android/native/src/decode/display.rs @@ -128,7 +128,9 @@ pub(super) fn install_render_callback( /// deleting the codec stops its internal threads, so no callback can still be running (or run /// later) against this pointer. pub(super) unsafe fn release_render_callback(ud: *const DisplayTracker) { - drop(Arc::from_raw(ud)); + // SAFETY: `ud` is the pointer `install_render_callback` leaked from `Arc::into_raw`, and this + // function's contract is that it is reclaimed exactly once, after the codec is gone. + unsafe { drop(Arc::from_raw(ud)) }; } /// The `AMediaCodecOnFrameRendered` trampoline: fires (possibly batched) on a codec-internal @@ -146,7 +148,10 @@ unsafe extern "C" fn on_frame_rendered( media_time_us: i64, system_nano: i64, ) { - let t = &*(userdata as *const DisplayTracker); + // SAFETY: the platform hands back exactly the `userdata` registered with the callback — the + // `Arc::into_raw` pointer from `install_render_callback`, whose refcount is held for as long as + // the codec exists, and the codec is what delivers this call. + let t = unsafe { &*(userdata as *const DisplayTracker) }; if !t.stats.enabled() { return; // HUD hidden — the ring is empty too (pushes are caller-gated) }