adb_client_tokio/
client.rs

1use crate::connection::{
2    AdbClientConnection, AdbClientStream, AdbRequest, AdbRequestEncoder, AdbResponseDecoder,
3    AdbResponseDecoderImpl,
4};
5use crate::shell::{AdbShellProtocol, AdbShellResponseId};
6use crate::util::{AdbError, Result};
7use std::fmt::Display;
8use std::path::Path;
9use tokio::net::{TcpStream, UnixStream};
10use tokio_stream::StreamExt;
11use tokio_util::codec::{FramedRead, FramedWrite};
12
13/// Specifiying all possibilities to connect to a device.
14#[derive(Debug, Clone)]
15pub enum Device {
16    /// use the only connected device (error if multiple devices connected)
17    Default,
18    /// use USB device (error if multiple devices connected)
19    Usb,
20    /// use TCP/IP device (error if multiple TCP/IP devices available)
21    TcpIp,
22    /// use device with given serial
23    Serial(String),
24}
25
26impl Display for Device {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        match self {
29            Device::Default => write!(f, "host:transport-any"),
30            Device::Usb => write!(f, "host:transport-usb"),
31            Device::TcpIp => write!(f, "host:transport-local"),
32            Device::Serial(serial) => write!(f, "host:transport:{}", serial),
33        }
34    }
35}
36
37/// allow external structs to convert to a device.
38pub trait ToDevice {
39    /// convert to a device.
40    fn to_device(&self) -> Device;
41}
42
43impl Into<AdbRequest> for Device {
44    fn into(self) -> AdbRequest {
45        AdbRequest::new(&self.to_string())
46    }
47}
48
49impl ToDevice for Device {
50    fn to_device(&self) -> Device {
51        self.clone()
52    }
53}
54
55impl ToDevice for &str {
56    fn to_device(&self) -> Device {
57        Device::Serial(self.to_string())
58    }
59}
60
61impl ToDevice for &String {
62    fn to_device(&self) -> Device {
63        Device::Serial(self.to_string())
64    }
65}
66
67/// AdbClientSocketUrl represents a URL that can be used to connect to an ADB server.
68#[derive(Debug, Clone)]
69pub enum AdbClientSocketUrl {
70    /// A UNIX socket
71    UNIX(String),
72    /// A TCP socket
73    TCP(String),
74}
75
76impl AdbClientSocketUrl {
77    /// Connect to the ADB server using the URL.
78    pub async fn connect(self) -> Result<AdbClient> {
79        let socket = match self {
80            AdbClientSocketUrl::UNIX(path) => AdbClientStream::Unix(UnixStream::connect(path).await?),
81            AdbClientSocketUrl::TCP(address) => AdbClientStream::Tcp(TcpStream::connect(address).await?),
82        };
83
84        Ok(AdbClient::new(socket))
85    }
86}
87
88/// ADB client that can connect to ADB server and execute commands.
89///
90/// Example:
91/// ```no_run
92/// use adb_client_tokio::AdbClient;
93///
94/// #[tokio::main]
95/// async fn main() {
96///     let mut adb = AdbClient::connect_tcp("127.0.0.1:5037").await.unwrap();
97///     let version = adb.get_host_version().await.expect("Failed to get host version");
98///     println!("ADB server version: {}", version);
99/// }
100/// ```
101#[derive(Debug)]
102pub struct AdbClient {
103    connection: AdbClientConnection,
104}
105
106impl AdbClient {
107    /// Connect to the ADB server using a socket.
108    pub fn new(stream: AdbClientStream) -> Self {
109        Self {
110            connection: AdbClientConnection::new(stream),
111        }
112    }
113
114    /// Get the version of the ADB server.
115    pub async fn get_host_version(&mut self) -> Result<String> {
116        self.connection
117            .send(AdbRequest::new("host:version"))
118            .await?;
119
120        self.connection.next().await
121    }
122
123    /// Get the list of devices connected to the ADB server.
124    pub async fn get_device_list(&mut self) -> Result<Vec<DeviceListItem>> {
125        self.connection
126            .send(AdbRequest::new("host:devices"))
127            .await?;
128        let devices = self.connection.next().await?;
129
130        let devices = devices
131            .trim()
132            .split('\n')
133            .filter_map(|line| {
134                let mut split = line.split("\t");
135                let id = match split.next() {
136                    Some(id) => id,
137                    None => return None,
138                };
139                let device_type = match split.next() {
140                    Some(id) => id,
141                    None => return None,
142                };
143
144                Some(DeviceListItem {
145                    id: id.into(),
146                    device_type: device_type.into(),
147                })
148            })
149            .collect();
150
151        Ok(devices)
152    }
153
154    /// Restart the adb server on the device in tcp mode on the given port.
155    pub async fn tcpip(mut self, device: impl ToDevice, port: u16) -> Result<String> {
156        self.connection.send(device.to_device().into()).await?;
157        self.connection.reader.decoder_mut().decoder_impl = AdbResponseDecoderImpl::Status;
158        self.connection.next().await?;
159
160        let request = AdbRequest::new(&format!("tcpip:{}", port));
161        self.connection.send(request).await?;
162        self.connection.reader.decoder_mut().decoder_impl =
163            AdbResponseDecoderImpl::StatusPayloadNewline;
164        let message = self.connection.next().await?;
165        Ok(message)
166    }
167
168    /// Connect to a tcp port on the give device.
169    pub async fn connect_to_device(
170        mut self,
171        device: impl ToDevice,
172        remote: Remote,
173    ) -> Result<AdbClientStream> {
174        self.connection.send(device.to_device().into()).await?;
175        self.connection.reader.decoder_mut().decoder_impl = AdbResponseDecoderImpl::Status;
176        self.connection.next().await?;
177
178        let request = AdbRequest::new(remote.to_string().as_str());
179        self.connection.send(request).await?;
180        self.connection.reader.decoder_mut().decoder_impl = AdbResponseDecoderImpl::Status;
181        self.connection.next().await?;
182
183        let reader = self.connection.reader.into_inner();
184        let writer = self.connection.writer.into_inner();
185
186        Ok(reader.reunite(writer)?)
187    }
188
189    /// Connect a device to the ADB server.
190    pub async fn connect_device_to_server(&mut self, connection_string: &str) -> Result<()> {
191        let request = AdbRequest::new(&format!("host:connect:{}", connection_string));
192        self.connection.send(request).await?;
193
194        let response = self.connection.next().await?;
195        if response.starts_with("already connected to") || response.starts_with("connected to") {
196            Ok(())
197        } else {
198            Err(AdbError::FailedResponseStatus(response))
199        }
200    }
201
202    /// forward tcp connections from local to remote.
203    pub async fn forward_server(
204        &mut self,
205        device: impl ToDevice,
206        local: &str,
207        remote: &str,
208    ) -> Result<()> {
209        self.connection.send(device.to_device().into()).await?;
210        self.connection.next().await?;
211
212        let request = AdbRequest::new(&format!("forward:tcp:{};{}", local, remote));
213        self.connection.send(request).await?;
214        self.connection.next().await?;
215
216        return Ok(());
217    }
218
219    /// run a shell command on the device.
220    pub async fn shell(mut self, device: impl ToDevice, command: &str) -> Result<String> {
221        self.connection.send(device.to_device().into()).await?;
222        self.connection.reader.decoder_mut().decoder_impl = AdbResponseDecoderImpl::Status;
223        self.connection.next().await?;
224
225        let request = AdbRequest::new(&format!("shell,v2,TERM=xterm-256color,raw:{}", command));
226        self.connection.send(request).await?;
227        self.connection.reader.decoder_mut().decoder_impl = AdbResponseDecoderImpl::Status;
228        self.connection.next().await?;
229
230        let reader = self.connection.reader.into_inner();
231        let mut reader = FramedRead::new(reader, AdbShellProtocol::new());
232
233        let mut response = Vec::<u8>::new();
234        loop {
235            let packet = match reader.next().await {
236                Some(Ok(package)) => package,
237                Some(Err(e)) => return Err(e.into()),
238                None => return Err(AdbError::FailedResponseStatus("No response".into())),
239            };
240
241            match packet.id {
242                AdbShellResponseId::Stderr => {
243                    print!("{}", std::str::from_utf8(&packet.payload)?);
244                    response.extend_from_slice(&packet.payload);
245                }
246                AdbShellResponseId::Stdout => {
247                    print!("{}", std::str::from_utf8(&packet.payload)?);
248                    response.extend_from_slice(&packet.payload);
249                }
250                AdbShellResponseId::Exit => {
251                    break;
252                }
253                _ => {}
254            }
255        }
256
257        let real_response = std::str::from_utf8(&response)?;
258        Ok(real_response.into())
259    }
260}
261
262/// represents the options to connect
263#[derive(Debug)]
264pub enum Remote {
265    /// TCP localhost:<port> on device
266    Tcp(u16),
267    /// Unix local domain socket on device
268    Unix(String),
269    /// Unix abstract local domain socket on device
270    LocalAbstract(String),
271    /// JDWP thread on VM process <pid>   
272    Jwp(u16),
273}
274
275impl Display for Remote {
276    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
277        match self {
278            Remote::Tcp(port) => write!(f, "tcp:{}", port),
279            Remote::LocalAbstract(name) => write!(f, "localabstract:{}", name),
280            Remote::Unix(path) => write!(f, "local:{}", path),
281            Remote::Jwp(pid) => write!(f, "jdwp:{}", pid),
282        }
283    }
284}
285
286/// ADB device list item.
287#[derive(Debug, Clone)]
288pub struct DeviceListItem {
289    /// Device serial number.
290    pub id: String,
291    /// Device type.
292    pub device_type: String,
293}
294
295#[cfg(test)]
296mod tests {
297    use super::*;
298
299    #[tokio::test]
300    async fn test_failure() -> Result<()> {
301        let mut adb_client = AdbClientSocketUrl::TCP("127.0.0.1:5037".to_owned()).connect().await?;
302        adb_client
303            .connection
304            .send(AdbRequest::new("host:abcd"))
305            .await?;
306        adb_client.connection.reader.decoder_mut().decoder_impl = AdbResponseDecoderImpl::Status;
307        let response = adb_client.connection.next().await;
308
309        assert_eq!(
310            "FAILED response status: unknown host service".to_string(),
311            response.err().unwrap().to_string()
312        );
313
314        Ok(())
315    }
316
317    #[tokio::test]
318    async fn test_get_host_version_command() -> Result<()> {
319        let mut adb_client = AdbClientSocketUrl::TCP("127.0.0.1:5037".to_owned()).connect().await?;
320
321        let version: String = adb_client.get_host_version().await?;
322
323        assert_eq!(version, "0029");
324        Ok(())
325    }
326
327    #[tokio::test]
328    async fn test_get_devices_command() -> Result<()> {
329        let mut adb_client = AdbClientSocketUrl::TCP("127.0.0.1:5037".to_owned()).connect().await?;
330
331        let devices = adb_client.get_device_list().await?;
332
333        assert_eq!(devices.len(), 1);
334        Ok(())
335    }
336
337    #[ignore]
338    #[tokio::test]
339    async fn test_tcpip_command() -> Result<()> {
340        let mut adb_client = AdbClientSocketUrl::TCP("127.0.0.1:5037".to_owned()).connect().await?;
341        let response: String = adb_client.tcpip("08261JECB10524", 5555).await?;
342
343        assert_eq!(response, "restarting in TCP mode port: 5555");
344        Ok(())
345    }
346
347    #[tokio::test]
348    async fn test_get_prop_command() -> Result<()> {
349        let mut adb_client = AdbClientSocketUrl::TCP("127.0.0.1:5037".to_owned()).connect().await?;
350        let manufaturer: String = adb_client
351            .shell("08261JECB10524", "getprop ro.product.manufacturer")
352            .await?;
353
354        let mut adb_client = AdbClientSocketUrl::TCP("127.0.0.1:5037".to_owned()).connect().await?;
355        let model: String = adb_client
356            .shell("08261JECB10524", "getprop ro.product.model")
357            .await?;
358
359        println!("manufaturer: {:?}", &manufaturer);
360        println!("model: {:?}", &model);
361
362        Ok(())
363    }
364}