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