Skip to main content

droidrun_adb/
server.rs

1/// ADB server — discovery, device enumeration, and server lifecycle management.
2use tracing::debug;
3
4use crate::connection::AdbConnection;
5use crate::device::AdbDevice;
6use crate::error::{AdbError, Result};
7use crate::models::{DeviceEvent, DeviceInfo, DeviceState, ForwardEntry};
8
9/// ADB server connection for device discovery and management.
10#[derive(Debug, Clone)]
11pub struct AdbServer {
12    host: String,
13    port: u16,
14}
15
16impl Default for AdbServer {
17    fn default() -> Self {
18        Self {
19            host: "127.0.0.1".into(),
20            port: 5037,
21        }
22    }
23}
24
25impl AdbServer {
26    /// Create a server connection with custom address.
27    pub fn new(host: impl Into<String>, port: u16) -> Self {
28        Self {
29            host: host.into(),
30            port,
31        }
32    }
33
34    // ── Device discovery ──────────────────────────────────────────
35
36    /// List all connected devices.
37    pub async fn devices(&self) -> Result<Vec<DeviceInfo>> {
38        let mut conn = AdbConnection::connect(&self.host, self.port).await?;
39        conn.send_and_okay("host:devices").await?;
40        let data = conn.read_length_prefixed_string().await?;
41
42        let devices: Vec<DeviceInfo> = data
43            .lines()
44            .filter(|l| !l.is_empty())
45            .filter_map(|line| {
46                let mut parts = line.split_whitespace();
47                let serial = parts.next()?.to_string();
48                let state_str = parts.next().unwrap_or("unknown");
49                Some(DeviceInfo {
50                    serial,
51                    state: DeviceState::from(state_str),
52                })
53            })
54            .collect();
55
56        debug!("found {} device(s)", devices.len());
57        Ok(devices)
58    }
59
60    /// Get a handle to the first connected device.
61    pub async fn device(&self) -> Result<AdbDevice> {
62        let devices = self.devices().await?;
63        let info = devices
64            .into_iter()
65            .find(|d| d.state.is_online())
66            .ok_or(AdbError::NoDevice)?;
67        Ok(AdbDevice::new(info.serial, &self.host, self.port))
68    }
69
70    /// Get a handle to a specific device by serial.
71    pub async fn device_by_serial(&self, serial: &str) -> Result<AdbDevice> {
72        let devices = self.devices().await?;
73        let found = devices.iter().any(|d| d.serial == serial);
74        if found {
75            Ok(AdbDevice::new(serial, &self.host, self.port))
76        } else {
77            Err(AdbError::DeviceNotFound(serial.to_string()))
78        }
79    }
80
81    /// Get a device — by serial if provided, otherwise the first available.
82    pub async fn resolve_device(&self, serial: Option<&str>) -> Result<AdbDevice> {
83        match serial {
84            Some(s) => self.device_by_serial(s).await,
85            None => self.device().await,
86        }
87    }
88
89    // ── Server lifecycle ──────────────────────────────────────────
90
91    /// Get the ADB server version.
92    pub async fn version(&self) -> Result<u32> {
93        let mut conn = AdbConnection::connect(&self.host, self.port).await?;
94        conn.send_and_okay("host:version").await?;
95        let version_str = conn.read_length_prefixed_string().await?;
96        let version = u32::from_str_radix(version_str.trim(), 16)
97            .map_err(|_| AdbError::Parse(format!("cannot parse version: {version_str}")))?;
98        Ok(version)
99    }
100
101    /// Kill the ADB server.
102    pub async fn server_kill(&self) -> Result<()> {
103        let mut conn = AdbConnection::connect(&self.host, self.port).await?;
104        conn.send_and_okay("host:kill").await?;
105        debug!("ADB server killed");
106        Ok(())
107    }
108
109    // ── Remote device management ─────────────────────────────────
110
111    /// Connect to a remote device via TCP/IP (adb connect host:port).
112    pub async fn connect_device(&self, addr: &str) -> Result<String> {
113        let mut conn = AdbConnection::connect(&self.host, self.port).await?;
114        conn.send_and_okay(&format!("host:connect:{addr}")).await?;
115        let response = conn.read_length_prefixed_string().await?;
116        debug!("connect {addr}: {response}");
117        Ok(response)
118    }
119
120    /// Disconnect from a remote device (adb disconnect host:port).
121    pub async fn disconnect_device(&self, addr: &str) -> Result<String> {
122        let mut conn = AdbConnection::connect(&self.host, self.port).await?;
123        conn.send_and_okay(&format!("host:disconnect:{addr}")).await?;
124        let response = conn.read_length_prefixed_string().await?;
125        debug!("disconnect {addr}: {response}");
126        Ok(response)
127    }
128
129    // ── Wait & Track ──────────────────────────────────────────────
130
131    /// Block until a device reaches the specified state, or time out.
132    ///
133    /// `state` can be "device", "recovery", "bootloader", etc.
134    pub async fn wait_for(
135        &self,
136        serial: Option<&str>,
137        state: &str,
138        timeout: std::time::Duration,
139    ) -> Result<()> {
140        let cmd = match serial {
141            Some(s) => format!("host-serial:{s}:wait-for-any-{state}"),
142            None => format!("host:wait-for-any-{state}"),
143        };
144        let host = self.host.clone();
145        let port = self.port;
146
147        let fut = async move {
148            let mut conn = AdbConnection::connect(&host, port).await?;
149            conn.send_and_okay(&cmd).await?;
150            // The server blocks this connection until the state is reached
151            Ok::<(), AdbError>(())
152        };
153
154        tokio::time::timeout(timeout, fut)
155            .await
156            .map_err(|_| AdbError::Timeout(format!("wait_for {state} timed out")))?
157    }
158
159    /// Track device connect/disconnect events as an async stream.
160    ///
161    /// Returns an mpsc receiver that yields device state updates. The
162    /// background task runs until the receiver is dropped.
163    pub async fn track_devices(
164        &self,
165    ) -> Result<tokio::sync::mpsc::Receiver<Vec<DeviceEvent>>> {
166        let mut conn = AdbConnection::connect(&self.host, self.port).await?;
167        conn.send_and_okay("host:track-devices").await?;
168
169        let (tx, rx) = tokio::sync::mpsc::channel(16);
170
171        tokio::spawn(async move {
172            loop {
173                match conn.read_length_prefixed_string().await {
174                    Ok(data) => {
175                        let events: Vec<DeviceEvent> = data
176                            .lines()
177                            .filter(|l| !l.is_empty())
178                            .filter_map(|line| {
179                                let mut parts = line.split_whitespace();
180                                let serial = parts.next()?.to_string();
181                                let state =
182                                    DeviceState::from(parts.next().unwrap_or("unknown"));
183                                Some(DeviceEvent { serial, state })
184                            })
185                            .collect();
186                        if tx.send(events).await.is_err() {
187                            break;
188                        }
189                    }
190                    Err(_) => break,
191                }
192            }
193        });
194
195        debug!("tracking device events");
196        Ok(rx)
197    }
198
199    // ── Server-level forward list ────────────────────────────────
200
201    /// List port forwards for ALL devices (server-level).
202    ///
203    /// Unlike `AdbDevice::forward_list()` which filters by serial,
204    /// this returns forwards across all connected devices.
205    pub async fn forward_list_all(&self) -> Result<Vec<ForwardEntry>> {
206        let mut conn = AdbConnection::connect(&self.host, self.port).await?;
207        conn.send_and_okay("host:list-forward").await?;
208        let data = conn.read_length_prefixed_string().await?;
209
210        let entries: Vec<ForwardEntry> = data
211            .lines()
212            .filter(|l| !l.is_empty())
213            .filter_map(|line| {
214                let parts: Vec<&str> = line.split_whitespace().collect();
215                if parts.len() >= 3 {
216                    Some(ForwardEntry {
217                        serial: parts[0].to_string(),
218                        local: parts[1].to_string(),
219                        remote: parts[2].to_string(),
220                    })
221                } else {
222                    None
223                }
224            })
225            .collect();
226
227        Ok(entries)
228    }
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234
235    #[test]
236    fn test_default_server() {
237        let server = AdbServer::default();
238        assert_eq!(server.host, "127.0.0.1");
239        assert_eq!(server.port, 5037);
240    }
241
242    #[test]
243    fn test_parse_devices_output() {
244        let data = "emulator-5554\tdevice\n192.168.1.5:5555\toffline\n";
245        let devices: Vec<DeviceInfo> = data
246            .lines()
247            .filter(|l| !l.is_empty())
248            .filter_map(|line| {
249                let mut parts = line.split_whitespace();
250                let serial = parts.next()?.to_string();
251                let state_str = parts.next().unwrap_or("unknown");
252                Some(DeviceInfo {
253                    serial,
254                    state: DeviceState::from(state_str),
255                })
256            })
257            .collect();
258
259        assert_eq!(devices.len(), 2);
260        assert_eq!(devices[0].serial, "emulator-5554");
261        assert!(devices[0].state.is_online());
262        assert_eq!(devices[1].serial, "192.168.1.5:5555");
263        assert!(!devices[1].state.is_online());
264    }
265
266    #[test]
267    fn test_parse_track_devices_output() {
268        let data = "emulator-5554\tdevice\n192.168.1.5:5555\toffline\n";
269        let events: Vec<DeviceEvent> = data
270            .lines()
271            .filter(|l| !l.is_empty())
272            .filter_map(|line| {
273                let mut parts = line.split_whitespace();
274                let serial = parts.next()?.to_string();
275                let state = DeviceState::from(parts.next().unwrap_or("unknown"));
276                Some(DeviceEvent { serial, state })
277            })
278            .collect();
279
280        assert_eq!(events.len(), 2);
281        assert_eq!(events[0].serial, "emulator-5554");
282        assert!(events[0].state.is_online());
283        assert!(!events[1].state.is_online());
284    }
285}