Combined session state from both agents (shared checkout): - Mesh Party / Flare (experimental): phone-to-phone FIPS via party QR, Flare chat/beam on :5680, optional fixed UDP listen (2121); party-only phones get the baked-in Archipelago anchor so two bare phones reach each other across the internet, not just on a hotspot. - Keep-warm mesh: a running healthy node survives app opens (restart only when dead or peers changed — peersDirty); requestMeshRestart for config changes. Cold start measured on-device: session in ~3.1s. - Restored after regression: startup LAN-vs-mesh race (the party work was based on a pre-#114 file copy) and same-node routing (BTCPay/ Pine/HA had returned to opening in the external browser). - UI: top status-bar scrim removed (bottom-only bars); web app-session mobile bar is solid black — app theme colour no longer bleeds through the bar and its safe-area strip. Served APK: 0.5.7 (vc27). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
130 lines
3.6 KiB
Rust
130 lines
3.6 KiB
Rust
//! JNI surface for `com.archipelago.app.fips.FipsNative` — JSON over strings,
|
|
//! no codegen (the myco / nostr-vpn embedding pattern). Errors come back as
|
|
//! `{"error": "…"}` so Kotlin never sees a raw exception from native code.
|
|
|
|
use std::sync::Once;
|
|
|
|
use jni::objects::{JClass, JString};
|
|
use jni::sys::{jboolean, jint, jstring};
|
|
use jni::JNIEnv;
|
|
|
|
use crate::mesh;
|
|
|
|
static LOG_INIT: Once = Once::new();
|
|
|
|
fn init_logging() {
|
|
LOG_INIT.call_once(|| {
|
|
use tracing_subscriber::layer::SubscriberExt;
|
|
use tracing_subscriber::util::SubscriberInitExt;
|
|
let _ = tracing_subscriber::registry()
|
|
.with(tracing_subscriber::EnvFilter::new("info"))
|
|
.with(paranoid_android::layer("archy-fips"))
|
|
.try_init();
|
|
});
|
|
}
|
|
|
|
fn jstr(env: &mut JNIEnv, s: &JString) -> String {
|
|
env.get_string(s).map(|s| s.into()).unwrap_or_default()
|
|
}
|
|
|
|
fn out(env: &JNIEnv, s: String) -> jstring {
|
|
env.new_string(s)
|
|
.map(|s| s.into_raw())
|
|
.unwrap_or(std::ptr::null_mut())
|
|
}
|
|
|
|
fn err_json(e: impl std::fmt::Display) -> String {
|
|
serde_json::json!({ "error": e.to_string() }).to_string()
|
|
}
|
|
|
|
fn identity_json(info: &mesh::IdentityInfo) -> String {
|
|
serde_json::json!({
|
|
"secret": info.secret_hex,
|
|
"npub": info.npub,
|
|
"address": info.address,
|
|
})
|
|
.to_string()
|
|
}
|
|
|
|
/// Kotlin: `external fun generateIdentity(): String`
|
|
#[no_mangle]
|
|
pub extern "system" fn Java_com_archipelago_app_fips_FipsNative_generateIdentity(
|
|
env: JNIEnv,
|
|
_class: JClass,
|
|
) -> jstring {
|
|
init_logging();
|
|
let json = match mesh::generate_identity() {
|
|
Ok(info) => identity_json(&info),
|
|
Err(e) => err_json(e),
|
|
};
|
|
out(&env, json)
|
|
}
|
|
|
|
/// Kotlin: `external fun deriveIdentity(secret: String): String`
|
|
#[no_mangle]
|
|
pub extern "system" fn Java_com_archipelago_app_fips_FipsNative_deriveIdentity(
|
|
mut env: JNIEnv,
|
|
_class: JClass,
|
|
secret: JString,
|
|
) -> jstring {
|
|
init_logging();
|
|
let secret = jstr(&mut env, &secret);
|
|
let json = match mesh::derive_identity(&secret) {
|
|
Ok(info) => identity_json(&info),
|
|
Err(e) => err_json(e),
|
|
};
|
|
out(&env, json)
|
|
}
|
|
|
|
/// Kotlin: `external fun start(secret: String, peersJson: String, tunFd: Int, listenPort: Int): String`
|
|
/// Returns `{"npub": "...", "address": "..."}` or `{"error": "..."}`.
|
|
/// `listenPort` 0 = outbound-only; non-zero = fixed UDP bind (party mode).
|
|
#[no_mangle]
|
|
pub extern "system" fn Java_com_archipelago_app_fips_FipsNative_start(
|
|
mut env: JNIEnv,
|
|
_class: JClass,
|
|
secret: JString,
|
|
peers_json: JString,
|
|
tun_fd: jint,
|
|
listen_port: jint,
|
|
) -> jstring {
|
|
init_logging();
|
|
let secret = jstr(&mut env, &secret);
|
|
let peers = jstr(&mut env, &peers_json);
|
|
let listen_port = u16::try_from(listen_port).unwrap_or(0);
|
|
let json = match mesh::start(&secret, &peers, tun_fd, listen_port) {
|
|
Ok((npub, address)) => {
|
|
serde_json::json!({ "npub": npub, "address": address }).to_string()
|
|
}
|
|
Err(e) => err_json(e),
|
|
};
|
|
out(&env, json)
|
|
}
|
|
|
|
/// Kotlin: `external fun stop()`
|
|
#[no_mangle]
|
|
pub extern "system" fn Java_com_archipelago_app_fips_FipsNative_stop(
|
|
_env: JNIEnv,
|
|
_class: JClass,
|
|
) {
|
|
mesh::stop();
|
|
}
|
|
|
|
/// Kotlin: `external fun isRunning(): Boolean`
|
|
#[no_mangle]
|
|
pub extern "system" fn Java_com_archipelago_app_fips_FipsNative_isRunning(
|
|
_env: JNIEnv,
|
|
_class: JClass,
|
|
) -> jboolean {
|
|
mesh::is_running() as jboolean
|
|
}
|
|
|
|
/// Kotlin: `external fun statusJson(): String`
|
|
#[no_mangle]
|
|
pub extern "system" fn Java_com_archipelago_app_fips_FipsNative_statusJson(
|
|
env: JNIEnv,
|
|
_class: JClass,
|
|
) -> jstring {
|
|
out(&env, mesh::status_json())
|
|
}
|