Cross-compile and package the Rust core for Android with UniFFI Kotlin bindings #92

Open
opened 2026-08-17 19:10:20 +00:00 by hugo · 0 comments
Owner

Goal

Ship the existing Rust storage implementation as ABI-specific Android native libraries and expose a typed, generated Kotlin API.

Scope

  • Add an Android bridge crate as a thin mechanical UniFFI layer over crates/storage; reuse or extract the platform-neutral parts of crates/apple instead of maintaining two hand-copied interfaces.
  • Build cdylib outputs with the Android NDK for aarch64-linux-android, armv7-linux-androideabi, and x86_64-linux-android; document how to narrow the release ABI set only through the support decision in #88.
  • Prefer cargo-ndk from Gradle tasks to place each .so in the correct generated jniLibs/<abi> directory. Do not commit build outputs.
  • Generate Kotlin bindings from the built library with the workspace-pinned UniFFI tool, enable UniFFI Android mode and immutable records, and package the required JNA Android artifact.
  • Make Gradle task inputs/outputs explicit so Rust compilation and binding generation are incremental, variant-aware, reproducible, and run before Kotlin compilation.
  • Preserve checksums and fail fast when Kotlin bindings and a native library do not match.
  • Keep bridge calls off the Android main thread, map cancellation and typed errors, and define callback/thread-attachment rules for any Rust-to-Kotlin platform callback.
  • Package stripped release libraries plus native debug symbols separately; verify AAB ABI splits and loading on physical arm64 and x86_64 emulator devices.
  • Verify every native dependency, including JNA, is compatible with 16 KB page sizes.

Acceptance criteria

  • Debug and release APK/AAB builds load the correct ABI library and call a fixture function through generated Kotlin without handwritten domain JNI.
  • All selected ABIs compile from a clean checkout with the pinned Rust, NDK, UniFFI, and JNA versions.
  • Generated APIs expose view-ready types and redact secrets in Debug/Display/error paths.
  • No Kotlin feature code parses Rust display strings to recover state.
  • Wrong/missing ABI, checksum mismatch, and native load failure produce deterministic non-secret diagnostics.
  • Rust project crates remain free of handwritten unsafe; generated/dependency FFI is isolated at the boundary.

Depends on #88 and the Android Gradle scaffold issue. Part of #87.

## Goal Ship the existing Rust storage implementation as ABI-specific Android native libraries and expose a typed, generated Kotlin API. ## Scope - Add an Android bridge crate as a thin mechanical UniFFI layer over `crates/storage`; reuse or extract the platform-neutral parts of `crates/apple` instead of maintaining two hand-copied interfaces. - Build `cdylib` outputs with the Android NDK for `aarch64-linux-android`, `armv7-linux-androideabi`, and `x86_64-linux-android`; document how to narrow the release ABI set only through the support decision in #88. - Prefer `cargo-ndk` from Gradle tasks to place each `.so` in the correct generated `jniLibs/<abi>` directory. Do not commit build outputs. - Generate Kotlin bindings from the built library with the workspace-pinned UniFFI tool, enable UniFFI Android mode and immutable records, and package the required JNA Android artifact. - Make Gradle task inputs/outputs explicit so Rust compilation and binding generation are incremental, variant-aware, reproducible, and run before Kotlin compilation. - Preserve checksums and fail fast when Kotlin bindings and a native library do not match. - Keep bridge calls off the Android main thread, map cancellation and typed errors, and define callback/thread-attachment rules for any Rust-to-Kotlin platform callback. - Package stripped release libraries plus native debug symbols separately; verify AAB ABI splits and loading on physical arm64 and x86_64 emulator devices. - Verify every native dependency, including JNA, is compatible with 16 KB page sizes. ## Acceptance criteria - Debug and release APK/AAB builds load the correct ABI library and call a fixture function through generated Kotlin without handwritten domain JNI. - All selected ABIs compile from a clean checkout with the pinned Rust, NDK, UniFFI, and JNA versions. - Generated APIs expose view-ready types and redact secrets in Debug/Display/error paths. - No Kotlin feature code parses Rust display strings to recover state. - Wrong/missing ABI, checksum mismatch, and native load failure produce deterministic non-secret diagnostics. - Rust project crates remain free of handwritten `unsafe`; generated/dependency FFI is isolated at the boundary. Depends on #88 and the Android Gradle scaffold issue. Part of #87.
hugo added this to the 06 - Implement an Android version of the software milestone 2026-08-17 19:10:20 +00:00
hugo added the enhancement label 2026-08-17 19:10:20 +00:00
Sign in to join this conversation.