بنقرة واحدة
rust-jni-bridge
Rust JNI functions, Kotlin bindings, jni crate usage, and raw JNI versus UniFFI choices.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Rust JNI functions, Kotlin bindings, jni crate usage, and raw JNI versus UniFFI choices.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Use this skill to generate well-branded interfaces and assets for RIPDPI (an Android-native, Compose-first VPN and DPI-bypass app), either for production or throwaway prototypes/mocks/etc. Contains essential design guidelines, colors, type, fonts, brand-ship icons, and an interactive UI kit of every public composable from the live ui/components tree.
Use when managing the Rust workspace, adding/removing crates, editing workspace dependencies, running cargo nextest/audit/deny, configuring Cargo profiles for Android cross-compilation, debugging Cargo.lock churn, migrating crate edition, or wiring Gradle to cargo via the ripdpi.android.rust-native plugin.
Use when modifying the diagnostics scan pipeline, ScanRequest/ScanReport types, ProbeTask families, ripdpi-monitor-engine / ripdpi-diagnostics-* crates, strategy-probe candidates, the diagnostics catalog (packs/profiles), wire-schema contracts between Rust and Kotlin, DIAGNOSTICS_ENGINE_SCHEMA_VERSION, golden contract tests, or adding a new probe type / profile. Triggers on diagnostics scans, strategy probes, automatic audit, dpi-detector profiles, or anything in core/diagnostics or native/rust/crates/ripdpi-monitor-*.
Android-specific Rust build, verification, and packaging — per-target 16 KiB page alignment, size-optimized release profile, ELF symbol allowlist, .so size budgets, NDK 29 specifics. Use when modifying .cargo/config.toml for Android targets, the workspace [profile.release] / [profile.android-jni] block, or when verifying a built .so before release.
Telemetry and observability discipline for the RIPDPI Android Rust stack — control-plane vs data-plane logging, bounded flume event queues, atomic counters, snapshot polling, readiness callbacks, and deterministic JSON contracts. Use when authoring or modifying telemetry emission code, the bounded event ring, the Kotlin-side telemetry consumer, or any per-packet logging.
Custom detekt rules, DI/privacy/suppression guardrails, detekt.yml configuration, and false-positive triage.
| name | rust-jni-bridge |
| description | Rust JNI functions, Kotlin bindings, jni crate usage, and raw JNI versus UniFFI choices. |
Two approaches for Rust-to-Kotlin bindings on Android: raw jni crate (manual, full control) and UniFFI (auto-generated, higher-level). Choose based on complexity and how many functions you expose.
Raw jni crate | UniFFI | |
|---|---|---|
| Control | Full | Framework-managed |
| Boilerplate | High (manual signatures) | Low (generated from UDL/proc-macro) |
| Error handling | Manual JNI exception throwing | Automatic mapping |
| Best for | Few functions, performance-critical | Many functions, complex types |
| Kotlin code | Write manually (external fun) | Auto-generated bindings |
| Crate | jni = "0.22" | uniffi = "0.28" |
For RIPDPI's small handle-based JNI surface, raw jni crate is simpler. Consider UniFFI only if the API grows well beyond the current proxy and tunnel session methods.
use jni::JNIEnv;
use jni::objects::{JObject, JString};
use jni::sys::{jint, jlong};
// Function name must match: Java_<package>_<Class>_<method>
// Package: com.poyka.ripdpi.core, Class: RipDpiProxyNativeBindings
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_poyka_ripdpi_core_RipDpiProxyNativeBindings_jniStart(
mut env: JNIEnv,
_thiz: JObject,
handle: jlong,
) -> jint {
match start_proxy(handle) {
Ok(result) => result,
Err(e) => {
let _ = env.throw_new("java/io/IOException", e.to_string());
-1
}
}
}
// String parameter handling
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_poyka_ripdpi_core_RipDpiProxyNativeBindings_jniCreate(
mut env: JNIEnv,
_thiz: JObject,
config_json: JString,
) -> jlong {
let config_json: String = match env.get_string(&config_json) {
Ok(s) => s.into(),
Err(_) => return 0,
};
create_session(&config_json).unwrap_or(0)
}
class RipDpiProxyNativeBindings @Inject constructor() : RipDpiProxyBindings {
companion object {
init {
RipDpiNativeLoader.ensureLoaded()
}
}
override fun create(configJson: String): Long = jniCreate(configJson)
override fun start(handle: Long): Int = jniStart(handle)
private external fun jniCreate(configJson: String): Long
private external fun jniStart(handle: Long): Int
}
Java_com_poyka_ripdpi_core_RipDpiProxyNativeBindings_<methodName>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^
package (dots -> underscores) method name
Must match exactly. Use #[unsafe(no_mangle)] and extern "system" on every exported function.
RIPDPI currently exports JNI entry points on RipDpiProxyNativeBindings, Tun2SocksNativeBindings, and NetworkDiagnosticsNativeBindings.
// src/main/rust/src/ripdpi.udl
namespace ripdpi {
[Throws=ProxyError]
i32 create_socket(string ip, i32 port);
[Throws=ProxyError]
i32 start_proxy(i32 fd);
[Throws=ProxyError]
i32 stop_proxy(i32 fd);
};
[Error]
enum ProxyError {
"SocketError",
"BindError",
"InvalidAddress",
};
uniffi::setup_scaffolding!();
#[derive(Debug, thiserror::Error, uniffi::Error)]
pub enum ProxyError {
#[error("Socket error")]
SocketError,
#[error("Bind error")]
BindError,
#[error("Invalid address")]
InvalidAddress,
}
#[uniffi::export]
pub fn create_socket(ip: String, port: i32) -> Result<i32, ProxyError> {
// implementation
}
cargo run --locked --bin uniffi-bindgen generate \
--library target/debug/libripdpi.so \
--language kotlin \
--out-dir core/engine/src/main/kotlin/com/poyka/ripdpi/rust
UniFFI generates Kotlin classes, enums, and error types automatically. No external fun declarations needed. RIPDPI does not currently use UniFFI for the Android bridge.
| Kotlin/Java | JNI type | Rust (jni crate) | Rust (UniFFI) |
|---|---|---|---|
Int | jint | jint (i32) | i32 |
Long | jlong | jlong (i64) | i64 |
Boolean | jboolean | jboolean (u8) | bool |
String | JString | JString -> String | String |
ByteArray | jbyteArray | JByteArray -> Vec<u8> | Vec<u8> |
Array<String> | JObjectArray | iterate with get_object_array_element | Vec<String> |
| Current function | Parameters | Returns | Notes |
|---|---|---|---|
jniCreate | String (JSON) | Long (handle) | Creates a proxy or tunnel session |
jniStart | Long / Long, Int | Int or Unit | Proxy start blocks; tunnel start owns its worker thread |
jniStop | Long | Unit | Graceful shutdown |
jniPollTelemetry / jniGetTelemetry | Long | String? | Proxy and tunnel runtime telemetry snapshots |
jniPollProgress / jniTakeReport / jniPollPassiveEvents | Long | String? | Diagnostics progress, final report, and passive events |
jniUpdateNetworkSnapshot | Long, String | Unit | Updates the active proxy session with network metadata |
jniDestroy | Long | Unit | Releases native session state |
| Mistake | Fix |
|---|---|
Missing #[unsafe(no_mangle)] | Required for JNI to find the function by name |
Using extern "C" instead of extern "system" | Use extern "system" for JNI on Android |
| Panicking across FFI boundary | Catch panics with std::panic::catch_unwind; never let panics cross into JVM |
| Leaking JNI local references | Use env.auto_local() or delete_local_ref in loops |
| Not handling JNI exceptions | Check env.exception_check() after calling Java methods from Rust |
rust-code-style -- Rust code style rules for the native workspacerust-lint-config -- Clippy, rustfmt, and cargo-deny configurationrust-crate-architecture -- Crate layering and dependency rules