fix: android clipboard permission (#10223)
* fix: android clipboard permission Signed-off-by: fufesou <linlong1266@gmail.com> * refact: Android, clipboard, floating ball Call rust to check if clipboard is enabled. Signed-off-by: fufesou <linlong1266@gmail.com> --------- Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
parent
3c838e7a92
commit
1c17fddf51
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -597,6 +597,8 @@ class _PermissionCheckerState extends State<PermissionChecker> {
|
||||
style: const TextStyle(color: MyTheme.darkGray),
|
||||
))
|
||||
]),
|
||||
PermissionRow(translate("Enable clipboard"), serverModel.clipboardOk,
|
||||
serverModel.toggleClipboard),
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
@ -59,6 +60,8 @@ class ServerModel with ChangeNotifier {
|
||||
|
||||
bool get fileOk => _fileOk;
|
||||
|
||||
bool get clipboardOk => _clipboardOk;
|
||||
|
||||
bool get showElevation => _showElevation;
|
||||
|
||||
int get connectStatus => _connectStatus;
|
||||
@ -209,6 +212,10 @@ class ServerModel with ChangeNotifier {
|
||||
_fileOk = fileOption != 'N';
|
||||
}
|
||||
|
||||
// clipboard
|
||||
final clipOption = await bind.mainGetOption(key: kOptionEnableClipboard);
|
||||
_clipboardOk = clipOption != 'N';
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
@ -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")]
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -217,8 +217,6 @@ pub enum Data {
|
||||
MouseMoveTime(i64),
|
||||
Authorize,
|
||||
Close,
|
||||
#[cfg(target_os = "android")]
|
||||
InputControl(bool),
|
||||
#[cfg(windows)]
|
||||
SAS,
|
||||
UserSid(Option<u32>),
|
||||
|
@ -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")]
|
||||
|
@ -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},
|
||||
@ -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,
|
||||
@ -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);
|
||||
@ -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(())
|
||||
}
|
||||
|
@ -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
|
||||
@ -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);
|
||||
@ -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;
|
||||
@ -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() {
|
||||
@ -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
|
||||
}
|
||||
@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
|
@ -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);
|
||||
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user