1use jni::objects::{GlobalRef, JClass, JObject, JString, JValue};
104use jni::sys::{jboolean, jint, jlong, JNI_FALSE, JNI_TRUE};
105use jni::JNIEnv;
106use log::{error, info, warn};
107use once_cell::sync::OnceCell;
108use rustvncserver::server::{ServerEvent, VncServer};
109use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
110use std::sync::{Arc, Mutex};
111use tokio::runtime::Runtime;
112use tokio::sync::{broadcast, mpsc};
113
114static VNC_RUNTIME: OnceCell<Runtime> = OnceCell::new();
120static VNC_SERVER: OnceCell<Arc<Mutex<Option<Arc<VncServer>>>>> = OnceCell::new();
122static SHUTDOWN_SIGNAL: OnceCell<broadcast::Sender<()>> = OnceCell::new();
124static EVENT_HANDLER_RUNNING: AtomicBool = AtomicBool::new(false);
126
127static JAVA_VM: OnceCell<jni::JavaVM> = OnceCell::new();
129static INPUT_SERVICE_CLASS: OnceCell<GlobalRef> = OnceCell::new();
131static MAIN_SERVICE_CLASS: OnceCell<GlobalRef> = OnceCell::new();
133
134#[allow(dead_code)]
136static NEXT_CLIENT_ID: AtomicU64 = AtomicU64::new(1);
137
138static FRAMEBUFFER_UPDATE_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
140
141pub fn register_vnc_natives(
175 env: &mut JNIEnv,
176 main_service_class: &str,
177 input_service_class: &str,
178) -> Result<(), String> {
179 if let Ok(vm) = env.get_java_vm() {
181 let _ = JAVA_VM.set(vm);
182 }
183
184 let main_class = env.find_class(main_service_class).map_err(|e| {
186 format!(
187 "Failed to find MainService class '{}': {}",
188 main_service_class, e
189 )
190 })?;
191
192 let main_global = env
193 .new_global_ref(main_class)
194 .map_err(|e| format!("Failed to create global ref for MainService: {}", e))?;
195
196 if MAIN_SERVICE_CLASS.set(main_global).is_err() {
197 return Err("MAIN_SERVICE_CLASS was already set".to_string());
198 }
199
200 let input_class = env.find_class(input_service_class).map_err(|e| {
202 format!(
203 "Failed to find InputService class '{}': {}",
204 input_service_class, e
205 )
206 })?;
207
208 let input_global = env
209 .new_global_ref(input_class)
210 .map_err(|e| format!("Failed to create global ref for InputService: {}", e))?;
211
212 if INPUT_SERVICE_CLASS.set(input_global).is_err() {
213 return Err("INPUT_SERVICE_CLASS was already set".to_string());
214 }
215
216 let main_class_local = env
218 .find_class(main_service_class)
219 .map_err(|e| format!("Failed to find MainService for registration: {}", e))?;
220
221 register_main_service_natives(env, main_class_local)?;
222
223 info!(
224 "VNC natives registered for {} and {}",
225 main_service_class, input_service_class
226 );
227
228 Ok(())
229}
230
231fn register_main_service_natives(env: &mut JNIEnv, class: JClass) -> Result<(), String> {
236 use jni::NativeMethod;
237
238 let required_methods: Vec<NativeMethod> = vec![
240 NativeMethod {
241 name: "vncStartServer".into(),
242 sig: "(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z".into(),
243 fn_ptr: native_vnc_start_server as *mut std::ffi::c_void,
244 },
245 NativeMethod {
246 name: "vncStopServer".into(),
247 sig: "()Z".into(),
248 fn_ptr: native_vnc_stop_server as *mut std::ffi::c_void,
249 },
250 NativeMethod {
251 name: "vncIsActive".into(),
252 sig: "()Z".into(),
253 fn_ptr: native_vnc_is_active as *mut std::ffi::c_void,
254 },
255 NativeMethod {
256 name: "vncConnectRepeater".into(),
257 sig: "(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)J".into(),
258 fn_ptr: native_vnc_connect_repeater as *mut std::ffi::c_void,
259 },
260 ];
261
262 env.register_native_methods(&class, &required_methods)
263 .map_err(|e| format!("Failed to register required MainService natives: {}", e))?;
264
265 let optional_methods: Vec<NativeMethod> = vec![
267 NativeMethod {
268 name: "vncInit".into(),
269 sig: "()V".into(),
270 fn_ptr: native_vnc_init as *mut std::ffi::c_void,
271 },
272 NativeMethod {
273 name: "vncUpdateFramebuffer".into(),
274 sig: "(Ljava/nio/ByteBuffer;)Z".into(),
275 fn_ptr: native_vnc_update_framebuffer as *mut std::ffi::c_void,
276 },
277 NativeMethod {
278 name: "vncUpdateFramebufferAndSend".into(),
279 sig: "(Ljava/nio/ByteBuffer;II)Z".into(),
280 fn_ptr: native_vnc_update_framebuffer_and_send as *mut std::ffi::c_void,
281 },
282 NativeMethod {
283 name: "vncUpdateFramebufferCropped".into(),
284 sig: "(Ljava/nio/ByteBuffer;IIIIII)Z".into(),
285 fn_ptr: native_vnc_update_framebuffer_cropped as *mut std::ffi::c_void,
286 },
287 NativeMethod {
288 name: "vncNewFramebuffer".into(),
289 sig: "(II)Z".into(),
290 fn_ptr: native_vnc_new_framebuffer as *mut std::ffi::c_void,
291 },
292 NativeMethod {
293 name: "vncSendCutText".into(),
294 sig: "(Ljava/lang/String;)V".into(),
295 fn_ptr: native_vnc_send_cut_text as *mut std::ffi::c_void,
296 },
297 NativeMethod {
298 name: "vncGetFramebufferWidth".into(),
299 sig: "()I".into(),
300 fn_ptr: native_vnc_get_framebuffer_width as *mut std::ffi::c_void,
301 },
302 NativeMethod {
303 name: "vncGetFramebufferHeight".into(),
304 sig: "()I".into(),
305 fn_ptr: native_vnc_get_framebuffer_height as *mut std::ffi::c_void,
306 },
307 NativeMethod {
308 name: "vncConnectReverse".into(),
309 sig: "(Ljava/lang/String;I)J".into(),
310 fn_ptr: native_vnc_connect_reverse as *mut std::ffi::c_void,
311 },
312 NativeMethod {
313 name: "vncGetRemoteHost".into(),
314 sig: "(J)Ljava/lang/String;".into(),
315 fn_ptr: native_vnc_get_remote_host as *mut std::ffi::c_void,
316 },
317 NativeMethod {
318 name: "vncGetDestinationPort".into(),
319 sig: "(J)I".into(),
320 fn_ptr: native_vnc_get_destination_port as *mut std::ffi::c_void,
321 },
322 NativeMethod {
323 name: "vncGetRepeaterId".into(),
324 sig: "(J)Ljava/lang/String;".into(),
325 fn_ptr: native_vnc_get_repeater_id as *mut std::ffi::c_void,
326 },
327 NativeMethod {
328 name: "vncDisconnect".into(),
329 sig: "(J)Z".into(),
330 fn_ptr: native_vnc_disconnect as *mut std::ffi::c_void,
331 },
332 NativeMethod {
333 name: "vncScheduleCopyRect".into(),
334 sig: "(IIIIII)V".into(),
335 fn_ptr: native_vnc_schedule_copy_rect as *mut std::ffi::c_void,
336 },
337 NativeMethod {
338 name: "vncDoCopyRect".into(),
339 sig: "(IIIIII)Z".into(),
340 fn_ptr: native_vnc_do_copy_rect as *mut std::ffi::c_void,
341 },
342 ];
343
344 for method in optional_methods {
345 let method_name = method.name.to_str().unwrap_or("unknown").to_string();
346 if env.register_native_methods(&class, &[method]).is_err() {
347 let _ = env.exception_clear();
349 log::debug!("Optional method {} not found, skipping", method_name);
350 }
351 }
352
353 Ok(())
354}
355
356fn get_or_init_vnc_runtime() -> &'static Runtime {
362 VNC_RUNTIME.get_or_init(|| {
363 tokio::runtime::Builder::new_multi_thread()
364 .enable_all()
365 .build()
366 .expect("Failed to build VNC Tokio runtime")
367 })
368}
369
370fn get_or_init_shutdown_signal() -> &'static broadcast::Sender<()> {
372 SHUTDOWN_SIGNAL.get_or_init(|| {
373 let (tx, _) = broadcast::channel(16);
374 tx
375 })
376}
377
378extern "system" fn native_vnc_init(env: JNIEnv, _class: JClass) {
383 android_logger::init_once(
385 android_logger::Config::default()
386 .with_max_level(log::LevelFilter::Info)
387 .with_tag("RustVNC"),
388 );
389
390 info!("Initializing Rust VNC Server");
391
392 get_or_init_vnc_runtime();
394 get_or_init_shutdown_signal();
395
396 if JAVA_VM.get().is_none() {
398 if let Ok(vm) = env.get_java_vm() {
399 let _ = JAVA_VM.set(vm);
400 }
401 }
402
403 VNC_SERVER.get_or_init(|| Arc::new(Mutex::new(None)));
405
406 info!("Rust VNC Server initialized");
407}
408
409extern "system" fn native_vnc_start_server(
410 mut env: JNIEnv,
411 _class: JClass,
412 width: jint,
413 height: jint,
414 port: jint,
415 desktop_name: JString,
416 password: JString,
417 _http_root_dir: JString,
418) -> jboolean {
419 const MAX_DIMENSION: i32 = 8192;
421 const MIN_DIMENSION: i32 = 1;
422
423 let width = match u16::try_from(width) {
424 Ok(w) if w >= MIN_DIMENSION as u16 && w <= MAX_DIMENSION as u16 => w,
425 _ => {
426 error!(
427 "Invalid width: {} (must be {}-{})",
428 width, MIN_DIMENSION, MAX_DIMENSION
429 );
430 return JNI_FALSE;
431 }
432 };
433
434 let height = match u16::try_from(height) {
435 Ok(h) if h >= MIN_DIMENSION as u16 && h <= MAX_DIMENSION as u16 => h,
436 _ => {
437 error!(
438 "Invalid height: {} (must be {}-{})",
439 height, MIN_DIMENSION, MAX_DIMENSION
440 );
441 return JNI_FALSE;
442 }
443 };
444
445 let port_opt: Option<u16> = if port == -1 {
447 None
448 } else {
449 match u16::try_from(port) {
450 Ok(p) if p > 0 => Some(p),
451 _ => {
452 error!(
453 "Invalid port: {} (must be -1 for disabled or 1-65535)",
454 port
455 );
456 return JNI_FALSE;
457 }
458 }
459 };
460
461 let desktop_name_str: String = match env.get_string(&desktop_name) {
462 Ok(s) => s.into(),
463 Err(e) => {
464 error!("Failed to get desktop name: {}", e);
465 return JNI_FALSE;
466 }
467 };
468
469 let password_str: Option<String> = if !password.is_null() {
470 match env.get_string(&password) {
471 Ok(s) => {
472 let pw: String = s.into();
473 if pw.is_empty() {
474 None
475 } else {
476 Some(pw)
477 }
478 }
479 Err(_) => None,
480 }
481 } else {
482 None
483 };
484
485 if let Some(p) = port_opt {
486 info!(
487 "Starting Rust VNC Server: {}x{} on port {}",
488 width, height, p
489 );
490 } else {
491 info!(
492 "Starting Rust VNC Server: {}x{} (inbound connections disabled)",
493 width, height
494 );
495 }
496
497 let (server, event_rx) = VncServer::new(width, height, desktop_name_str, password_str);
499 let server: Arc<VncServer> = Arc::new(server);
500
501 if let Some(server_container) = VNC_SERVER.get() {
503 match server_container.lock() {
504 Ok(mut guard) => {
505 *guard = Some(server.clone());
506 }
507 Err(e) => {
508 error!("Failed to lock server container: {}", e);
509 return JNI_FALSE;
510 }
511 }
512 } else {
513 error!("VNC server container not initialized");
514 return JNI_FALSE;
515 }
516
517 spawn_event_handler(event_rx);
519
520 if let Some(listen_port) = port_opt {
522 let runtime = get_or_init_vnc_runtime();
523 let server_clone = server.clone();
524 let mut shutdown_rx = get_or_init_shutdown_signal().subscribe();
525
526 runtime.spawn(async move {
527 tokio::select! {
528 result = server_clone.listen(listen_port) => {
529 if let Err(e) = result {
530 error!("VNC server listen error: {}", e);
531 }
532 }
533 _ = shutdown_rx.recv() => {
534 info!("VNC server received shutdown signal");
535 }
536 }
537 });
538 } else {
539 info!("VNC server running in outbound-only mode (no listener)");
540 }
541
542 info!("Rust VNC Server started successfully");
543 JNI_TRUE
544}
545
546extern "system" fn native_vnc_stop_server(_env: JNIEnv, _class: JClass) -> jboolean {
547 info!("Stopping Rust VNC Server");
548
549 if let Some(server_container) = VNC_SERVER.get() {
551 if let Ok(guard) = server_container.lock() {
552 if let Some(server_arc) = guard.as_ref() {
553 let client_ids = server_arc.get_client_ids().unwrap_or_else(|_| {
554 warn!("Failed to get read lock on client IDs list");
555 Vec::new()
556 });
557
558 info!("Found {} client(s) to disconnect", client_ids.len());
559
560 let runtime = get_or_init_vnc_runtime();
561 let server_clone = server_arc.clone();
562
563 if let Some(vm) = JAVA_VM.get() {
565 if let Ok(mut env) = vm.attach_current_thread() {
566 if let Some(main_class) = MAIN_SERVICE_CLASS.get() {
567 for client_id in &client_ids {
568 info!("Calling Java onClientDisconnected for client {}", client_id);
569 let args = [JValue::Long(*client_id as jlong)];
570 if let Err(e) = env.call_static_method(
571 main_class,
572 "onClientDisconnected",
573 "(J)V",
574 &args,
575 ) {
576 error!(
577 "Failed to call onClientDisconnected for client {}: {}",
578 client_id, e
579 );
580 }
581 }
582 }
583 }
584 }
585
586 info!("Disconnecting all clients with 3s timeout");
588 let disconnect_result = runtime.block_on(async {
589 tokio::time::timeout(
590 tokio::time::Duration::from_secs(3),
591 server_clone.disconnect_all_clients(),
592 )
593 .await
594 });
595
596 match disconnect_result {
597 Ok(_) => info!("All clients disconnected successfully"),
598 Err(_) => warn!("Client disconnect timed out after 3s"),
599 }
600 }
601 }
602 }
603
604 if let Some(shutdown_tx) = SHUTDOWN_SIGNAL.get() {
606 info!("Sending shutdown signal to tasks");
607 let _ = shutdown_tx.send(());
608 }
609
610 if let Some(server_container) = VNC_SERVER.get() {
612 if let Ok(mut guard) = server_container.lock() {
613 *guard = None;
614 info!("Server reference cleared");
615 }
616 }
617
618 EVENT_HANDLER_RUNNING.store(false, Ordering::SeqCst);
620
621 info!("Rust VNC Server stopped successfully");
622 JNI_TRUE
623}
624
625extern "system" fn native_vnc_update_framebuffer(
626 env: JNIEnv,
627 _class: JClass,
628 buffer: JObject,
629) -> jboolean {
630 let buffer_ptr = match env.get_direct_buffer_address((&buffer).into()) {
631 Ok(ptr) => ptr,
632 Err(e) => {
633 error!("Failed to get buffer address: {}", e);
634 return JNI_FALSE;
635 }
636 };
637
638 let buffer_capacity = match env.get_direct_buffer_capacity((&buffer).into()) {
639 Ok(cap) => cap,
640 Err(e) => {
641 error!("Failed to get buffer capacity: {}", e);
642 return JNI_FALSE;
643 }
644 };
645
646 let buffer_copy = {
648 let buffer_slice = unsafe { std::slice::from_raw_parts(buffer_ptr, buffer_capacity) };
649 buffer_slice.to_vec()
650 };
651
652 if FRAMEBUFFER_UPDATE_IN_PROGRESS
654 .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
655 .is_err()
656 {
657 return JNI_TRUE;
658 }
659
660 if let Some(server_container) = VNC_SERVER.get() {
661 if let Ok(guard) = server_container.lock() {
662 if let Some(server) = guard.as_ref() {
663 let rt = get_or_init_vnc_runtime();
664 let server_clone = server.clone();
665
666 rt.spawn(async move {
667 if let Err(e) = server_clone
668 .framebuffer()
669 .update_from_slice(&buffer_copy)
670 .await
671 {
672 error!("Failed to update framebuffer: {}", e);
673 }
674 FRAMEBUFFER_UPDATE_IN_PROGRESS.store(false, Ordering::SeqCst);
675 });
676
677 return JNI_TRUE;
678 }
679 }
680 }
681
682 FRAMEBUFFER_UPDATE_IN_PROGRESS.store(false, Ordering::SeqCst);
683 JNI_FALSE
684}
685
686extern "system" fn native_vnc_update_framebuffer_and_send(
687 env: JNIEnv,
688 class: JClass,
689 buffer: JObject,
690 _width: jint,
691 _height: jint,
692) -> jboolean {
693 native_vnc_update_framebuffer(env, class, buffer)
694}
695
696extern "system" fn native_vnc_update_framebuffer_cropped(
697 env: JNIEnv,
698 _class: JClass,
699 buffer: JObject,
700 _width: jint,
701 _height: jint,
702 crop_x: jint,
703 crop_y: jint,
704 crop_width: jint,
705 crop_height: jint,
706) -> jboolean {
707 let buffer_ptr = match env.get_direct_buffer_address((&buffer).into()) {
708 Ok(ptr) => ptr,
709 Err(e) => {
710 error!("Failed to get buffer address: {}", e);
711 return JNI_FALSE;
712 }
713 };
714
715 let buffer_capacity = match env.get_direct_buffer_capacity((&buffer).into()) {
716 Ok(cap) => cap,
717 Err(e) => {
718 error!("Failed to get buffer capacity: {}", e);
719 return JNI_FALSE;
720 }
721 };
722
723 const MAX_DIMENSION: i32 = 8192;
725
726 if crop_x < 0 || crop_y < 0 || crop_width <= 0 || crop_height <= 0 {
727 error!(
728 "Invalid crop parameters: x={}, y={}, w={}, h={}",
729 crop_x, crop_y, crop_width, crop_height
730 );
731 return JNI_FALSE;
732 }
733
734 if crop_width > MAX_DIMENSION || crop_height > MAX_DIMENSION {
735 error!(
736 "Crop dimensions too large: {}x{} (max {})",
737 crop_width, crop_height, MAX_DIMENSION
738 );
739 return JNI_FALSE;
740 }
741
742 let expected_size = (crop_width as usize)
743 .checked_mul(crop_height as usize)
744 .and_then(|s| s.checked_mul(4))
745 .unwrap_or(0);
746
747 if expected_size == 0 || buffer_capacity != expected_size {
748 error!(
749 "Cropped buffer size mismatch: expected {}, got {}",
750 expected_size, buffer_capacity
751 );
752 return JNI_FALSE;
753 }
754
755 let buffer_copy = {
756 let buffer_slice = unsafe { std::slice::from_raw_parts(buffer_ptr, buffer_capacity) };
757 buffer_slice.to_vec()
758 };
759
760 if FRAMEBUFFER_UPDATE_IN_PROGRESS
761 .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
762 .is_err()
763 {
764 return JNI_TRUE;
765 }
766
767 if let Some(server_container) = VNC_SERVER.get() {
768 if let Ok(guard) = server_container.lock() {
769 if let Some(server) = guard.as_ref() {
770 let rt = get_or_init_vnc_runtime();
771 let server_clone = server.clone();
772
773 rt.spawn(async move {
774 if let Err(e) = server_clone
775 .framebuffer()
776 .update_cropped(
777 &buffer_copy,
778 crop_x as u16,
779 crop_y as u16,
780 crop_width as u16,
781 crop_height as u16,
782 )
783 .await
784 {
785 error!("Failed to update cropped framebuffer: {}", e);
786 }
787 FRAMEBUFFER_UPDATE_IN_PROGRESS.store(false, Ordering::SeqCst);
788 });
789
790 return JNI_TRUE;
791 }
792 }
793 }
794
795 FRAMEBUFFER_UPDATE_IN_PROGRESS.store(false, Ordering::SeqCst);
796 JNI_FALSE
797}
798
799extern "system" fn native_vnc_new_framebuffer(
800 _env: JNIEnv,
801 _class: JClass,
802 width: jint,
803 height: jint,
804) -> jboolean {
805 const MAX_DIMENSION: i32 = 8192;
806 const MIN_DIMENSION: i32 = 1;
807
808 let width = match u16::try_from(width) {
809 Ok(w) if w >= MIN_DIMENSION as u16 && w <= MAX_DIMENSION as u16 => w,
810 _ => {
811 error!(
812 "Invalid width: {} (must be {}-{})",
813 width, MIN_DIMENSION, MAX_DIMENSION
814 );
815 return JNI_FALSE;
816 }
817 };
818
819 let height = match u16::try_from(height) {
820 Ok(h) if h >= MIN_DIMENSION as u16 && h <= MAX_DIMENSION as u16 => h,
821 _ => {
822 error!(
823 "Invalid height: {} (must be {}-{})",
824 height, MIN_DIMENSION, MAX_DIMENSION
825 );
826 return JNI_FALSE;
827 }
828 };
829
830 info!("Resizing framebuffer to {}x{}", width, height);
831
832 if let Some(server_container) = VNC_SERVER.get() {
833 if let Ok(guard) = server_container.lock() {
834 if let Some(server) = guard.as_ref() {
835 let runtime = get_or_init_vnc_runtime();
836
837 if let Err(e) = runtime.block_on(server.framebuffer().resize(width, height)) {
838 error!("Failed to resize framebuffer: {}", e);
839 return JNI_FALSE;
840 }
841
842 info!("Framebuffer resized successfully to {}x{}", width, height);
843 return JNI_TRUE;
844 }
845 }
846 }
847
848 error!("VNC server not initialized");
849 JNI_FALSE
850}
851
852extern "system" fn native_vnc_send_cut_text(mut env: JNIEnv, _class: JClass, text: JString) {
853 let text_str: String = match env.get_string(&text) {
854 Ok(s) => s.into(),
855 Err(e) => {
856 error!("Failed to get cut text: {}", e);
857 return;
858 }
859 };
860
861 if let Some(server_container) = VNC_SERVER.get() {
862 if let Ok(guard) = server_container.lock() {
863 if let Some(server) = guard.as_ref() {
864 let runtime = get_or_init_vnc_runtime();
865 let server_clone = server.clone();
866 let text_clone = text_str;
867
868 runtime.spawn(async move {
869 if let Err(e) = server_clone.send_cut_text_to_all(text_clone).await {
870 error!("Failed to send cut text: {}", e);
871 }
872 });
873 }
874 }
875 }
876}
877
878extern "system" fn native_vnc_is_active(_env: JNIEnv, _class: JClass) -> jboolean {
879 if let Some(server_container) = VNC_SERVER.get() {
880 if let Ok(guard) = server_container.lock() {
881 if guard.is_some() {
882 return JNI_TRUE;
883 }
884 }
885 }
886 JNI_FALSE
887}
888
889extern "system" fn native_vnc_get_framebuffer_width(_env: JNIEnv, _class: JClass) -> jint {
890 if let Some(server_container) = VNC_SERVER.get() {
891 if let Ok(guard) = server_container.lock() {
892 if let Some(server) = guard.as_ref() {
893 return server.framebuffer().width() as jint;
894 }
895 }
896 }
897 -1
898}
899
900extern "system" fn native_vnc_get_framebuffer_height(_env: JNIEnv, _class: JClass) -> jint {
901 if let Some(server_container) = VNC_SERVER.get() {
902 if let Ok(guard) = server_container.lock() {
903 if let Some(server) = guard.as_ref() {
904 return server.framebuffer().height() as jint;
905 }
906 }
907 }
908 -1
909}
910
911extern "system" fn native_vnc_connect_reverse(
912 mut env: JNIEnv,
913 _class: JClass,
914 host: JString,
915 port: jint,
916) -> jlong {
917 let host_str: String = match env.get_string(&host) {
918 Ok(s) => s.into(),
919 Err(e) => {
920 error!("Failed to get reverse connection host: {}", e);
921 return 0;
922 }
923 };
924
925 let port_u16 = port as u16;
926
927 info!("Initiating reverse connection to {}:{}", host_str, port_u16);
928
929 if let Some(server_container) = VNC_SERVER.get() {
930 let server = match server_container.lock() {
931 Ok(guard) => {
932 if let Some(s) = guard.as_ref() {
933 s.clone()
934 } else {
935 error!("VNC server not started");
936 return 0;
937 }
938 }
939 Err(e) => {
940 error!("Failed to lock server container: {}", e);
941 return 0;
942 }
943 };
944
945 let runtime = get_or_init_vnc_runtime();
946
947 return runtime.block_on(async move {
948 match server.connect_reverse(host_str, port_u16).await {
949 Ok(client_id) => {
950 info!("Reverse connection established, client ID: {}", client_id);
951 client_id as jlong
952 }
953 Err(e) => {
954 error!("Failed to establish reverse connection: {}", e);
955 0
956 }
957 }
958 });
959 }
960
961 error!("VNC server not initialized");
962 0
963}
964
965extern "system" fn native_vnc_connect_repeater(
966 mut env: JNIEnv,
967 _class: JClass,
968 host: JString,
969 port: jint,
970 repeater_id: JString,
971 request_id: JString,
972) -> jlong {
973 let host_str: String = match env.get_string(&host) {
974 Ok(s) => s.into(),
975 Err(e) => {
976 error!("Failed to get repeater host: {}", e);
977 return 0;
978 }
979 };
980
981 let repeater_id_str: String = match env.get_string(&repeater_id) {
982 Ok(s) => s.into(),
983 Err(e) => {
984 error!("Failed to get repeater ID: {}", e);
985 return 0;
986 }
987 };
988
989 let request_id_opt: Option<String> = if !request_id.is_null() {
990 match env.get_string(&request_id) {
991 Ok(s) => {
992 let req_id: String = s.into();
993 if req_id.is_empty() {
994 None
995 } else {
996 Some(req_id)
997 }
998 }
999 Err(_) => None,
1000 }
1001 } else {
1002 None
1003 };
1004
1005 let port_u16 = port as u16;
1006
1007 info!(
1008 "Connecting to VNC repeater {}:{} with ID: {}, request_id: {:?}",
1009 host_str, port_u16, repeater_id_str, request_id_opt
1010 );
1011
1012 if let Some(server_container) = VNC_SERVER.get() {
1013 let server = match server_container.lock() {
1014 Ok(guard) => {
1015 if let Some(s) = guard.as_ref() {
1016 s.clone()
1017 } else {
1018 error!("VNC server not started");
1019 return 0;
1020 }
1021 }
1022 Err(e) => {
1023 error!("Failed to lock server container: {}", e);
1024 return 0;
1025 }
1026 };
1027
1028 let runtime = get_or_init_vnc_runtime();
1029
1030 return runtime.block_on(async move {
1031 match server
1032 .connect_repeater_with_request_id(
1033 host_str,
1034 port_u16,
1035 repeater_id_str,
1036 request_id_opt,
1037 )
1038 .await
1039 {
1040 Ok(client_id) => {
1041 info!("Repeater connection established, client ID: {}", client_id);
1042 client_id as jlong
1043 }
1044 Err(e) => {
1045 error!("Failed to connect to repeater: {}", e);
1046 0
1047 }
1048 }
1049 });
1050 }
1051
1052 error!("VNC server not initialized");
1053 0
1054}
1055
1056extern "system" fn native_vnc_get_remote_host<'local>(
1057 env: JNIEnv<'local>,
1058 _class: JClass<'local>,
1059 client_id: jlong,
1060) -> JString<'local> {
1061 if let Some(server_container) = VNC_SERVER.get() {
1062 if let Ok(guard) = server_container.lock() {
1063 if let Some(server) = guard.as_ref() {
1064 if let Ok(clients) = server.clients_try_read() {
1065 for client_arc in clients.iter() {
1066 if let Ok(client_guard) = client_arc.try_read() {
1067 if client_guard.get_client_id() == client_id as usize {
1068 let host = client_guard.get_remote_host().to_string();
1069 if let Ok(jstr) = env.new_string(&host) {
1070 return jstr;
1071 }
1072 }
1073 }
1074 }
1075 }
1076 }
1077 }
1078 }
1079
1080 JString::default()
1081}
1082
1083extern "system" fn native_vnc_get_destination_port(
1084 _env: JNIEnv,
1085 _class: JClass,
1086 client_id: jlong,
1087) -> jint {
1088 if let Some(server_container) = VNC_SERVER.get() {
1089 if let Ok(guard) = server_container.lock() {
1090 if let Some(server) = guard.as_ref() {
1091 if let Ok(clients) = server.clients_try_read() {
1092 for client_arc in clients.iter() {
1093 if let Ok(client_guard) = client_arc.try_read() {
1094 if client_guard.get_client_id() == client_id as usize {
1095 return client_guard.get_destination_port();
1096 }
1097 }
1098 }
1099 }
1100 }
1101 }
1102 }
1103
1104 -1
1105}
1106
1107extern "system" fn native_vnc_get_repeater_id<'local>(
1108 env: JNIEnv<'local>,
1109 _class: JClass<'local>,
1110 client_id: jlong,
1111) -> JString<'local> {
1112 if let Some(server_container) = VNC_SERVER.get() {
1113 if let Ok(guard) = server_container.lock() {
1114 if let Some(server) = guard.as_ref() {
1115 if let Ok(clients) = server.clients_try_read() {
1116 for client_arc in clients.iter() {
1117 if let Ok(client_guard) = client_arc.try_read() {
1118 if client_guard.get_client_id() == client_id as usize {
1119 if let Some(id) = client_guard.get_repeater_id() {
1120 if let Ok(jstr) = env.new_string(id) {
1121 return jstr;
1122 }
1123 }
1124 }
1125 }
1126 }
1127 }
1128 }
1129 }
1130 }
1131
1132 JString::default()
1133}
1134
1135extern "system" fn native_vnc_disconnect(
1136 _env: JNIEnv,
1137 _class: JClass,
1138 client_id: jlong,
1139) -> jboolean {
1140 if let Some(server_container) = VNC_SERVER.get() {
1141 if let Ok(guard) = server_container.lock() {
1142 if let Some(server) = guard.as_ref() {
1143 if let Ok(mut clients) = server.clients_try_write() {
1144 let initial_len = clients.len();
1145
1146 clients.retain(|client_arc| {
1147 if let Ok(client_guard) = client_arc.try_read() {
1148 client_guard.get_client_id() != client_id as usize
1149 } else {
1150 true
1151 }
1152 });
1153
1154 let removed = clients.len() < initial_len;
1155 if removed {
1156 info!("Client {} disconnected successfully", client_id);
1157 return JNI_TRUE;
1158 } else {
1159 warn!("Client {} not found for disconnect", client_id);
1160 return JNI_FALSE;
1161 }
1162 }
1163 }
1164 }
1165 }
1166
1167 JNI_FALSE
1168}
1169
1170extern "system" fn native_vnc_schedule_copy_rect(
1171 _env: JNIEnv,
1172 _class: JClass,
1173 x: jint,
1174 y: jint,
1175 width: jint,
1176 height: jint,
1177 dx: jint,
1178 dy: jint,
1179) {
1180 if x < 0 || y < 0 || width <= 0 || height <= 0 {
1181 error!(
1182 "Invalid copy rect parameters: x={}, y={}, w={}, h={}",
1183 x, y, width, height
1184 );
1185 return;
1186 }
1187
1188 if let Some(server_container) = VNC_SERVER.get() {
1189 if let Ok(guard) = server_container.lock() {
1190 if let Some(server) = guard.as_ref() {
1191 let runtime = get_or_init_vnc_runtime();
1192 let server_clone = server.clone();
1193
1194 runtime.spawn(async move {
1195 server_clone
1196 .schedule_copy_rect(
1197 x as u16,
1198 y as u16,
1199 width as u16,
1200 height as u16,
1201 dx as i16,
1202 dy as i16,
1203 )
1204 .await;
1205 });
1206 }
1207 }
1208 }
1209}
1210
1211extern "system" fn native_vnc_do_copy_rect(
1212 _env: JNIEnv,
1213 _class: JClass,
1214 x: jint,
1215 y: jint,
1216 width: jint,
1217 height: jint,
1218 dx: jint,
1219 dy: jint,
1220) -> jboolean {
1221 if x < 0 || y < 0 || width <= 0 || height <= 0 {
1222 error!(
1223 "Invalid copy rect parameters: x={}, y={}, w={}, h={}",
1224 x, y, width, height
1225 );
1226 return JNI_FALSE;
1227 }
1228
1229 if let Some(server_container) = VNC_SERVER.get() {
1230 if let Ok(guard) = server_container.lock() {
1231 if let Some(server) = guard.as_ref() {
1232 let runtime = get_or_init_vnc_runtime();
1233
1234 let result = runtime.block_on(server.do_copy_rect(
1235 x as u16,
1236 y as u16,
1237 width as u16,
1238 height as u16,
1239 dx as i16,
1240 dy as i16,
1241 ));
1242
1243 match result {
1244 Ok(()) => return JNI_TRUE,
1245 Err(e) => {
1246 error!("Failed to perform copy rect: {}", e);
1247 return JNI_FALSE;
1248 }
1249 }
1250 }
1251 }
1252 }
1253
1254 error!("VNC server not initialized");
1255 JNI_FALSE
1256}
1257
1258fn spawn_event_handler(mut event_rx: mpsc::UnboundedReceiver<ServerEvent>) {
1264 if EVENT_HANDLER_RUNNING
1265 .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
1266 .is_err()
1267 {
1268 warn!("Event handler already running");
1269 return;
1270 }
1271
1272 let runtime = get_or_init_vnc_runtime();
1273 let mut shutdown_rx = get_or_init_shutdown_signal().subscribe();
1274
1275 runtime.spawn(async move {
1276 info!("VNC event handler started");
1277
1278 loop {
1279 tokio::select! {
1280 Some(event) = event_rx.recv() => {
1281 handle_server_event(event);
1282 }
1283 _ = shutdown_rx.recv() => {
1284 info!("Event handler received shutdown signal");
1285 break;
1286 }
1287 }
1288 }
1289
1290 EVENT_HANDLER_RUNNING.store(false, Ordering::SeqCst);
1291 info!("VNC event handler stopped");
1292 });
1293}
1294
1295fn handle_server_event(event: ServerEvent) {
1297 let vm = match JAVA_VM.get() {
1298 Some(vm) => vm,
1299 None => {
1300 error!("Java VM not available");
1301 return;
1302 }
1303 };
1304
1305 let mut env = match vm.attach_current_thread() {
1306 Ok(env) => env,
1307 Err(e) => {
1308 error!("Failed to attach to Java thread: {}", e);
1309 return;
1310 }
1311 };
1312
1313 match event {
1314 ServerEvent::ClientConnected { client_id } => {
1315 info!("Client {} connected", client_id);
1316 if let Some(main_class) = MAIN_SERVICE_CLASS.get() {
1317 let args = [JValue::Long(client_id as jlong)];
1318 if let Err(e) =
1319 env.call_static_method(main_class, "onClientConnected", "(J)V", &args)
1320 {
1321 error!("Failed to call onClientConnected: {}", e);
1322 }
1323 }
1324 }
1325 ServerEvent::ClientDisconnected { client_id } => {
1326 info!("Client {} disconnected", client_id);
1327 if let Some(main_class) = MAIN_SERVICE_CLASS.get() {
1328 let args = [JValue::Long(client_id as jlong)];
1329 if let Err(e) =
1330 env.call_static_method(main_class, "onClientDisconnected", "(J)V", &args)
1331 {
1332 error!("Failed to call onClientDisconnected: {}", e);
1333 }
1334 }
1335 }
1336 ServerEvent::KeyPress {
1337 client_id,
1338 down,
1339 key,
1340 } => {
1341 if let Some(input_class) = INPUT_SERVICE_CLASS.get() {
1342 let args = [
1343 JValue::Int(if down { 1 } else { 0 }),
1344 JValue::Long(key as jlong),
1345 JValue::Long(client_id as jlong),
1346 ];
1347 if let Err(e) = env.call_static_method(input_class, "onKeyEvent", "(IJJ)V", &args) {
1348 error!("Failed to call onKeyEvent: {}", e);
1349 }
1350 }
1351 }
1352 ServerEvent::PointerMove {
1353 client_id,
1354 x,
1355 y,
1356 button_mask,
1357 } => {
1358 if let Some(input_class) = INPUT_SERVICE_CLASS.get() {
1359 let args = [
1360 JValue::Int(button_mask as jint),
1361 JValue::Int(x as jint),
1362 JValue::Int(y as jint),
1363 JValue::Long(client_id as jlong),
1364 ];
1365 if let Err(e) =
1366 env.call_static_method(input_class, "onPointerEvent", "(IIIJ)V", &args)
1367 {
1368 error!("Failed to call onPointerEvent: {}", e);
1369 }
1370 }
1371 }
1372 ServerEvent::CutText { client_id, text } => {
1373 if let Some(input_class) = INPUT_SERVICE_CLASS.get() {
1374 if let Ok(jtext) = env.new_string(&text) {
1375 let args = [JValue::Object(&jtext), JValue::Long(client_id as jlong)];
1376 if let Err(e) = env.call_static_method(
1377 input_class,
1378 "onCutText",
1379 "(Ljava/lang/String;J)V",
1380 &args,
1381 ) {
1382 error!("Failed to call onCutText: {}", e);
1383 }
1384 }
1385 }
1386 }
1387 ServerEvent::RfbMessageSent {
1388 client_id: _,
1389 request_id,
1390 success,
1391 } => {
1392 info!(
1393 "RFB message sent: request_id={:?}, success={}",
1394 request_id, success
1395 );
1396 if let Some(main_class) = MAIN_SERVICE_CLASS.get() {
1397 let request_id_obj: JObject = match &request_id {
1398 Some(id) => match env.new_string(id) {
1399 Ok(jstr) => JObject::from(jstr),
1400 Err(_) => JObject::null(),
1401 },
1402 None => JObject::null(),
1403 };
1404
1405 let args = [
1406 JValue::Object(&request_id_obj),
1407 JValue::Bool(u8::from(success)),
1408 ];
1409 if let Err(e) = env.call_static_method(
1410 main_class,
1411 "notifyRfbMessageSent",
1412 "(Ljava/lang/String;Z)V",
1413 &args,
1414 ) {
1415 error!("Failed to call notifyRfbMessageSent: {}", e);
1416 }
1417 }
1418 }
1419 ServerEvent::HandshakeComplete {
1420 client_id: _,
1421 request_id,
1422 success,
1423 } => {
1424 info!(
1425 "Handshake complete: request_id={:?}, success={}",
1426 request_id, success
1427 );
1428 if let Some(main_class) = MAIN_SERVICE_CLASS.get() {
1429 let request_id_obj: JObject = match &request_id {
1430 Some(id) => match env.new_string(id) {
1431 Ok(jstr) => JObject::from(jstr),
1432 Err(_) => JObject::null(),
1433 },
1434 None => JObject::null(),
1435 };
1436
1437 let args = [
1438 JValue::Object(&request_id_obj),
1439 JValue::Bool(u8::from(success)),
1440 ];
1441 if let Err(e) = env.call_static_method(
1442 main_class,
1443 "notifyHandshakeComplete",
1444 "(Ljava/lang/String;Z)V",
1445 &args,
1446 ) {
1447 error!("Failed to call notifyHandshakeComplete: {}", e);
1448 }
1449 }
1450 }
1451 }
1452}
1453
1454#[no_mangle]
1477pub extern "system" fn JNI_OnLoad(
1478 jvm: jni::JavaVM,
1479 _reserved: *mut std::ffi::c_void,
1480) -> jni::sys::jint {
1481 use jni::sys::{JNI_ERR, JNI_VERSION_1_6};
1482
1483 let mut env = match jvm.get_env() {
1485 Ok(env) => env,
1486 Err(e) => {
1487 eprintln!("JNI_OnLoad: Failed to get JNI environment: {}", e);
1489 return JNI_ERR;
1490 }
1491 };
1492
1493 if let Ok(java_vm) = env.get_java_vm() {
1496 let _ = JAVA_VM.set(java_vm);
1497 }
1498
1499 let log_tag =
1501 read_system_property(&mut env, "rustvnc.log_tag").unwrap_or_else(|| "RustVNC".to_string());
1502
1503 let log_tag_static: &'static str = Box::leak(log_tag.into_boxed_str());
1506 android_logger::init_once(
1507 android_logger::Config::default()
1508 .with_max_level(log::LevelFilter::Info)
1509 .with_tag(log_tag_static),
1510 );
1511
1512 info!("JNI_OnLoad: rustvncserver-android loaded");
1513
1514 let main_class = read_system_property(&mut env, "rustvnc.main_service_class");
1516 let input_class = read_system_property(&mut env, "rustvnc.input_service_class");
1517
1518 match (main_class, input_class) {
1519 (Some(main), Some(input)) => {
1520 info!("JNI_OnLoad: Registering natives for {} and {}", main, input);
1521
1522 match register_vnc_natives(&mut env, &main, &input) {
1523 Ok(()) => {
1524 info!("VNC native methods registered successfully");
1525 get_or_init_vnc_runtime();
1527 get_or_init_shutdown_signal();
1528 VNC_SERVER.get_or_init(|| Arc::new(Mutex::new(None)));
1529 info!("VNC runtime initialized");
1530 }
1531 Err(e) => {
1532 error!("Failed to register VNC native methods: {}", e);
1533 return JNI_ERR;
1534 }
1535 }
1536 }
1537 (None, None) => {
1538 info!(
1539 "JNI_OnLoad: No class properties set, skipping auto-registration. \
1540 Set rustvnc.main_service_class and rustvnc.input_service_class \
1541 before System.loadLibrary(), or call register_vnc_natives() manually."
1542 );
1543 }
1544 (main, input) => {
1545 error!(
1546 "JNI_OnLoad: Partial configuration - main_service_class={:?}, input_service_class={:?}. \
1547 Both must be set or neither.",
1548 main, input
1549 );
1550 return JNI_ERR;
1551 }
1552 }
1553
1554 JNI_VERSION_1_6
1555}
1556
1557fn read_system_property(env: &mut JNIEnv, property_name: &str) -> Option<String> {
1561 let system_class = env.find_class("java/lang/System").ok()?;
1563
1564 let prop_name_jstr = env.new_string(property_name).ok()?;
1566
1567 let result = env
1569 .call_static_method(
1570 system_class,
1571 "getProperty",
1572 "(Ljava/lang/String;)Ljava/lang/String;",
1573 &[JValue::Object(&prop_name_jstr.into())],
1574 )
1575 .ok()?;
1576
1577 let jobj = result.l().ok()?;
1579 if jobj.is_null() {
1580 return None;
1581 }
1582
1583 let jstr = JString::from(jobj);
1584 let rust_str: String = env.get_string(&jstr).ok()?.into();
1585
1586 if rust_str.is_empty() {
1587 None
1588 } else {
1589 Some(rust_str)
1590 }
1591}