diff --git a/Cargo.lock b/Cargo.lock index 934af3701ed..83de7400811 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,6 +121,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" +[[package]] +name = "android-wakelock" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "296e5b7c23adb32743194b1810604b772f2be10f0b0387365cb3ba09cd5c1851" +dependencies = [ + "jni 0.21.1", + "log", + "ndk-context", +] + [[package]] name = "android_log-sys" version = "0.3.1" @@ -5197,6 +5208,7 @@ dependencies = [ name = "rustdesk" version = "1.2.4" dependencies = [ + "android-wakelock", "android_logger", "arboard", "async-process", @@ -5487,6 +5499,7 @@ dependencies = [ "lazy_static", "log", "ndk", + "ndk-context", "num_cpus", "pkg-config", "quest", diff --git a/Cargo.toml b/Cargo.toml index 969d5aee365..354f443ada0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -147,6 +147,7 @@ once_cell = {version = "1.18", optional = true} [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.13" jni = "0.21" +android-wakelock = "0.1" [workspace] members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/portable"] diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt index cc2e20e257f..edec8c42d2f 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt @@ -211,6 +211,7 @@ class MainService : Service() { override fun onCreate() { super.onCreate() Log.d(logTag,"MainService onCreate") + init(this) HandlerThread("Service", Process.THREAD_PRIORITY_BACKGROUND).apply { start() serviceLooper = looper @@ -315,7 +316,6 @@ class MainService : Service() { mediaProjection = mediaProjectionManager.getMediaProjection(Activity.RESULT_OK, it) checkMediaPermission() - init(this) _isReady = true } ?: let { Log.d(logTag, "getParcelableExtra intent null, invoke requestMediaProjection") diff --git a/libs/scrap/Cargo.toml b/libs/scrap/Cargo.toml index d8b176597aa..df2ca2592fa 100644 --- a/libs/scrap/Cargo.toml +++ b/libs/scrap/Cargo.toml @@ -36,6 +36,7 @@ lazy_static = "1.4" log = "0.4" serde_json = "1.0" ndk = { version = "0.7", features = ["media"], optional = true} +ndk-context = "0.1" [target.'cfg(not(target_os = "android"))'.dev-dependencies] repng = "0.2" diff --git a/libs/scrap/src/android/ffi.rs b/libs/scrap/src/android/ffi.rs index c07fe1fb1d6..cba49292097 100644 --- a/libs/scrap/src/android/ffi.rs +++ b/libs/scrap/src/android/ffi.rs @@ -19,6 +19,7 @@ lazy_static! { static ref MAIN_SERVICE_CTX: RwLock> = RwLock::new(None); // MainService -> video service / audio service / info static ref VIDEO_RAW: Mutex = Mutex::new(FrameRaw::new("video", MAX_VIDEO_FRAME_TIMEOUT)); static ref AUDIO_RAW: Mutex = Mutex::new(FrameRaw::new("audio", MAX_AUDIO_FRAME_TIMEOUT)); + static ref NDK_CONTEXT_INITED: Mutex = Default::default(); } const MAX_VIDEO_FRAME_TIMEOUT: Duration = Duration::from_millis(100); @@ -150,6 +151,7 @@ pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_init( *JVM.write().unwrap() = Some(jvm); if let Ok(context) = env.new_global_ref(ctx) { *MAIN_SERVICE_CTX.write().unwrap() = Some(context); + init_ndk_context().ok(); } } } @@ -165,7 +167,12 @@ pub fn call_main_service_pointer_input(kind: &str, mask: i32, x: i32, y: i32) -> ctx, "rustPointerInput", "(Ljava/lang/String;III)V", - &[JValue::Object(&JObject::from(kind)), JValue::Int(mask), JValue::Int(x), JValue::Int(y)], + &[ + JValue::Object(&JObject::from(kind)), + JValue::Int(mask), + JValue::Int(x), + JValue::Int(y), + ], )?; return Ok(()); } else { @@ -246,3 +253,26 @@ pub fn call_main_service_set_by_name( return Err(JniError::ThrowFailed(-1)); } } + +fn init_ndk_context() -> JniResult<()> { + let mut lock = NDK_CONTEXT_INITED.lock().unwrap(); + if *lock { + unsafe { + ndk_context::release_android_context(); + } + } + if let (Some(jvm), Some(ctx)) = ( + JVM.read().unwrap().as_ref(), + MAIN_SERVICE_CTX.read().unwrap().as_ref(), + ) { + unsafe { + ndk_context::initialize_android_context( + jvm.get_java_vm_pointer() as _, + ctx.as_obj().as_raw() as _, + ); + } + *lock = true; + return Ok(()); + } + Err(JniError::ThrowFailed(-1)) +} diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 71aefe8175b..f75e14242b8 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -80,6 +80,39 @@ pub fn get_active_username() -> String { #[cfg(target_os = "android")] pub const PA_SAMPLE_RATE: u32 = 48000; +#[cfg(target_os = "android")] +#[derive(Default)] +pub struct WakeLock { + lock: Option, +} + +#[cfg(target_os = "android")] +impl WakeLock { + pub fn new(tag: &str) -> Self { + let tag = format!("{}:{tag}", crate::get_app_name()); + match android_wakelock::partial(tag) { + Ok(lock) => Self { lock: Some(lock) }, + Err(e) => { + hbb_common::log::error!("Failed to get wakelock: {e:?}"); + Self::default() + } + } + } + + pub fn acquire(&self) -> Option { + match self.lock.as_ref() { + Some(lock) => match lock.acquire() { + Ok(guard) => Some(guard), + Err(e) => { + hbb_common::log::error!("Failed to acquire wakelock guard: {e:?}"); + None + } + }, + None => None, + } + } +} + pub(crate) struct InstallingService; // please use new impl InstallingService { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index c36c6b90d7f..8834935b44c 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -363,8 +363,12 @@ fn get_capturer(current: usize, portable_service_running: bool) -> ResultType ResultType<()> { - #[cfg(not(any(target_os = "android", target_os = "ios")))] + #[cfg(not(target_os = "android"))] let _wake_lock = get_wake_lock(); + #[cfg(target_os = "android")] + let wake_lock = crate::platform::WakeLock::new("video service"); + #[cfg(target_os = "android")] + let _lock_guard = wake_lock.acquire(); // Wayland only support one video capturer for now. It is ok to call ensure_inited() here. // @@ -619,7 +623,6 @@ fn get_recorder( height: usize, codec_name: &CodecName, ) -> Arc>> { - #[cfg(not(target_os = "ios"))] let recorder = if !Config::get_option("allow-auto-record-incoming").is_empty() { use crate::hbbs_http::record_upload; @@ -644,8 +647,6 @@ fn get_recorder( } else { Default::default() }; - #[cfg(target_os = "ios")] - let recorder: Arc>> = Default::default(); recorder } @@ -690,7 +691,6 @@ fn handle_one_frame( vf.display = display as _; let mut msg = Message::new(); msg.set_video_frame(vf); - #[cfg(not(target_os = "ios"))] recorder .lock() .unwrap() @@ -735,7 +735,7 @@ fn start_uac_elevation_check() { }); } -#[cfg(not(any(target_os = "android", target_os = "ios")))] +#[cfg(not(target_os = "android"))] fn get_wake_lock() -> crate::platform::WakeLock { let (display, idle, sleep) = if cfg!(windows) { (true, false, false) @@ -784,7 +784,7 @@ pub fn make_display_changed_msg( width: display.width, height: display.height, cursor_embedded: display_service::capture_cursor_embedded(), - #[cfg(not(any(target_os = "android", target_os = "ios")))] + #[cfg(not(target_os = "android"))] resolutions: Some(SupportedResolutions { resolutions: if display.name.is_empty() { vec![]