fix(client/android): the JNI crate wouldn't build under the unsafe-op lint

`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.
This commit is contained in:
2026-07-29 12:17:18 +02:00
parent b63f9f4ac0
commit 8172e819a4
+7 -2
View File
@@ -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)
}