fix: ping as engine instance method — same lifecycle as call
Ping was a static JNI method that loaded the .so before nativeInit, crashing jemalloc. Now ping is an instance method on WzpEngine: - Engine is created once (nativeInit), reused for both ping and call - pingRelay() uses same tokio runtime pattern as startCall() - Auto-pings all servers on app launch (after engine init) - No process restart needed - TOFU fingerprints saved on first successful ping Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
193
Cargo.lock
generated
193
Cargo.lock
generated
@@ -297,6 +297,12 @@ dependencies = [
|
|||||||
"tower-service",
|
"tower-service",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base16ct"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.22.1"
|
version = "0.22.1"
|
||||||
@@ -467,6 +473,7 @@ dependencies = [
|
|||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"serde",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
@@ -627,6 +634,24 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crunchy"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-bigint"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
@@ -650,6 +675,7 @@ dependencies = [
|
|||||||
"digest",
|
"digest",
|
||||||
"fiat-crypto",
|
"fiat-crypto",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
|
"serde",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@@ -816,6 +842,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
|
"const-oid",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
@@ -850,6 +877,21 @@ dependencies = [
|
|||||||
"rustfft",
|
"rustfft",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ecdsa"
|
||||||
|
version = "0.16.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
|
||||||
|
dependencies = [
|
||||||
|
"der",
|
||||||
|
"digest",
|
||||||
|
"elliptic-curve",
|
||||||
|
"rfc6979",
|
||||||
|
"serdect",
|
||||||
|
"signature",
|
||||||
|
"spki",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ed25519"
|
name = "ed25519"
|
||||||
version = "2.2.3"
|
version = "2.2.3"
|
||||||
@@ -857,6 +899,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
|
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
|
"serde",
|
||||||
"signature",
|
"signature",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -881,6 +924,26 @@ version = "1.15.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "elliptic-curve"
|
||||||
|
version = "0.13.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"crypto-bigint",
|
||||||
|
"digest",
|
||||||
|
"ff",
|
||||||
|
"generic-array",
|
||||||
|
"group",
|
||||||
|
"pkcs8",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"sec1",
|
||||||
|
"serdect",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.35"
|
version = "0.8.35"
|
||||||
@@ -924,6 +987,16 @@ version = "2.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ff"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fiat-crypto"
|
name = "fiat-crypto"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
@@ -1084,6 +1157,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
"version_check",
|
"version_check",
|
||||||
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1143,6 +1217,17 @@ version = "0.3.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "group"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
|
||||||
|
dependencies = [
|
||||||
|
"ff",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.4.13"
|
version = "0.4.13"
|
||||||
@@ -1626,6 +1711,21 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "k256"
|
||||||
|
version = "0.13.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"ecdsa",
|
||||||
|
"elliptic-curve",
|
||||||
|
"once_cell",
|
||||||
|
"serdect",
|
||||||
|
"sha2",
|
||||||
|
"signature",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -1702,6 +1802,15 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchers"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||||
|
dependencies = [
|
||||||
|
"regex-automata",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchit"
|
name = "matchit"
|
||||||
version = "0.7.3"
|
version = "0.7.3"
|
||||||
@@ -2389,6 +2498,16 @@ dependencies = [
|
|||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rfc6979"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
|
||||||
|
dependencies = [
|
||||||
|
"hmac",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.14"
|
version = "0.17.14"
|
||||||
@@ -2567,6 +2686,21 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sec1"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"der",
|
||||||
|
"generic-array",
|
||||||
|
"pkcs8",
|
||||||
|
"serdect",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework"
|
name = "security-framework"
|
||||||
version = "3.7.0"
|
version = "3.7.0"
|
||||||
@@ -2671,6 +2805,16 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serdect"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
@@ -2724,6 +2868,7 @@ version = "2.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"digest",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2937,6 +3082,15 @@ version = "0.1.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
|
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny-keccak"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||||
|
dependencies = [
|
||||||
|
"crunchy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinystr"
|
name = "tinystr"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
@@ -3235,10 +3389,14 @@ version = "0.3.23"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
|
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"matchers",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
|
"once_cell",
|
||||||
|
"regex-automata",
|
||||||
"sharded-slab",
|
"sharded-slab",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thread_local",
|
"thread_local",
|
||||||
|
"tracing",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-log",
|
"tracing-log",
|
||||||
]
|
]
|
||||||
@@ -3367,6 +3525,18 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.4.2",
|
||||||
|
"js-sys",
|
||||||
|
"serde_core",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "valuable"
|
name = "valuable"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -3406,7 +3576,28 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "warzone-protocol"
|
name = "warzone-protocol"
|
||||||
version = "0.1.0"
|
version = "0.0.38"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"bincode",
|
||||||
|
"bip39",
|
||||||
|
"chacha20poly1305",
|
||||||
|
"chrono",
|
||||||
|
"curve25519-dalek",
|
||||||
|
"ed25519-dalek",
|
||||||
|
"hex",
|
||||||
|
"hkdf",
|
||||||
|
"k256",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sha2",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"tiny-keccak",
|
||||||
|
"uuid",
|
||||||
|
"x25519-dalek",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
|
|||||||
@@ -153,20 +153,21 @@ class WzpEngine(private val callback: WzpCallback) {
|
|||||||
private external fun nativeWriteAudioDirect(handle: Long, buffer: java.nio.ByteBuffer, sampleCount: Int): Int
|
private external fun nativeWriteAudioDirect(handle: Long, buffer: java.nio.ByteBuffer, sampleCount: Int): Int
|
||||||
private external fun nativeReadAudioDirect(handle: Long, buffer: java.nio.ByteBuffer, maxSamples: Int): Int
|
private external fun nativeReadAudioDirect(handle: Long, buffer: java.nio.ByteBuffer, maxSamples: Int): Int
|
||||||
private external fun nativeDestroy(handle: Long)
|
private external fun nativeDestroy(handle: Long)
|
||||||
|
private external fun nativePingRelay(handle: Long, relay: String): String?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ping a relay server. Requires engine to be initialized.
|
||||||
|
* Returns JSON `{"rtt_ms":N,"server_fingerprint":"hex"}` or null.
|
||||||
|
*/
|
||||||
|
fun pingRelay(address: String): String? {
|
||||||
|
if (nativeHandle == 0L) return null
|
||||||
|
return nativePingRelay(nativeHandle, address)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
init {
|
init {
|
||||||
System.loadLibrary("wzp_android")
|
System.loadLibrary("wzp_android")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Ping a relay server. Returns JSON `{"rtt_ms":N,"server_fingerprint":"hex"}`
|
|
||||||
* or null if unreachable. Does not require an engine instance.
|
|
||||||
*/
|
|
||||||
fun pingRelay(address: String): String? = nativePingRelay(address)
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
private external fun nativePingRelay(relay: String): String?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,36 +1,12 @@
|
|||||||
package com.wzp.net
|
package com.wzp.net
|
||||||
|
|
||||||
/**
|
// Relay pinging is now done via WzpEngine.pingRelay() (instance method).
|
||||||
* Relay ping via native QUIC — requires loading the native .so.
|
// This file kept for the data class only.
|
||||||
* After ping completes, the process must be restarted (System.exit)
|
|
||||||
* because jemalloc initialization during .so load corrupts state
|
|
||||||
* on Android 16 MTE devices.
|
|
||||||
*
|
|
||||||
* Flow: ping all servers → save results → exit → app restarts → load results
|
|
||||||
*/
|
|
||||||
object RelayPinger {
|
|
||||||
|
|
||||||
|
object RelayPinger {
|
||||||
data class PingResult(
|
data class PingResult(
|
||||||
val rttMs: Int,
|
val rttMs: Int,
|
||||||
val reachable: Boolean,
|
val reachable: Boolean,
|
||||||
val serverFingerprint: String = "",
|
val serverFingerprint: String = "",
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
|
||||||
* Ping a relay via the native QUIC stack.
|
|
||||||
* WARNING: After calling this, the process must be restarted.
|
|
||||||
*/
|
|
||||||
fun ping(address: String): PingResult {
|
|
||||||
return try {
|
|
||||||
val json = com.wzp.engine.WzpEngine.pingRelay(address) ?: return PingResult(0, false)
|
|
||||||
val obj = org.json.JSONObject(json)
|
|
||||||
PingResult(
|
|
||||||
rttMs = obj.getInt("rtt_ms"),
|
|
||||||
reachable = true,
|
|
||||||
serverFingerprint = obj.optString("server_fingerprint", ""),
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
PingResult(0, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,53 +208,59 @@ class CallViewModel : ViewModel(), WzpCallback {
|
|||||||
settings?.saveSelectedServer(_selectedServer.value)
|
settings?.saveSelectedServer(_selectedServer.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load saved ping results from last ping-and-exit cycle. */
|
/**
|
||||||
fun loadSavedPingResults() {
|
* Ping all servers via native QUIC. Requires engine to be initialized.
|
||||||
val s = settings ?: return
|
* Creates engine if needed, pings, keeps engine alive for subsequent Connect.
|
||||||
|
*/
|
||||||
|
fun pingAllServers() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
// Ensure engine exists
|
||||||
|
if (engine == null || engine?.isInitialized != true) {
|
||||||
|
try {
|
||||||
|
engine = WzpEngine(this@CallViewModel).also { it.init() }
|
||||||
|
engineInitialized = true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "engine init for ping failed: $e")
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val eng = engine ?: return@launch
|
||||||
|
|
||||||
val results = mutableMapOf<String, PingResult>()
|
val results = mutableMapOf<String, PingResult>()
|
||||||
val known = mutableMapOf<String, String>()
|
val known = mutableMapOf<String, String>()
|
||||||
_servers.value.forEach { server ->
|
_servers.value.forEach { server ->
|
||||||
val rtt = s.loadPingRtt(server.address)
|
val json = withContext(Dispatchers.IO) {
|
||||||
val fp = s.loadServerFingerprint(server.address)
|
eng.pingRelay(server.address)
|
||||||
if (rtt >= 0) {
|
}
|
||||||
results[server.address] = PingResult(rttMs = rtt, serverFingerprint = fp ?: "")
|
if (json != null) {
|
||||||
|
try {
|
||||||
|
val obj = JSONObject(json)
|
||||||
|
val rtt = obj.getInt("rtt_ms")
|
||||||
|
val fp = obj.optString("server_fingerprint", "")
|
||||||
|
results[server.address] = PingResult(rttMs = rtt, serverFingerprint = fp)
|
||||||
|
// TOFU
|
||||||
|
if (fp.isNotEmpty()) {
|
||||||
|
val saved = settings?.loadServerFingerprint(server.address)
|
||||||
|
if (saved == null) settings?.saveServerFingerprint(server.address, fp)
|
||||||
|
known[server.address] = saved ?: fp
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {}
|
||||||
}
|
}
|
||||||
fp?.let { known[server.address] = it }
|
|
||||||
}
|
}
|
||||||
_pingResults.value = results
|
_pingResults.value = results
|
||||||
_knownFingerprints.value = known
|
_knownFingerprints.value = known
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/** Load saved TOFU fingerprints. */
|
||||||
* Ping all servers via native QUIC, save results, then exit process.
|
fun loadSavedFingerprints() {
|
||||||
* On restart, saved results are loaded. This avoids the jemalloc crash
|
val known = mutableMapOf<String, String>()
|
||||||
* by ensuring the native .so is only loaded once per process lifetime.
|
|
||||||
*/
|
|
||||||
fun pingAndExit() {
|
|
||||||
viewModelScope.launch {
|
|
||||||
val results = mutableMapOf<String, PingResult>()
|
|
||||||
_servers.value.forEach { server ->
|
_servers.value.forEach { server ->
|
||||||
val pr = withContext(Dispatchers.IO) {
|
settings?.loadServerFingerprint(server.address)?.let {
|
||||||
com.wzp.net.RelayPinger.ping(server.address)
|
known[server.address] = it
|
||||||
}
|
|
||||||
results[server.address] = PingResult(
|
|
||||||
rttMs = pr.rttMs,
|
|
||||||
serverFingerprint = pr.serverFingerprint,
|
|
||||||
)
|
|
||||||
// Save results
|
|
||||||
settings?.savePingRtt(server.address, pr.rttMs)
|
|
||||||
if (pr.serverFingerprint.isNotEmpty()) {
|
|
||||||
val saved = settings?.loadServerFingerprint(server.address)
|
|
||||||
if (saved == null) {
|
|
||||||
settings?.saveServerFingerprint(server.address, pr.serverFingerprint)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
_knownFingerprints.value = known
|
||||||
_pingResults.value = results
|
|
||||||
// Exit process — next launch loads saved results, native .so reinits cleanly
|
|
||||||
delay(300) // let UI update
|
|
||||||
System.exit(0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get lock status for a server. */
|
/** Get lock status for a server. */
|
||||||
|
|||||||
@@ -90,8 +90,11 @@ fun InCallScreen(
|
|||||||
|
|
||||||
var showManageRelays by remember { mutableStateOf(false) }
|
var showManageRelays by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
// Load saved ping results from last ping-and-exit cycle
|
// Ping servers on launch — engine init + QUIC ping, no restart needed
|
||||||
LaunchedEffect(Unit) { viewModel.loadSavedPingResults() }
|
LaunchedEffect(Unit) {
|
||||||
|
viewModel.loadSavedFingerprints()
|
||||||
|
viewModel.pingAllServers()
|
||||||
|
}
|
||||||
|
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
@@ -227,21 +230,6 @@ fun InCallScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
|
|
||||||
// Ping button — pings all servers via native QUIC, saves results, exits app
|
|
||||||
OutlinedButton(
|
|
||||||
onClick = { viewModel.pingAndExit() },
|
|
||||||
modifier = Modifier.fillMaxWidth().height(40.dp),
|
|
||||||
shape = RoundedCornerShape(8.dp),
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
"Ping Servers (restarts app)",
|
|
||||||
style = MaterialTheme.typography.labelSmall,
|
|
||||||
color = TextDim
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
errorMessage?.let { err ->
|
errorMessage?.let { err ->
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
Text(text = err, color = Red, style = MaterialTheme.typography.bodySmall)
|
Text(text = err, color = Red, style = MaterialTheme.typography.bodySmall)
|
||||||
|
|||||||
@@ -169,6 +169,46 @@ impl WzpEngine {
|
|||||||
info!("stop_call: done");
|
info!("stop_call: done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ping a relay — same pattern as start_call (creates runtime on calling thread).
|
||||||
|
/// Returns JSON `{"rtt_ms":N,"server_fingerprint":"hex"}` or error.
|
||||||
|
pub fn ping_relay(&self, address: &str) -> Result<String, anyhow::Error> {
|
||||||
|
let addr: SocketAddr = address.parse()?;
|
||||||
|
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||||
|
|
||||||
|
let rt = tokio::runtime::Builder::new_current_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
rt.block_on(async {
|
||||||
|
let bind: SocketAddr = "0.0.0.0:0".parse().unwrap();
|
||||||
|
let endpoint = wzp_transport::create_endpoint(bind, None)?;
|
||||||
|
let client_cfg = wzp_transport::client_config();
|
||||||
|
let start = Instant::now();
|
||||||
|
|
||||||
|
let conn = tokio::time::timeout(
|
||||||
|
std::time::Duration::from_secs(3),
|
||||||
|
wzp_transport::connect(&endpoint, addr, "ping", client_cfg),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| anyhow::anyhow!("timeout"))??;
|
||||||
|
|
||||||
|
let rtt_ms = start.elapsed().as_millis() as u64;
|
||||||
|
let server_fp = conn
|
||||||
|
.peer_identity()
|
||||||
|
.and_then(|id| id.downcast::<Vec<rustls::pki_types::CertificateDer>>().ok())
|
||||||
|
.and_then(|certs| certs.first().map(|c| {
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
let mut h = std::collections::hash_map::DefaultHasher::new();
|
||||||
|
c.as_ref().hash(&mut h);
|
||||||
|
format!("{:016x}", h.finish())
|
||||||
|
}))
|
||||||
|
.unwrap_or_default();
|
||||||
|
conn.close(0u32.into(), b"ping");
|
||||||
|
|
||||||
|
Ok(format!(r#"{{"rtt_ms":{},"server_fingerprint":"{}"}}"#, rtt_ms, server_fp))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_mute(&self, muted: bool) {
|
pub fn set_mute(&self, muted: bool) {
|
||||||
self.state.muted.store(muted, Ordering::Relaxed);
|
self.state.muted.store(muted, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -318,71 +318,22 @@ pub unsafe extern "system" fn Java_com_wzp_engine_WzpEngine_nativeDestroy(
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ping a relay server — returns JSON `{"rtt_ms":N,"server_fingerprint":"hex"}` or null on failure.
|
/// Ping a relay server — instance method, requires engine handle.
|
||||||
/// Does NOT require an engine handle — creates a temporary QUIC connection.
|
/// Returns JSON `{"rtt_ms":N,"server_fingerprint":"hex"}` or null on failure.
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "system" fn Java_com_wzp_engine_WzpEngine_nativePingRelay<'a>(
|
pub unsafe extern "system" fn Java_com_wzp_engine_WzpEngine_nativePingRelay<'a>(
|
||||||
mut env: JNIEnv<'a>,
|
mut env: JNIEnv<'a>,
|
||||||
_class: JClass,
|
_class: JClass,
|
||||||
|
handle: jlong,
|
||||||
relay_j: JString,
|
relay_j: JString,
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||||
|
let h = unsafe { handle_ref(handle) };
|
||||||
let relay: String = env.get_string(&relay_j).map(|s| s.into()).unwrap_or_default();
|
let relay: String = env.get_string(&relay_j).map(|s| s.into()).unwrap_or_default();
|
||||||
let addr: std::net::SocketAddr = match relay.parse() {
|
match h.engine.ping_relay(&relay) {
|
||||||
Ok(a) => a,
|
Ok(json) => Some(json),
|
||||||
Err(_) => return None,
|
Err(_) => None,
|
||||||
};
|
|
||||||
|
|
||||||
let _ = rustls::crypto::ring::default_provider().install_default();
|
|
||||||
|
|
||||||
let rt = match tokio::runtime::Builder::new_current_thread()
|
|
||||||
.enable_all()
|
|
||||||
.build()
|
|
||||||
{
|
|
||||||
Ok(rt) => rt,
|
|
||||||
Err(_) => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
rt.block_on(async {
|
|
||||||
let bind: std::net::SocketAddr = "0.0.0.0:0".parse().unwrap();
|
|
||||||
let endpoint = match wzp_transport::create_endpoint(bind, None) {
|
|
||||||
Ok(e) => e,
|
|
||||||
Err(_) => return None,
|
|
||||||
};
|
|
||||||
let client_cfg = wzp_transport::client_config();
|
|
||||||
let start = std::time::Instant::now();
|
|
||||||
|
|
||||||
match tokio::time::timeout(
|
|
||||||
std::time::Duration::from_secs(3),
|
|
||||||
wzp_transport::connect(&endpoint, addr, "ping", client_cfg),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(Ok(conn)) => {
|
|
||||||
let rtt_ms = start.elapsed().as_millis() as u64;
|
|
||||||
let server_fp = conn
|
|
||||||
.peer_identity()
|
|
||||||
.and_then(|id| {
|
|
||||||
id.downcast::<Vec<rustls::pki_types::CertificateDer>>().ok()
|
|
||||||
})
|
|
||||||
.and_then(|certs| {
|
|
||||||
certs.first().map(|c| {
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
let mut h = std::collections::hash_map::DefaultHasher::new();
|
|
||||||
c.as_ref().hash(&mut h);
|
|
||||||
format!("{:016x}", h.finish())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
conn.close(0u32.into(), b"ping");
|
|
||||||
Some(format!(
|
|
||||||
r#"{{"rtt_ms":{},"server_fingerprint":"{}"}}"#,
|
|
||||||
rtt_ms, server_fp
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let json = match result {
|
let json = match result {
|
||||||
|
|||||||
Reference in New Issue
Block a user