Skip to content

Commit

Permalink
fix: android clipboard permission (rustdesk#10223)
Browse files Browse the repository at this point in the history
* fix: android clipboard permission

Signed-off-by: fufesou <[email protected]>

* refact: Android, clipboard, floating ball

Call rust to check if clipboard is enabled.

Signed-off-by: fufesou <[email protected]>

---------

Signed-off-by: fufesou <[email protected]>
  • Loading branch information
fufesou authored Dec 7, 2024
1 parent 3c838e7 commit 1c17fdd
Show file tree
Hide file tree
Showing 10 changed files with 80 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,8 @@ class FloatingWindowService : Service(), View.OnTouchListener {
popupMenu.menu.add(0, idShowRustDesk, 0, translate("Show RustDesk"))
// For host side, clipboard sync
val idSyncClipboard = 1
val isClipboardListenerEnabled = MainActivity.rdClipboardManager?.isCaptureStarted ?: false
if (isClipboardListenerEnabled) {
val isServiceSyncEnabled = (MainActivity.rdClipboardManager?.isCaptureStarted ?: false) && FFI.isServiceClipboardEnabled()
if (isServiceSyncEnabled) {
popupMenu.menu.add(0, idSyncClipboard, 0, translate("Update client clipboard"))
}
val idStopService = 2
Expand Down
1 change: 1 addition & 0 deletions flutter/android/app/src/main/kotlin/ffi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ object FFI {
external fun setCodecInfo(info: String)
external fun getLocalOption(key: String): String
external fun onClipboardUpdate(clips: ByteBuffer)
external fun isServiceClipboardEnabled(): Boolean
}
2 changes: 2 additions & 0 deletions flutter/lib/mobile/pages/server_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,8 @@ class _PermissionCheckerState extends State<PermissionChecker> {
style: const TextStyle(color: MyTheme.darkGray),
))
]),
PermissionRow(translate("Enable clipboard"), serverModel.clipboardOk,
serverModel.toggleClipboard),
]));
}
}
Expand Down
15 changes: 15 additions & 0 deletions flutter/lib/models/server_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class ServerModel with ChangeNotifier {
bool _inputOk = false;
bool _audioOk = false;
bool _fileOk = false;
bool _clipboardOk = false;
bool _showElevation = false;
bool hideCm = false;
int _connectStatus = 0; // Rendezvous Server status
Expand Down Expand Up @@ -59,6 +60,8 @@ class ServerModel with ChangeNotifier {

bool get fileOk => _fileOk;

bool get clipboardOk => _clipboardOk;

bool get showElevation => _showElevation;

int get connectStatus => _connectStatus;
Expand Down Expand Up @@ -209,6 +212,10 @@ class ServerModel with ChangeNotifier {
_fileOk = fileOption != 'N';
}

// clipboard
final clipOption = await bind.mainGetOption(key: kOptionEnableClipboard);
_clipboardOk = clipOption != 'N';

notifyListeners();
}

Expand Down Expand Up @@ -315,6 +322,14 @@ class ServerModel with ChangeNotifier {
notifyListeners();
}

toggleClipboard() async {
_clipboardOk = !clipboardOk;
bind.mainSetOption(
key: kOptionEnableClipboard,
value: clipboardOk ? defaultOptionYes : 'N');
notifyListeners();
}

toggleInput() async {
if (clients.isNotEmpty) {
await showClientsMayNotBeChangedAlert(parent.target);
Expand Down
26 changes: 21 additions & 5 deletions src/flutter_ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -834,11 +834,19 @@ pub fn main_show_option(_key: String) -> SyncReturn<bool> {
pub fn main_set_option(key: String, value: String) {
#[cfg(target_os = "android")]
if key.eq(config::keys::OPTION_ENABLE_KEYBOARD) {
crate::ui_cm_interface::notify_input_control(config::option2bool(
config::keys::OPTION_ENABLE_KEYBOARD,
&value,
));
crate::ui_cm_interface::switch_permission_all(
"keyboard".to_owned(),
config::option2bool(&key, &value),
);
}
#[cfg(target_os = "android")]
if key.eq(config::keys::OPTION_ENABLE_CLIPBOARD) {
crate::ui_cm_interface::switch_permission_all(
"clipboard".to_owned(),
config::option2bool(&key, &value),
);
}

if key.eq("custom-rendezvous-server") {
set_option(key, value.clone());
#[cfg(target_os = "android")]
Expand Down Expand Up @@ -2332,7 +2340,7 @@ pub mod server_side {
use jni::{
errors::{Error as JniError, Result as JniResult},
objects::{JClass, JObject, JString},
sys::jstring,
sys::{jboolean, jstring},
JNIEnv,
};

Expand Down Expand Up @@ -2405,4 +2413,12 @@ pub mod server_side {
};
return env.new_string(res).unwrap_or_default().into_raw();
}

#[no_mangle]
pub unsafe extern "system" fn Java_ffi_FFI_isServiceClipboardEnabled(
env: JNIEnv,
_class: JClass,
) -> jboolean {
jboolean::from(crate::server::is_clipboard_service_ok())
}
}
2 changes: 0 additions & 2 deletions src/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,6 @@ pub enum Data {
MouseMoveTime(i64),
Authorize,
Close,
#[cfg(target_os = "android")]
InputControl(bool),
#[cfg(windows)]
SAS,
UserSid(Option<u32>),
Expand Down
2 changes: 2 additions & 0 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ pub mod audio_service;
cfg_if::cfg_if! {
if #[cfg(not(target_os = "ios"))] {
mod clipboard_service;
#[cfg(target_os = "android")]
pub use clipboard_service::is_clipboard_service_ok;
#[cfg(target_os = "linux")]
pub(crate) mod wayland;
#[cfg(target_os = "linux")]
Expand Down
12 changes: 12 additions & 0 deletions src/server/clipboard_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use crate::ipc::{self, ClipboardFile, ClipboardNonFile, Data};
use clipboard_master::{CallbackResult, ClipboardHandler};
#[cfg(target_os = "android")]
use hbb_common::config::{keys, option2bool};
#[cfg(target_os = "android")]
use std::sync::atomic::{AtomicBool, Ordering};
use std::{
io,
sync::mpsc::{channel, RecvTimeoutError, Sender},
Expand All @@ -16,6 +18,9 @@ use std::{
#[cfg(windows)]
use tokio::runtime::Runtime;

#[cfg(target_os = "android")]
static CLIPBOARD_SERVICE_OK: AtomicBool = AtomicBool::new(false);

#[cfg(not(target_os = "android"))]
struct Handler {
sp: EmptyExtraFieldService,
Expand All @@ -27,6 +32,11 @@ struct Handler {
rt: Option<Runtime>,
}

#[cfg(target_os = "android")]
pub fn is_clipboard_service_ok() -> bool {
CLIPBOARD_SERVICE_OK.load(Ordering::SeqCst)
}

pub fn new() -> GenericService {
let svc = EmptyExtraFieldService::new(NAME.to_owned(), false);
GenericService::run(&svc.clone(), run);
Expand Down Expand Up @@ -224,11 +234,13 @@ impl Handler {

#[cfg(target_os = "android")]
fn run(sp: EmptyExtraFieldService) -> ResultType<()> {
CLIPBOARD_SERVICE_OK.store(sp.ok(), Ordering::SeqCst);
while sp.ok() {
if let Some(msg) = crate::clipboard::get_clipboards_msg(false) {
sp.send(msg);
}
std::thread::sleep(Duration::from_millis(INTERVAL));
}
CLIPBOARD_SERVICE_OK.store(false, Ordering::SeqCst);
Ok(())
}
32 changes: 14 additions & 18 deletions src/server/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,11 +463,6 @@ impl Connection {
conn.on_close("connection manager", true).await;
break;
}
#[cfg(target_os = "android")]
ipc::Data::InputControl(v) => {
conn.keyboard = v;
conn.send_permission(Permission::Keyboard, v).await;
}
ipc::Data::CmErr(e) => {
if e != "expected" {
// cm closed before connection
Expand All @@ -492,6 +487,9 @@ impl Connection {
conn.keyboard = enabled;
conn.send_permission(Permission::Keyboard, enabled).await;
if let Some(s) = conn.server.upgrade() {
s.write().unwrap().subscribe(
super::clipboard_service::NAME,
conn.inner.clone(), conn.can_sub_clipboard_service());
s.write().unwrap().subscribe(
NAME_CURSOR,
conn.inner.clone(), enabled || conn.show_remote_cursor);
Expand All @@ -502,7 +500,7 @@ impl Connection {
if let Some(s) = conn.server.upgrade() {
s.write().unwrap().subscribe(
super::clipboard_service::NAME,
conn.inner.clone(), conn.clipboard_enabled() && conn.peer_keyboard_enabled());
conn.inner.clone(), conn.can_sub_clipboard_service());
}
} else if &name == "audio" {
conn.audio = enabled;
Expand Down Expand Up @@ -1372,16 +1370,7 @@ impl Connection {
if !self.follow_remote_window {
noperms.push(NAME_WINDOW_FOCUS);
}
// Do not consider the clipboard and keyboard permissions on Android.
// Because syncing the clipboard on Android is manually triggered by the user in the floating ball.
#[cfg(target_os = "android")]
let keyboard_clip_noperm = self.disable_keyboard || self.disable_clipboard;
#[cfg(not(target_os = "android"))]
let keyboard_clip_noperm =
!self.clipboard_enabled() || !self.peer_keyboard_enabled();
if keyboard_clip_noperm
|| crate::get_builtin_option(keys::OPTION_ONE_WAY_CLIPBOARD_REDIRECTION) == "Y"
{
if !self.can_sub_clipboard_service() {
noperms.push(super::clipboard_service::NAME);
}
if !self.audio_enabled() {
Expand Down Expand Up @@ -1453,6 +1442,13 @@ impl Connection {
self.clipboard && !self.disable_clipboard
}

#[inline]
fn can_sub_clipboard_service(&self) -> bool {
self.clipboard_enabled()
&& self.peer_keyboard_enabled()
&& crate::get_builtin_option(keys::OPTION_ONE_WAY_CLIPBOARD_REDIRECTION) != "Y"
}

fn audio_enabled(&self) -> bool {
self.audio && !self.disable_audio
}
Expand Down Expand Up @@ -2930,7 +2926,7 @@ impl Connection {
s.write().unwrap().subscribe(
super::clipboard_service::NAME,
self.inner.clone(),
self.clipboard_enabled() && self.peer_keyboard_enabled(),
self.can_sub_clipboard_service(),
);
}
}
Expand All @@ -2942,7 +2938,7 @@ impl Connection {
s.write().unwrap().subscribe(
super::clipboard_service::NAME,
self.inner.clone(),
self.clipboard_enabled() && self.peer_keyboard_enabled(),
self.can_sub_clipboard_service(),
);
s.write().unwrap().subscribe(
NAME_CURSOR,
Expand Down
20 changes: 11 additions & 9 deletions src/ui_cm_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,15 +280,6 @@ pub fn close(id: i32) {
};
}

#[inline]
#[cfg(target_os = "android")]
pub fn notify_input_control(v: bool) {
for (_, mut client) in CLIENTS.write().unwrap().iter_mut() {
client.keyboard = v;
allow_err!(client.tx.send(Data::InputControl(v)));
}
}

#[inline]
pub fn remove(id: i32) {
CLIENTS.write().unwrap().remove(&id);
Expand All @@ -312,6 +303,17 @@ pub fn switch_permission(id: i32, name: String, enabled: bool) {
};
}

#[inline]
#[cfg(target_os = "android")]
pub fn switch_permission_all(name: String, enabled: bool) {
for (_, client) in CLIENTS.read().unwrap().iter() {
allow_err!(client.tx.send(Data::SwitchPermission {
name: name.clone(),
enabled
}));
}
}

#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
#[inline]
pub fn get_clients_state() -> String {
Expand Down

0 comments on commit 1c17fdd

Please sign in to comment.