diff --git a/android-ffi/src/lib.rs b/android-ffi/src/lib.rs index d283387..09e0f0e 100644 --- a/android-ffi/src/lib.rs +++ b/android-ffi/src/lib.rs @@ -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> = Mutex::new(None); +static LAST_ERROR: Mutex> = 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]