use std::path::PathBuf;
use std::sync::mpsc::{channel, Receiver, RecvTimeoutError, Sender};
use std::thread;
use std::time::Duration;
use droidkraft_core::features::fastboot::{FastbootCommand, FastbootManager};
use droidkraft_core::features::flash::{RebootTarget, RootStatus};
use droidkraft_core::features::rom::{self, DeviceProfile, DownloadProgress, FlashStep, RomBuild};
use droidkraft_core::{AdbManager, DeviceStatus};
use crate::commands::CommandAction;
trait StringErr<T> {
fn str_err(self) -> Result<T, String>;
}
impl<T, E: std::fmt::Display> StringErr<T> for Result<T, E> {
fn str_err(self) -> Result<T, String> {
self.map_err(|e| e.to_string())
}
}
const STATUS_INTERVAL: Duration = Duration::from_secs(2);
pub enum WorkerRequest {
Run {
label: String,
action: CommandAction,
},
Shell { label: String, command: String },
DetectRoot,
Remount,
Fastboot {
label: String,
command: FastbootCommand,
},
RefreshStatus,
DetectProfile,
FetchRoms { codename: String },
Download { build: RomBuild, dest: PathBuf },
RunFlashStep {
idx: usize,
step: FlashStep,
serial: String,
},
}
pub enum WorkerResponse {
Status(DeviceStatus),
Output {
label: String,
result: Result<String, String>,
},
Root(Result<RootStatus, String>),
Profile(DeviceProfile),
Roms(Result<Vec<RomBuild>, String>),
DownloadProgress(DownloadProgress),
Downloaded(Result<PathBuf, String>),
FlashStepDone {
idx: usize,
result: Result<String, String>,
},
}
pub struct Worker {
req_tx: Sender<WorkerRequest>,
res_rx: Receiver<WorkerResponse>,
}
impl Worker {
pub fn spawn() -> Self {
let (req_tx, req_rx) = channel::<WorkerRequest>();
let (res_tx, res_rx) = channel::<WorkerResponse>();
thread::spawn(move || worker_loop(req_rx, res_tx));
Self { req_tx, res_rx }
}
pub fn send(&self, req: WorkerRequest) {
let _ = self.req_tx.send(req);
}
pub fn drain(&self) -> Vec<WorkerResponse> {
self.res_rx.try_iter().collect()
}
}
fn worker_loop(req_rx: Receiver<WorkerRequest>, res_tx: Sender<WorkerResponse>) {
let mut adb = AdbManager::new();
let _ = res_tx.send(WorkerResponse::Status(adb.fetch_device_status()));
loop {
match req_rx.recv_timeout(STATUS_INTERVAL) {
Ok(req) => {
handle_request(&mut adb, &res_tx, req);
}
Err(RecvTimeoutError::Timeout) => {
let _ = res_tx.send(WorkerResponse::Status(adb.fetch_device_status()));
}
Err(RecvTimeoutError::Disconnected) => break,
}
}
}
fn handle_request(adb: &mut AdbManager, res_tx: &Sender<WorkerResponse>, req: WorkerRequest) {
match req {
WorkerRequest::RefreshStatus => {
let _ = res_tx.send(WorkerResponse::Status(adb.fetch_device_status()));
}
WorkerRequest::Run { label, action } => {
let result = run_action(adb, action);
let _ = res_tx.send(WorkerResponse::Output { label, result });
}
WorkerRequest::Shell { label, command } => {
let result = adb.shell_command(&command).str_err();
let _ = res_tx.send(WorkerResponse::Output { label, result });
}
WorkerRequest::DetectRoot => {
let result = adb.detect_root().str_err();
let _ = res_tx.send(WorkerResponse::Root(result));
}
WorkerRequest::Remount => {
let result = adb.remount().str_err();
let _ = res_tx.send(WorkerResponse::Output {
label: "Remount".into(),
result,
});
}
WorkerRequest::Fastboot { label, command } => {
let result = FastbootManager::new().execute(command).str_err();
let _ = res_tx.send(WorkerResponse::Output { label, result });
}
WorkerRequest::DetectProfile => {
if let Ok(profile) = adb.detect_device_profile() {
let _ = res_tx.send(WorkerResponse::Profile(profile));
}
}
WorkerRequest::FetchRoms { codename } => {
let _ = res_tx.send(WorkerResponse::Roms(fetch_roms(&codename)));
}
WorkerRequest::Download { build, dest } => {
let tx = res_tx.clone();
thread::spawn(move || {
let result = rom::download_and_verify(
&build.download_url,
&dest,
build.sha256.as_deref(),
|p| {
let _ = tx.send(WorkerResponse::DownloadProgress(p));
},
);
let _ = tx.send(WorkerResponse::Downloaded(result));
});
}
WorkerRequest::RunFlashStep { idx, step, serial } => {
let fastboot = FastbootManager::new();
let result = rom::run_flash_step(&step, &serial, adb, &fastboot);
let _ = res_tx.send(WorkerResponse::FlashStepDone { idx, result });
}
}
}
fn fetch_roms(codename: &str) -> Result<Vec<RomBuild>, String> {
droidkraft_core::features::rom::lineage::fetch_builds(codename)
}
fn run_action(adb: &mut AdbManager, action: CommandAction) -> Result<String, String> {
match action {
CommandAction::Adb(cmd) => adb.execute(cmd).str_err(),
CommandAction::Shell(cmd) => adb.shell_command(&cmd).str_err(),
CommandAction::Reboot(target) => reboot(adb, target),
}
}
fn reboot(adb: &mut AdbManager, target: RebootTarget) -> Result<String, String> {
adb.reboot(target).str_err()
}