feat(android-ffi): nativeLastErrorKind 出口带错误分类(3a)

LAST_ERROR 槽位改存 (kind, detail),新增 nativeLastErrorKind();FFI 契约
kind 先取、message 后取(消息读走清空槽位)。kind 编号与 viewer_core::
ConnectErrorKind::code 一致,跨 FFI 只传整数。JNI 符号 12→13 全部导出(字节扫描核)。
This commit is contained in:
郑博文 2026-10-31 13:29:48 +08:00
parent 4eb82f9779
commit f28db050fb

View File

@ -34,7 +34,8 @@ struct SessionBox {
runtime: tokio::runtime::Runtime,
}
/// 连接被控端。成功返回句柄(>0),失败返回 0;错误消息经 `nativeLastError()` 取。
/// 连接被控端。成功返回句柄(>0),失败返回 0;错误消息经 `nativeLastError()` 取,
/// **错误分类**经 `nativeLastErrorKind()` 取(3a:界面按分类显示中文文案,原始英文作第二行)。
#[no_mangle]
pub extern "system" fn Java_com_verify_remote_RustBridge_nativeConnect<'local>(
mut env: JNIEnv<'local>,
@ -46,14 +47,14 @@ pub extern "system" fn Java_com_verify_remote_RustBridge_nativeConnect<'local>(
let host_s = match env.get_string(&host) {
Ok(s) => s.to_string_lossy().into_owned(),
Err(_) => {
set_last_error("host 参数不是合法字符串");
set_last_error(-1, "host 参数不是合法字符串");
return 0;
}
};
let code_s = match env.get_string(&code) {
Ok(s) => s.to_string_lossy().into_owned(),
Err(_) => {
set_last_error("code 参数不是合法字符串");
set_last_error(-1, "code 参数不是合法字符串");
return 0;
}
};
@ -64,7 +65,7 @@ pub extern "system" fn Java_com_verify_remote_RustBridge_nativeConnect<'local>(
.build()
{
Ok(r) => r,
Err(e) => return Err(format!("runtime init: {e}")),
Err(e) => return Err((-1i32, format!("runtime init: {e}"))),
};
let cfg = ConnectConfig::direct(host_s, port as u16, code_s);
match runtime.block_on(Session::connect(cfg)) {
@ -72,53 +73,73 @@ pub extern "system" fn Java_com_verify_remote_RustBridge_nativeConnect<'local>(
session: Arc::new(session),
runtime,
}),
Err(e) => Err(format!("{e:#}")),
// ConnectError 的 Display 即原始 detail;kind.code() 与
// viewer_core::ConnectErrorKind::code 的映射表一致(FFI 契约)。
Err(e) => Err((e.kind.code(), e.detail)),
}
});
match built {
Ok(Ok(boxed)) => {
set_last_error("");
set_last_error(0, "");
Box::into_raw(Box::new(Mutex::new(boxed))) as jlong
}
Ok(Err(msg)) => {
set_last_error(&msg);
Ok(Err((kind, msg))) => {
set_last_error(kind, &msg);
0
}
Err(_) => {
set_last_error("panic in nativeConnect");
set_last_error(-1, "panic in nativeConnect");
0
}
}
}
fn set_last_error(msg: &str) {
/// kind = 0 表示清除;-1 表示 FFI 层自身错误(无 viewer-core 分类,Kotlin 走 Other 文案)。
fn set_last_error(kind: i32, msg: &str) {
let mut slot = LAST_ERROR.lock().unwrap();
if msg.is_empty() {
*slot = None;
} else {
*slot = Some(msg.to_string());
*slot = Some((kind, msg.to_string()));
}
}
static LAST_ERROR: Mutex<Option<String>> = Mutex::new(None);
static LAST_ERROR: Mutex<Option<(i32, String)>> = Mutex::new(None);
/// 取最近一次 nativeConnect 的错误消息(无错误返回 null)。
/// FFI 契约:调用方必须先 `nativeLastErrorKind()` 再 `nativeLastError()`——
/// 本函数读走消息时**清空整个槽位**(kind 一起失效),颠倒顺序会拿到 kind=0。
#[no_mangle]
pub extern "system" fn Java_com_verify_remote_RustBridge_nativeLastError<'local>(
env: JNIEnv<'local>,
_class: JClass<'local>,
) -> jstring {
let msg = LAST_ERROR.lock().unwrap().take();
let mut slot = LAST_ERROR.lock().unwrap();
let msg = slot.as_ref().map(|(_, m)| m.clone());
match msg {
Some(m) => env
.new_string(m)
.map(|s| s.into_raw())
.unwrap_or(std::ptr::null_mut()),
Some(m) => {
*slot = None;
env.new_string(m)
.map(|s| s.into_raw())
.unwrap_or(std::ptr::null_mut())
}
None => std::ptr::null_mut(),
}
}
/// 取最近一次 nativeConnect 的错误分类码(viewer_core::ConnectErrorKind::code)。
/// 只读不清空;0 = 无错误,-1 = FFI 层自身错误(参数非法/panic/runtime 初始化失败,
/// 无 viewer-core 分类,Kotlin 按 Other 文案 + 原始消息显示)。
#[no_mangle]
pub extern "system" fn Java_com_verify_remote_RustBridge_nativeLastErrorKind(
_env: JNIEnv,
_class: JClass,
) -> jint {
let slot = LAST_ERROR.lock().unwrap();
slot.as_ref().map(|(k, _)| *k).unwrap_or(0)
}
/// 取下一帧(阻塞直到有帧或会话结束)。返回 null = 会话结束。
/// 仅允许解码线程调用;destroy 前必须先停止本线程(见文件头锁纪律)。
#[no_mangle]