feat: add mdns devices discovery (#54)
* feat: add mdns devices discovery --------- Co-authored-by: Jinke <164604729+JinkeJ@users.noreply.github.com>
This commit is contained in:
2
.github/workflows/rust-build.yml
vendored
2
.github/workflows/rust-build.yml
vendored
@@ -12,4 +12,4 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build project
|
||||
run: cargo build --release
|
||||
run: cargo build --release --all-features
|
||||
2
.github/workflows/rust-quality.yml
vendored
2
.github/workflows/rust-quality.yml
vendored
@@ -39,4 +39,4 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
run: cargo test --verbose --all-features
|
||||
|
||||
2
.github/workflows/rust-release.yml
vendored
2
.github/workflows/rust-release.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
cargo install cargo-generate-rpm
|
||||
|
||||
- name: "build-release"
|
||||
run: cargo build --release
|
||||
run: cargo build --all-features --release
|
||||
|
||||
- name: "Build DEB package"
|
||||
run: cargo deb -p adb_cli
|
||||
|
||||
@@ -9,7 +9,7 @@ homepage = "https://github.com/cocool97/adb_client"
|
||||
keywords = ["adb", "android", "tcp", "usb"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/cocool97/adb_client"
|
||||
version = "2.0.5"
|
||||
version = "2.0.6"
|
||||
|
||||
# To build locally when working on a new release
|
||||
[patch.crates-io]
|
||||
|
||||
@@ -21,4 +21,19 @@ pub enum HostCommand {
|
||||
Connect { address: SocketAddrV4 },
|
||||
/// Disconnect device over WI-FI
|
||||
Disconnect { address: SocketAddrV4 },
|
||||
/// MDNS services
|
||||
Mdns {
|
||||
#[clap(subcommand)]
|
||||
subcommand: MdnsCommand,
|
||||
},
|
||||
/// Display server status
|
||||
ServerStatus,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub enum MdnsCommand {
|
||||
/// Check mdns status
|
||||
Check,
|
||||
/// List mdns services available
|
||||
Services,
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ mod tcp;
|
||||
mod usb;
|
||||
|
||||
pub use emu::EmuCommand;
|
||||
pub use host::HostCommand;
|
||||
pub use host::{HostCommand, MdnsCommand};
|
||||
pub use local::LocalCommand;
|
||||
pub use tcp::{TcpCommand, TcpCommands};
|
||||
pub use usb::{UsbCommand, UsbCommands};
|
||||
|
||||
@@ -8,24 +8,40 @@ mod models;
|
||||
|
||||
use adb_client::{
|
||||
ADBDeviceExt, ADBEmulatorDevice, ADBServer, ADBTcpDevice, ADBUSBDevice, DeviceShort,
|
||||
MDNSBackend, MDNSDiscoveryService,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use clap::Parser;
|
||||
use commands::{EmuCommand, HostCommand, LocalCommand, TcpCommands, UsbCommands};
|
||||
use commands::{EmuCommand, HostCommand, LocalCommand, MdnsCommand, TcpCommands, UsbCommands};
|
||||
use models::{Command, Opts};
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let opt = Opts::parse();
|
||||
let opts = Opts::parse();
|
||||
|
||||
// RUST_LOG variable has more priority then "--debug" flag
|
||||
if std::env::var("RUST_LOG").is_err() {
|
||||
let level = match opts.debug {
|
||||
true => "trace",
|
||||
false => "info",
|
||||
};
|
||||
|
||||
std::env::set_var("RUST_LOG", level);
|
||||
}
|
||||
|
||||
// Setting default log level as "info" if not set
|
||||
if std::env::var("RUST_LOG").is_err() {
|
||||
std::env::set_var("RUST_LOG", "info");
|
||||
}
|
||||
env_logger::init();
|
||||
|
||||
match opt.command {
|
||||
match opts.command {
|
||||
Command::Local(local) => {
|
||||
let mut adb_server = ADBServer::new(opt.address);
|
||||
let mut adb_server = ADBServer::new(opts.address);
|
||||
|
||||
let mut device = match opt.serial {
|
||||
let mut device = match opts.serial {
|
||||
Some(serial) => adb_server.get_device_by_name(&serial)?,
|
||||
None => adb_server.get_device()?,
|
||||
};
|
||||
@@ -104,7 +120,7 @@ fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
Command::Host(host) => {
|
||||
let mut adb_server = ADBServer::new(opt.address);
|
||||
let mut adb_server = ADBServer::new(opts.address);
|
||||
|
||||
match host {
|
||||
HostCommand::Version => {
|
||||
@@ -148,10 +164,35 @@ fn main() -> Result<()> {
|
||||
adb_server.disconnect_device(address)?;
|
||||
log::info!("Disconnected {address}");
|
||||
}
|
||||
HostCommand::Mdns { subcommand } => match subcommand {
|
||||
MdnsCommand::Check => {
|
||||
let check = adb_server.mdns_check()?;
|
||||
let server_status = adb_server.server_status()?;
|
||||
match server_status.mdns_backend {
|
||||
MDNSBackend::Unknown => log::info!("unknown mdns backend..."),
|
||||
MDNSBackend::Bonjour => match check {
|
||||
true => log::info!("mdns daemon version [Bonjour]"),
|
||||
false => log::info!("ERROR: mdns daemon unavailable"),
|
||||
},
|
||||
MDNSBackend::OpenScreen => {
|
||||
log::info!("mdns daemon version [Openscreen discovery 0.0.0]")
|
||||
}
|
||||
}
|
||||
}
|
||||
MdnsCommand::Services => {
|
||||
log::info!("List of discovered mdns services");
|
||||
for service in adb_server.mdns_services()? {
|
||||
log::info!("{}", service);
|
||||
}
|
||||
}
|
||||
},
|
||||
HostCommand::ServerStatus => {
|
||||
log::info!("{}", adb_server.server_status()?);
|
||||
}
|
||||
}
|
||||
}
|
||||
Command::Emu(emu) => {
|
||||
let mut emulator = match opt.serial {
|
||||
let mut emulator = match opts.serial {
|
||||
Some(serial) => ADBEmulatorDevice::new(serial, None)?,
|
||||
None => return Err(anyhow!("Serial must be set to use emulators !")),
|
||||
};
|
||||
@@ -289,6 +330,23 @@ fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
}
|
||||
Command::MdnsDiscovery => {
|
||||
let mut service = MDNSDiscoveryService::new()?;
|
||||
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
service.start(tx)?;
|
||||
|
||||
log::info!("Starting mdns discovery...");
|
||||
while let Ok(device) = rx.recv() {
|
||||
log::info!(
|
||||
"Found device {} with addresses {:?}",
|
||||
device.fullname,
|
||||
device.addresses
|
||||
)
|
||||
}
|
||||
|
||||
service.shutdown()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -7,6 +7,8 @@ use crate::commands::{EmuCommand, HostCommand, LocalCommand, TcpCommand, UsbComm
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(about, version, author)]
|
||||
pub struct Opts {
|
||||
#[clap(long = "debug")]
|
||||
pub debug: bool,
|
||||
#[clap(short = 'a', long = "address", default_value = "127.0.0.1:5037")]
|
||||
pub address: SocketAddrV4,
|
||||
/// Serial id of a specific device. Every request will be sent to this device.
|
||||
@@ -29,4 +31,6 @@ pub enum Command {
|
||||
Usb(UsbCommand),
|
||||
/// Device commands via TCP, no server needed
|
||||
Tcp(TcpCommand),
|
||||
/// Discover devices over MDNS without using adb-server
|
||||
MdnsDiscovery,
|
||||
}
|
||||
|
||||
@@ -16,9 +16,12 @@ byteorder = { version = "1.5.0" }
|
||||
chrono = { version = "0.4.38" }
|
||||
homedir = { version = "0.3.4" }
|
||||
image = { version = "0.25.5" }
|
||||
log = { version = "0.4.22", features = ["max_level_debug", "release_max_level_debug"]}
|
||||
lazy_static = { version = "1.5.0" }
|
||||
log = { version = "0.4.22" }
|
||||
mdns-sd = { version = "0.12.0" }
|
||||
num-bigint = { version = "0.8.4", package = "num-bigint-dig" }
|
||||
num-traits = { version = "0.2.19" }
|
||||
quick-protobuf = { version = "0.8.1" }
|
||||
rand = { version = "0.8.5" }
|
||||
rcgen = { version = "0.13.1" }
|
||||
regex = { version = "1.11.0", features = ["perf", "std", "unicode"] }
|
||||
|
||||
@@ -108,6 +108,12 @@ pub enum RustADBError {
|
||||
/// Cannot upgrade connection from TCP to TLS
|
||||
#[error("upgrade error: {0}")]
|
||||
UpgradeError(String),
|
||||
/// An error occurred while getting mdns devices
|
||||
#[error(transparent)]
|
||||
MDNSError(#[from] mdns_sd::Error),
|
||||
/// An error occurred while sending data to channel
|
||||
#[error(transparent)]
|
||||
SendError(#[from] std::sync::mpsc::SendError<crate::MDNSDevice>),
|
||||
}
|
||||
|
||||
impl<T> From<std::sync::PoisonError<T>> for RustADBError {
|
||||
|
||||
@@ -9,6 +9,7 @@ mod constants;
|
||||
mod device;
|
||||
mod emulator_device;
|
||||
mod error;
|
||||
mod mdns;
|
||||
mod models;
|
||||
mod server;
|
||||
mod server_device;
|
||||
@@ -19,7 +20,10 @@ pub use adb_device_ext::ADBDeviceExt;
|
||||
pub use device::{ADBTcpDevice, ADBUSBDevice};
|
||||
pub use emulator_device::ADBEmulatorDevice;
|
||||
pub use error::{Result, RustADBError};
|
||||
pub use models::{AdbStatResponse, AdbVersion, DeviceLong, DeviceShort, DeviceState, RebootType};
|
||||
pub use mdns::*;
|
||||
pub use models::{
|
||||
AdbStatResponse, AdbVersion, DeviceLong, DeviceShort, DeviceState, MDNSBackend, RebootType,
|
||||
};
|
||||
pub use server::*;
|
||||
pub use server_device::ADBServerDevice;
|
||||
pub use transports::*;
|
||||
|
||||
19
adb_client/src/mdns/mdns_device.rs
Normal file
19
adb_client/src/mdns/mdns_device.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use std::{collections::HashSet, net::IpAddr};
|
||||
|
||||
/// Represent a device found from mdns search
|
||||
#[derive(Debug)]
|
||||
pub struct MDNSDevice {
|
||||
/// Full device address when resolved
|
||||
pub fullname: String,
|
||||
/// Device IP addresses
|
||||
pub addresses: HashSet<IpAddr>,
|
||||
}
|
||||
|
||||
impl From<mdns_sd::ServiceInfo> for MDNSDevice {
|
||||
fn from(value: mdns_sd::ServiceInfo) -> Self {
|
||||
Self {
|
||||
fullname: value.get_fullname().to_string(),
|
||||
addresses: value.get_addresses().to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
73
adb_client/src/mdns/mdns_discovery.rs
Normal file
73
adb_client/src/mdns/mdns_discovery.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use mdns_sd::{ServiceDaemon, ServiceEvent};
|
||||
use std::{sync::mpsc::Sender, thread::JoinHandle};
|
||||
|
||||
use crate::{MDNSDevice, Result, RustADBError};
|
||||
|
||||
const ADB_SERVICE_NAME: &str = "_adb-tls-connect._tcp.local.";
|
||||
|
||||
/// Structure holding responsibility over mdns discovery
|
||||
pub struct MDNSDiscoveryService {
|
||||
daemon: ServiceDaemon,
|
||||
thread_handle: Option<JoinHandle<Result<()>>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for MDNSDiscoveryService {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MDNSDiscoveryService")
|
||||
.field("daemon", &self.daemon.get_metrics())
|
||||
.field("handle", &self.thread_handle)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl MDNSDiscoveryService {
|
||||
/// Instantiate a new discovery service to find devices over mdns
|
||||
pub fn new() -> Result<Self> {
|
||||
Ok(MDNSDiscoveryService {
|
||||
daemon: ServiceDaemon::new()?,
|
||||
thread_handle: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Start discovery by spawning a new thread responsible of getting events.
|
||||
pub fn start(&mut self, sender: Sender<MDNSDevice>) -> Result<()> {
|
||||
let receiver = self.daemon.browse(ADB_SERVICE_NAME)?;
|
||||
|
||||
let handle: JoinHandle<Result<()>> = std::thread::spawn(move || loop {
|
||||
while let Ok(event) = receiver.recv() {
|
||||
match event {
|
||||
ServiceEvent::SearchStarted(_)
|
||||
| ServiceEvent::ServiceRemoved(_, _)
|
||||
| ServiceEvent::ServiceFound(_, _)
|
||||
| ServiceEvent::SearchStopped(_) => {
|
||||
// Ignoring these events. We are only interesting in found devices
|
||||
continue;
|
||||
}
|
||||
ServiceEvent::ServiceResolved(service_info) => {
|
||||
if let Err(e) = sender.send(MDNSDevice::from(service_info)) {
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
self.thread_handle = Some(handle);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Shutdown discovery engines.
|
||||
pub fn shutdown(&mut self) -> Result<()> {
|
||||
match self.daemon.shutdown() {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => match e {
|
||||
mdns_sd::Error::Again => {
|
||||
self.daemon.shutdown()?;
|
||||
Ok(())
|
||||
}
|
||||
e => Err(RustADBError::MDNSError(e)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
5
adb_client/src/mdns/mod.rs
Normal file
5
adb_client/src/mdns/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod mdns_device;
|
||||
mod mdns_discovery;
|
||||
|
||||
pub use mdns_device::MDNSDevice;
|
||||
pub use mdns_discovery::MDNSDiscoveryService;
|
||||
@@ -16,6 +16,10 @@ pub(crate) enum AdbServerCommand {
|
||||
Pair(SocketAddrV4, String),
|
||||
TransportAny,
|
||||
TransportSerial(String),
|
||||
MDNSCheck,
|
||||
MDNSServices,
|
||||
ServerStatus,
|
||||
ReconnectOffline,
|
||||
Install(u64),
|
||||
// Local commands
|
||||
ShellCommand(String),
|
||||
@@ -23,8 +27,13 @@ pub(crate) enum AdbServerCommand {
|
||||
FrameBuffer,
|
||||
Sync,
|
||||
Reboot(RebootType),
|
||||
Forward(String, String, String),
|
||||
Forward(String, String),
|
||||
ForwardRemoveAll,
|
||||
Reverse(String, String),
|
||||
ReverseRemoveAll,
|
||||
Reconnect,
|
||||
TcpIp(u16),
|
||||
Usb,
|
||||
}
|
||||
|
||||
impl Display for AdbServerCommand {
|
||||
@@ -56,12 +65,23 @@ impl Display for AdbServerCommand {
|
||||
write!(f, "host:pair:{code}:{addr}")
|
||||
}
|
||||
AdbServerCommand::FrameBuffer => write!(f, "framebuffer:"),
|
||||
AdbServerCommand::Forward(serial, remote, local) => {
|
||||
write!(f, "host-serial:{serial}:forward:{local};{remote}")
|
||||
AdbServerCommand::Forward(remote, local) => {
|
||||
write!(f, "host:forward:{local};{remote}")
|
||||
}
|
||||
AdbServerCommand::ForwardRemoveAll => write!(f, "host:killforward-all"),
|
||||
AdbServerCommand::Reverse(remote, local) => {
|
||||
write!(f, "reverse:forward:{remote};{local}")
|
||||
}
|
||||
AdbServerCommand::ReverseRemoveAll => write!(f, "reverse:killforward-all"),
|
||||
AdbServerCommand::MDNSCheck => write!(f, "host:mdns:check"),
|
||||
AdbServerCommand::MDNSServices => write!(f, "host:mdns:services"),
|
||||
AdbServerCommand::ServerStatus => write!(f, "host:server-status"),
|
||||
AdbServerCommand::Reconnect => write!(f, "reconnect"),
|
||||
AdbServerCommand::ReconnectOffline => write!(f, "host:reconnect-offline"),
|
||||
AdbServerCommand::TcpIp(port) => {
|
||||
write!(f, "tcpip:{port}")
|
||||
}
|
||||
AdbServerCommand::Usb => write!(f, "usb:"),
|
||||
AdbServerCommand::Install(size) => write!(f, "exec:cmd package 'install' -S {size}"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,22 @@ pub enum DeviceState {
|
||||
Authorizing,
|
||||
/// The device is unauthorized.
|
||||
Unauthorized,
|
||||
/// Haven't received a response from the device yet.
|
||||
Connecting,
|
||||
/// Insufficient permissions to communicate with the device.
|
||||
NoPerm,
|
||||
/// USB device detached from the adb server (known but not opened/claimed).
|
||||
Detached,
|
||||
/// Device running fastboot OS (fastboot) or userspace fastboot (fastbootd).
|
||||
Bootloader,
|
||||
/// What a device sees from its end of a Transport (adb host).
|
||||
Host,
|
||||
/// Device with bootloader loaded but no ROM OS loaded (adbd).
|
||||
Recovery,
|
||||
/// Device running Android OS Sideload mode (minadbd sideload mode).
|
||||
Sideload,
|
||||
/// Device running Android OS Rescue mode (minadbd rescue mode).
|
||||
Rescue,
|
||||
}
|
||||
|
||||
impl Display for DeviceState {
|
||||
@@ -25,6 +41,14 @@ impl Display for DeviceState {
|
||||
DeviceState::NoDevice => write!(f, "no device"),
|
||||
DeviceState::Authorizing => write!(f, "authorizing"),
|
||||
DeviceState::Unauthorized => write!(f, "unauthorized"),
|
||||
DeviceState::Connecting => write!(f, "connecting"),
|
||||
DeviceState::NoPerm => write!(f, "noperm"),
|
||||
DeviceState::Detached => write!(f, "detached"),
|
||||
DeviceState::Bootloader => write!(f, "bootloader"),
|
||||
DeviceState::Host => write!(f, "host"),
|
||||
DeviceState::Recovery => write!(f, "recovery"),
|
||||
DeviceState::Sideload => write!(f, "sideload"),
|
||||
DeviceState::Rescue => write!(f, "rescue"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,6 +64,14 @@ impl FromStr for DeviceState {
|
||||
"no device" => Ok(Self::NoDevice),
|
||||
"authorizing" => Ok(Self::Authorizing),
|
||||
"unauthorized" => Ok(Self::Unauthorized),
|
||||
"connecting" => Ok(Self::Connecting),
|
||||
"noperm" => Ok(Self::NoPerm),
|
||||
"detached" => Ok(Self::Detached),
|
||||
"bootloader" => Ok(Self::Bootloader),
|
||||
"host" => Ok(Self::Host),
|
||||
"recovery" => Ok(Self::Recovery),
|
||||
"sideload" => Ok(Self::Sideload),
|
||||
"rescue" => Ok(Self::Rescue),
|
||||
_ => Err(RustADBError::UnknownDeviceState(lowercased)),
|
||||
}
|
||||
}
|
||||
|
||||
64
adb_client/src/models/mdns_services.rs
Normal file
64
adb_client/src/models/mdns_services.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use regex::bytes::Regex;
|
||||
use std::net::SocketAddrV4;
|
||||
use std::sync::LazyLock;
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
use crate::RustADBError;
|
||||
|
||||
static MDNS_SERVICES_REGEX: LazyLock<Regex> = LazyLock::new(|| {
|
||||
Regex::new("^(\\S+)\t(\\S+)\t([\\d\\.]+:\\d+)\n?$").expect("Cannot build mdns services regex")
|
||||
});
|
||||
|
||||
/// Represents MDNS Services
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MDNSServices {
|
||||
/// Service name
|
||||
pub service_name: String,
|
||||
/// Reg type
|
||||
pub reg_type: String,
|
||||
/// IP addr with port
|
||||
pub socket_v4: SocketAddrV4,
|
||||
}
|
||||
|
||||
impl Display for MDNSServices {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}\t{}\t{}",
|
||||
self.service_name, self.reg_type, self.socket_v4
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for MDNSServices {
|
||||
type Error = RustADBError;
|
||||
|
||||
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
||||
let groups = MDNS_SERVICES_REGEX
|
||||
.captures(value)
|
||||
.ok_or(RustADBError::RegexParsingError)?;
|
||||
Ok(MDNSServices {
|
||||
service_name: String::from_utf8(
|
||||
groups
|
||||
.get(1)
|
||||
.ok_or(RustADBError::RegexParsingError)?
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
)?,
|
||||
reg_type: String::from_utf8(
|
||||
groups
|
||||
.get(2)
|
||||
.ok_or(RustADBError::RegexParsingError)?
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
)?,
|
||||
socket_v4: SocketAddrV4::from_str(&String::from_utf8(
|
||||
groups
|
||||
.get(3)
|
||||
.ok_or(RustADBError::RegexParsingError)?
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
)?)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,9 @@ mod device_long;
|
||||
mod device_short;
|
||||
mod device_state;
|
||||
mod host_features;
|
||||
mod mdns_services;
|
||||
mod reboot_type;
|
||||
mod server_status;
|
||||
mod sync_command;
|
||||
|
||||
pub(crate) use adb_emulator_command::ADBEmulatorCommand;
|
||||
@@ -19,5 +21,8 @@ pub use device_long::DeviceLong;
|
||||
pub use device_short::DeviceShort;
|
||||
pub use device_state::DeviceState;
|
||||
pub use host_features::HostFeatures;
|
||||
pub use mdns_services::MDNSServices;
|
||||
pub use reboot_type::RebootType;
|
||||
pub use server_status::MDNSBackend;
|
||||
pub use server_status::ServerStatus;
|
||||
pub use sync_command::SyncCommand;
|
||||
|
||||
158
adb_client/src/models/server_status.rs
Normal file
158
adb_client/src/models/server_status.rs
Normal file
@@ -0,0 +1,158 @@
|
||||
use quick_protobuf::{BytesReader, MessageRead};
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::RustADBError;
|
||||
|
||||
#[derive(Debug, PartialEq, Default, Eq, Clone, Copy)]
|
||||
pub enum UsbBackend {
|
||||
#[default]
|
||||
Unknown = 0,
|
||||
Native = 1,
|
||||
LibUSB = 2,
|
||||
}
|
||||
|
||||
impl From<i32> for UsbBackend {
|
||||
fn from(i: i32) -> Self {
|
||||
match i {
|
||||
0 => UsbBackend::Unknown,
|
||||
1 => UsbBackend::Native,
|
||||
2 => UsbBackend::LibUSB,
|
||||
_ => Self::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for UsbBackend {
|
||||
fn from(s: &'a str) -> Self {
|
||||
match s {
|
||||
"UNKNOWN_USB" => UsbBackend::Unknown,
|
||||
"NATIVE" => UsbBackend::Native,
|
||||
"LIBUSB" => UsbBackend::LibUSB,
|
||||
_ => Self::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for UsbBackend {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
UsbBackend::Unknown => write!(f, "UNKNOWN_USB"),
|
||||
UsbBackend::Native => write!(f, "NATIVE"),
|
||||
UsbBackend::LibUSB => write!(f, "LIBUSB"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// MDNS Backend Status
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub enum MDNSBackend {
|
||||
#[default]
|
||||
/// Unknown
|
||||
Unknown = 0,
|
||||
/// Bonjour
|
||||
Bonjour = 1,
|
||||
/// OpenScreen
|
||||
OpenScreen = 2,
|
||||
}
|
||||
|
||||
impl From<i32> for MDNSBackend {
|
||||
fn from(i: i32) -> Self {
|
||||
match i {
|
||||
0 => MDNSBackend::Unknown,
|
||||
1 => MDNSBackend::Bonjour,
|
||||
2 => MDNSBackend::OpenScreen,
|
||||
_ => Self::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for MDNSBackend {
|
||||
fn from(s: &'a str) -> Self {
|
||||
match s {
|
||||
"UNKNOWN_MDNS" => MDNSBackend::Unknown,
|
||||
"BONJOUR" => MDNSBackend::Bonjour,
|
||||
"OPENSCREEN" => MDNSBackend::OpenScreen,
|
||||
_ => Self::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for MDNSBackend {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
MDNSBackend::Unknown => write!(f, "UNKNOWN_MDNS"),
|
||||
MDNSBackend::Bonjour => write!(f, "BONJOUR"),
|
||||
MDNSBackend::OpenScreen => write!(f, "OPENSCREEN"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Structure representing current server status
|
||||
#[derive(Debug, Clone, Default, PartialEq)]
|
||||
pub struct ServerStatus {
|
||||
pub usb_backend: UsbBackend,
|
||||
pub usb_backend_forced: bool,
|
||||
pub mdns_backend: MDNSBackend,
|
||||
pub mdns_backend_forced: bool,
|
||||
pub version: String,
|
||||
pub build: String,
|
||||
pub executable_absolute_path: String,
|
||||
pub log_absolute_path: String,
|
||||
pub os: String,
|
||||
}
|
||||
|
||||
impl<'a> MessageRead<'a> for ServerStatus {
|
||||
fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> quick_protobuf::Result<Self> {
|
||||
let mut msg = Self::default();
|
||||
while !r.is_eof() {
|
||||
match r.next_tag(bytes) {
|
||||
Ok(8) => msg.usb_backend = r.read_enum(bytes)?,
|
||||
Ok(16) => msg.usb_backend_forced = r.read_bool(bytes)?,
|
||||
Ok(24) => msg.mdns_backend = r.read_enum(bytes)?,
|
||||
Ok(32) => msg.mdns_backend_forced = r.read_bool(bytes)?,
|
||||
Ok(42) => msg.version = r.read_string(bytes)?.to_string(),
|
||||
Ok(50) => msg.build = r.read_string(bytes)?.to_string(),
|
||||
Ok(58) => msg.executable_absolute_path = r.read_string(bytes)?.to_string(),
|
||||
Ok(66) => msg.log_absolute_path = r.read_string(bytes)?.to_string(),
|
||||
Ok(74) => msg.os = r.read_string(bytes)?.to_string(),
|
||||
Ok(t) => {
|
||||
r.read_unknown(bytes, t)?;
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ServerStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "usb_backend: {}", self.usb_backend)?;
|
||||
if self.usb_backend_forced {
|
||||
writeln!(f, "usb_backend_forced: {}", self.usb_backend_forced)?;
|
||||
}
|
||||
writeln!(f, "mdns_backend: {}", self.mdns_backend)?;
|
||||
if self.mdns_backend_forced {
|
||||
writeln!(f, "mdns_backend_forced: {}", self.mdns_backend_forced)?;
|
||||
}
|
||||
writeln!(f, "version: \"{}\"", self.version)?;
|
||||
writeln!(f, "build: \"{}\"", self.build)?;
|
||||
writeln!(
|
||||
f,
|
||||
"executable_absolute_path: \"{}\"",
|
||||
self.executable_absolute_path
|
||||
)?;
|
||||
writeln!(f, "log_absolute_path: \"{}\"", self.log_absolute_path)?;
|
||||
writeln!(f, "os: \"{}\"", self.os)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for ServerStatus {
|
||||
type Error = RustADBError;
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
let mut reader = BytesReader::from_bytes(&value);
|
||||
ServerStatus::from_reader(&mut reader, &value).map_err(|_| RustADBError::ConversionError)
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ use crate::ADBTransport;
|
||||
use crate::Result;
|
||||
use crate::RustADBError;
|
||||
use crate::TCPServerTransport;
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddrV4;
|
||||
use std::process::Command;
|
||||
|
||||
@@ -12,6 +13,8 @@ pub struct ADBServer {
|
||||
pub(crate) transport: Option<TCPServerTransport>,
|
||||
/// Address to connect to
|
||||
pub(crate) socket_addr: Option<SocketAddrV4>,
|
||||
/// adb-server start envs
|
||||
pub(crate) envs: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl ADBServer {
|
||||
@@ -20,6 +23,7 @@ impl ADBServer {
|
||||
Self {
|
||||
transport: None,
|
||||
socket_addr: Some(address),
|
||||
envs: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +53,13 @@ impl ADBServer {
|
||||
|
||||
if is_local_ip {
|
||||
// ADB Server is local, we start it if not already running
|
||||
let child = Command::new("adb").arg("start-server").spawn();
|
||||
let mut command = Command::new("adb");
|
||||
command.arg("start-server");
|
||||
for (env_k, env_v) in self.envs.iter() {
|
||||
command.env(env_k, env_v);
|
||||
}
|
||||
|
||||
let child = command.spawn();
|
||||
match child {
|
||||
Ok(mut child) => {
|
||||
if let Err(e) = child.wait() {
|
||||
|
||||
62
adb_client/src/server/commands/mdns.rs
Normal file
62
adb_client/src/server/commands/mdns.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use std::io::BufRead;
|
||||
|
||||
use crate::{
|
||||
models::{AdbServerCommand, MDNSBackend, MDNSServices},
|
||||
ADBServer, Result,
|
||||
};
|
||||
|
||||
const OPENSCREEN_MDNS_BACKEND: &str = "ADB_MDNS_OPENSCREEN";
|
||||
|
||||
impl ADBServer {
|
||||
/// Check if mdns discovery is available
|
||||
pub fn mdns_check(&mut self) -> Result<bool> {
|
||||
let response = self
|
||||
.connect()?
|
||||
.proxy_connection(AdbServerCommand::MDNSCheck, true)?;
|
||||
|
||||
match String::from_utf8(response) {
|
||||
Ok(s) if s.starts_with("mdns daemon version") => Ok(true),
|
||||
Ok(_) => Ok(false),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// List all discovered mdns services
|
||||
pub fn mdns_services(&mut self) -> Result<Vec<MDNSServices>> {
|
||||
let services = self
|
||||
.connect()?
|
||||
.proxy_connection(AdbServerCommand::MDNSServices, true)?;
|
||||
|
||||
let mut vec_services: Vec<MDNSServices> = vec![];
|
||||
for service in services.lines() {
|
||||
match service {
|
||||
Ok(service) => {
|
||||
vec_services.push(MDNSServices::try_from(service.as_bytes())?);
|
||||
}
|
||||
Err(e) => log::error!("{}", e),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(vec_services)
|
||||
}
|
||||
|
||||
/// Check if specified backend mdns service is used, otherwise restart adb server with envs
|
||||
pub fn mdns_force_backend(&mut self, backend: MDNSBackend) -> Result<()> {
|
||||
let server_status = self.server_status()?;
|
||||
if server_status.mdns_backend != backend {
|
||||
self.kill()?;
|
||||
self.envs.insert(
|
||||
OPENSCREEN_MDNS_BACKEND.to_string(),
|
||||
(if backend == MDNSBackend::OpenScreen {
|
||||
"1"
|
||||
} else {
|
||||
"0"
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
self.connect()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -2,5 +2,8 @@ mod connect;
|
||||
mod devices;
|
||||
mod disconnect;
|
||||
mod kill;
|
||||
mod mdns;
|
||||
mod pair;
|
||||
mod reconnect;
|
||||
mod server_status;
|
||||
mod version;
|
||||
|
||||
10
adb_client/src/server/commands/reconnect.rs
Normal file
10
adb_client/src/server/commands/reconnect.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
use crate::{models::AdbServerCommand, ADBServer, Result};
|
||||
|
||||
impl ADBServer {
|
||||
/// Reconnect the device
|
||||
pub fn reconnect_offline(&mut self) -> Result<()> {
|
||||
self.connect()?
|
||||
.proxy_connection(AdbServerCommand::ReconnectOffline, false)
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
15
adb_client/src/server/commands/server_status.rs
Normal file
15
adb_client/src/server/commands/server_status.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use crate::{
|
||||
models::{AdbServerCommand, ServerStatus},
|
||||
ADBServer, Result,
|
||||
};
|
||||
|
||||
impl ADBServer {
|
||||
/// Check ADB server status
|
||||
pub fn server_status(&mut self) -> Result<ServerStatus> {
|
||||
let status = self
|
||||
.connect()?
|
||||
.proxy_connection(AdbServerCommand::ServerStatus, true)?;
|
||||
|
||||
ServerStatus::try_from(status)
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,18 @@ impl ADBServerDevice {
|
||||
.send_adb_request(AdbServerCommand::TransportSerial(serial.clone()))?;
|
||||
|
||||
self.get_transport_mut()
|
||||
.proxy_connection(AdbServerCommand::Forward(serial, remote, local), false)
|
||||
.proxy_connection(AdbServerCommand::Forward(remote, local), false)
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
/// Remove all previously applied forward rules
|
||||
pub fn forward_remove_all(&mut self) -> Result<()> {
|
||||
let serial = self.identifier.clone();
|
||||
self.connect()?
|
||||
.send_adb_request(AdbServerCommand::TransportSerial(serial.clone()))?;
|
||||
|
||||
self.get_transport_mut()
|
||||
.proxy_connection(AdbServerCommand::ForwardRemoveAll, false)
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,11 @@ mod install;
|
||||
mod list;
|
||||
mod logcat;
|
||||
mod reboot;
|
||||
mod reconnect;
|
||||
mod recv;
|
||||
mod reverse;
|
||||
mod send;
|
||||
mod stat;
|
||||
mod tcpip;
|
||||
mod transport;
|
||||
mod usb;
|
||||
|
||||
14
adb_client/src/server_device/commands/reconnect.rs
Normal file
14
adb_client/src/server_device/commands/reconnect.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use crate::{models::AdbServerCommand, ADBServerDevice, Result};
|
||||
|
||||
impl ADBServerDevice {
|
||||
/// Reconnect device
|
||||
pub fn reconnect(&mut self) -> Result<()> {
|
||||
let serial = self.identifier.clone();
|
||||
self.connect()?
|
||||
.send_adb_request(AdbServerCommand::TransportSerial(serial))?;
|
||||
|
||||
self.get_transport_mut()
|
||||
.proxy_connection(AdbServerCommand::Reconnect, false)
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
@@ -11,4 +11,15 @@ impl ADBServerDevice {
|
||||
.proxy_connection(AdbServerCommand::Reverse(remote, local), false)
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
/// Remove all reverse rules
|
||||
pub fn reverse_remove_all(&mut self) -> Result<()> {
|
||||
let serial = self.identifier.clone();
|
||||
self.connect()?
|
||||
.send_adb_request(AdbServerCommand::TransportSerial(serial.clone()))?;
|
||||
|
||||
self.get_transport_mut()
|
||||
.proxy_connection(AdbServerCommand::ReverseRemoveAll, false)
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
14
adb_client/src/server_device/commands/tcpip.rs
Normal file
14
adb_client/src/server_device/commands/tcpip.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use crate::{models::AdbServerCommand, ADBServerDevice, Result};
|
||||
|
||||
impl ADBServerDevice {
|
||||
/// Set adb daemon to tcp/ip mode
|
||||
pub fn tcpip(&mut self, port: u16) -> Result<()> {
|
||||
let serial = self.identifier.clone();
|
||||
self.connect()?
|
||||
.send_adb_request(AdbServerCommand::TransportSerial(serial))?;
|
||||
|
||||
self.get_transport_mut()
|
||||
.proxy_connection(AdbServerCommand::TcpIp(port), false)
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
14
adb_client/src/server_device/commands/usb.rs
Normal file
14
adb_client/src/server_device/commands/usb.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use crate::{models::AdbServerCommand, ADBServerDevice, Result};
|
||||
|
||||
impl ADBServerDevice {
|
||||
/// Set adb daemon to usb mode
|
||||
pub fn usb(&mut self) -> Result<()> {
|
||||
let serial = self.identifier.clone();
|
||||
self.connect()?
|
||||
.send_adb_request(AdbServerCommand::TransportSerial(serial))?;
|
||||
|
||||
self.get_transport_mut()
|
||||
.proxy_connection(AdbServerCommand::Usb, false)
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user