Add Android device deployment flow (#15146)

* Add Android device deployment flow

  Notify the Android Flutter UI when the server requires deployment, add a deploy dialog with API token/custom ID inputs, and reuse shared deploy logic
  for CLI and FFI

Signed-off-by: 21pages <sunboeasy@gmail.com>

* Hide Android deploy API token input

Signed-off-by: 21pages <sunboeasy@gmail.com>

* add more translations

Signed-off-by: 21pages <sunboeasy@gmail.com>

* optimize transations

Signed-off-by: 21pages <sunboeasy@gmail.com>

* Hide deploy action for outgoing-only clients

Signed-off-by: 21pages <sunboeasy@gmail.com>

* Fix deployment register throttle state reset

Signed-off-by: 21pages <sunboeasy@gmail.com>

* Move Android deploy dialog out of settings page

Signed-off-by: 21pages <sunboeasy@gmail.com>

* Use async mutex for deploy register throttle

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
21pages
2026-06-02 14:28:30 +08:00
committed by GitHub
parent 32c6e32e04
commit d99ddf6816
59 changed files with 604 additions and 66 deletions

View File

@@ -42,6 +42,8 @@ static SHOULD_EXIT: AtomicBool = AtomicBool::new(false);
static MANUAL_RESTARTED: AtomicBool = AtomicBool::new(false);
static SENT_REGISTER_PK: AtomicBool = AtomicBool::new(false);
pub(crate) static NEEDS_DEPLOY: AtomicBool = AtomicBool::new(false);
#[cfg(target_os = "android")]
static NOTIFIED_NEEDS_DEPLOY: AtomicBool = AtomicBool::new(false);
// register_pk retry interval (ms) when device is awaiting deployment
const DEPLOY_RETRY_INTERVAL: i64 = 30_000;
lazy_static::lazy_static! {
@@ -66,6 +68,26 @@ async fn deploy_register_throttled() -> bool {
.unwrap_or(false)
}
#[cfg(target_os = "android")]
fn notify_android_needs_deploy() {
if NOTIFIED_NEEDS_DEPLOY.load(Ordering::SeqCst) {
return;
}
let event = serde_json::json!({ "name": "android_needs_deploy" }).to_string();
if matches!(
crate::flutter::push_global_event(crate::flutter::APP_TYPE_MAIN, event),
Some(true)
) {
NOTIFIED_NEEDS_DEPLOY.store(true, Ordering::SeqCst);
}
}
#[cfg(target_os = "android")]
pub(crate) fn reset_needs_deploy_notification() {
NEEDS_DEPLOY.store(false, Ordering::SeqCst);
NOTIFIED_NEEDS_DEPLOY.store(false, Ordering::SeqCst);
}
#[derive(Clone)]
pub struct RendezvousMediator {
addr: TargetAddr<'static>,
@@ -117,6 +139,7 @@ impl RendezvousMediator {
crate::platform::linux_desktop_manager::start_xdesktop();
}
scrap::codec::test_av1();
*LAST_NOT_DEPLOYED_REGISTER.lock().await = None;
loop {
let timeout = Arc::new(RwLock::new(CONNECT_TIMEOUT));
let conn_start_time = Instant::now();
@@ -322,6 +345,8 @@ impl RendezvousMediator {
Config::set_host_key_confirmed(&self.host_prefix, true);
*SOLVING_PK_MISMATCH.lock().await = "".to_owned();
NEEDS_DEPLOY.store(false, Ordering::SeqCst);
#[cfg(target_os = "android")]
reset_needs_deploy_notification();
}
Ok(register_pk_response::Result::UUID_MISMATCH) => {
self.handle_uuid_mismatch(sink).await?;
@@ -336,6 +361,8 @@ impl RendezvousMediator {
// was deleted by an admin while running.
Config::set_key_confirmed(false);
Config::set_host_key_confirmed(&self.host_prefix, false);
#[cfg(target_os = "android")]
notify_android_needs_deploy();
}
_ => {
log::error!("unknown RegisterPkResponse");