1 #[macro_use]
2 extern crate log;
3 extern crate regex;
4 extern crate tempfile;
5 extern crate walkdir;
6 
7 pub mod adb;
8 pub mod shell;
9 
10 #[cfg(test)]
11 pub mod test;
12 
13 use std::collections::BTreeMap;
14 use std::convert::TryFrom;
15 use std::fmt;
16 use std::fs::File;
17 use std::io::{self, Read, Write};
18 use std::iter::FromIterator;
19 use std::net::TcpStream;
20 use std::num::{ParseIntError, TryFromIntError};
21 use std::path::Path;
22 use std::str::Utf8Error;
23 use std::time::{Duration, SystemTime};
24 use walkdir::WalkDir;
25 
26 use crate::adb::{DeviceSerial, SyncCommand};
27 
28 pub type Result<T> = std::result::Result<T, DeviceError>;
29 
30 #[derive(Debug)]
31 pub enum DeviceError {
32     Adb(String),
33     Io(io::Error),
34     FromInt(TryFromIntError),
35     MultipleDevices,
36     ParseInt(ParseIntError),
37     UnknownDevice(String),
38     Utf8(Utf8Error),
39     WalkDir(walkdir::Error),
40 }
41 
42 impl fmt::Display for DeviceError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result43     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44         match *self {
45             DeviceError::Adb(ref message) => message.fmt(f),
46             DeviceError::MultipleDevices => write!(f, "Multiple Android devices online"),
47             DeviceError::UnknownDevice(ref serial) => {
48                 write!(f, "Unknown Android device with serial '{}'", serial)
49             }
50             _ => self.to_string().fmt(f),
51         }
52     }
53 }
54 
55 impl From<io::Error> for DeviceError {
from(value: io::Error) -> DeviceError56     fn from(value: io::Error) -> DeviceError {
57         DeviceError::Io(value)
58     }
59 }
60 
61 impl From<ParseIntError> for DeviceError {
from(value: ParseIntError) -> DeviceError62     fn from(value: ParseIntError) -> DeviceError {
63         DeviceError::ParseInt(value)
64     }
65 }
66 
67 impl From<TryFromIntError> for DeviceError {
from(value: TryFromIntError) -> DeviceError68     fn from(value: TryFromIntError) -> DeviceError {
69         DeviceError::FromInt(value)
70     }
71 }
72 
73 impl From<Utf8Error> for DeviceError {
from(value: Utf8Error) -> DeviceError74     fn from(value: Utf8Error) -> DeviceError {
75         DeviceError::Utf8(value)
76     }
77 }
78 
79 impl From<walkdir::Error> for DeviceError {
from(value: walkdir::Error) -> DeviceError80     fn from(value: walkdir::Error) -> DeviceError {
81         DeviceError::WalkDir(value)
82     }
83 }
84 
encode_message(payload: &str) -> Result<String>85 fn encode_message(payload: &str) -> Result<String> {
86     let hex_length = u16::try_from(payload.len()).map(|len| format!("{:0>4X}", len))?;
87 
88     Ok(format!("{}{}", hex_length, payload).to_owned())
89 }
90 
parse_device_info(line: &str) -> Option<DeviceInfo>91 fn parse_device_info(line: &str) -> Option<DeviceInfo> {
92     // Turn "serial\tdevice key1:value1 key2:value2 ..." into a `DeviceInfo`.
93     let mut pairs = line.split_whitespace();
94     let serial = pairs.next();
95     let state = pairs.next();
96     if let (Some(serial), Some("device")) = (serial, state) {
97         let info: BTreeMap<String, String> = pairs
98             .filter_map(|pair| {
99                 let mut kv = pair.split(':');
100                 if let (Some(k), Some(v), None) = (kv.next(), kv.next(), kv.next()) {
101                     Some((k.to_owned(), v.to_owned()))
102                 } else {
103                     None
104                 }
105             })
106             .collect();
107 
108         Some(DeviceInfo {
109             serial: serial.to_owned(),
110             info,
111         })
112     } else {
113         None
114     }
115 }
116 
117 /// Reads the payload length of a host message from the stream.
read_length<R: Read>(stream: &mut R) -> Result<usize>118 fn read_length<R: Read>(stream: &mut R) -> Result<usize> {
119     let mut bytes: [u8; 4] = [0; 4];
120     stream.read_exact(&mut bytes)?;
121 
122     let response = std::str::from_utf8(&bytes)?;
123 
124     Ok(usize::from_str_radix(&response, 16)?)
125 }
126 
127 /// Reads the payload length of a device message from the stream.
read_length_little_endian(reader: &mut dyn Read) -> Result<usize>128 fn read_length_little_endian(reader: &mut dyn Read) -> Result<usize> {
129     let mut bytes: [u8; 4] = [0; 4];
130     reader.read_exact(&mut bytes)?;
131 
132     let n: usize = (bytes[0] as usize)
133         + ((bytes[1] as usize) << 8)
134         + ((bytes[2] as usize) << 16)
135         + ((bytes[3] as usize) << 24);
136 
137     Ok(n)
138 }
139 
140 /// Writes the payload length of a device message to the stream.
write_length_little_endian(writer: &mut dyn Write, n: usize) -> Result<usize>141 fn write_length_little_endian(writer: &mut dyn Write, n: usize) -> Result<usize> {
142     let mut bytes = [0; 4];
143     bytes[0] = (n & 0xFF) as u8;
144     bytes[1] = ((n >> 8) & 0xFF) as u8;
145     bytes[2] = ((n >> 16) & 0xFF) as u8;
146     bytes[3] = ((n >> 24) & 0xFF) as u8;
147 
148     writer.write(&bytes[..]).map_err(DeviceError::Io)
149 }
150 
read_response(stream: &mut TcpStream, has_output: bool, has_length: bool) -> Result<Vec<u8>>151 fn read_response(stream: &mut TcpStream, has_output: bool, has_length: bool) -> Result<Vec<u8>> {
152     let mut bytes: [u8; 1024] = [0; 1024];
153 
154     stream.read_exact(&mut bytes[0..4])?;
155 
156     if &bytes[0..4] != SyncCommand::Okay.code() {
157         let n = bytes.len().min(read_length(stream)?);
158         stream.read_exact(&mut bytes[0..n])?;
159 
160         let message = std::str::from_utf8(&bytes[0..n]).map(|s| format!("adb error: {}", s))?;
161 
162         return Err(DeviceError::Adb(message));
163     }
164 
165     let mut response = Vec::new();
166 
167     if has_output {
168         stream.read_to_end(&mut response)?;
169 
170         if response.len() >= 4 && &response[0..4] == SyncCommand::Okay.code() {
171             // Sometimes the server produces OKAYOKAY.  Sometimes there is a transport OKAY and
172             // then the underlying command OKAY.  This is straight from `chromedriver`.
173             response = response.split_off(4);
174         }
175 
176         if response.len() >= 4 && &response[0..4] == SyncCommand::Fail.code() {
177             // The server may even produce OKAYFAIL, which means the underlying
178             // command failed. First split-off the `FAIL` and length of the message.
179             response = response.split_off(8);
180 
181             let message = std::str::from_utf8(&*response).map(|s| format!("adb error: {}", s))?;
182 
183             return Err(DeviceError::Adb(message));
184         }
185 
186         if has_length {
187             if response.len() >= 4 {
188                 let message = response.split_off(4);
189                 let slice: &mut &[u8] = &mut &*response;
190 
191                 let n = read_length(slice)?;
192                 warn!(
193                     "adb server response contained hexstring length {} and message length was {} \
194                      and message was {:?}",
195                     n,
196                     message.len(),
197                     std::str::from_utf8(&message)?
198                 );
199 
200                 return Ok(message);
201             } else {
202                 return Err(DeviceError::Adb(format!(
203                     "adb server response did not contain expected hexstring length: {:?}",
204                     std::str::from_utf8(&response)?
205                 )));
206             }
207         }
208     }
209 
210     Ok(response)
211 }
212 
213 /// Detailed information about an ADB device.
214 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
215 pub struct DeviceInfo {
216     pub serial: DeviceSerial,
217     pub info: BTreeMap<String, String>,
218 }
219 
220 /// Represents a connection to an ADB host, which multiplexes the connections to
221 /// individual devices.
222 #[derive(Debug)]
223 pub struct Host {
224     /// The TCP host to connect to.  Defaults to `"localhost"`.
225     pub host: Option<String>,
226     /// The TCP port to connect to.  Defaults to `5037`.
227     pub port: Option<u16>,
228     /// Optional TCP read timeout duration.  Defaults to 2s.
229     pub read_timeout: Option<Duration>,
230     /// Optional TCP write timeout duration.  Defaults to 2s.
231     pub write_timeout: Option<Duration>,
232 }
233 
234 impl Default for Host {
default() -> Host235     fn default() -> Host {
236         Host {
237             host: Some("localhost".to_string()),
238             port: Some(5037),
239             read_timeout: Some(Duration::from_secs(2)),
240             write_timeout: Some(Duration::from_secs(2)),
241         }
242     }
243 }
244 
245 impl Host {
246     /// Searches for available devices, and selects the one as specified by `device_serial`.
247     ///
248     /// If multiple devices are online, and no device has been specified,
249     /// the `ANDROID_SERIAL` environment variable can be used to select one.
device_or_default<T: AsRef<str>>(self, device_serial: Option<&T>) -> Result<Device>250     pub fn device_or_default<T: AsRef<str>>(self, device_serial: Option<&T>) -> Result<Device> {
251         let serials: Vec<String> = self
252             .devices::<Vec<_>>()?
253             .into_iter()
254             .map(|d| d.serial)
255             .collect();
256 
257         if let Some(ref serial) = device_serial
258             .map(|v| v.as_ref().to_owned())
259             .or_else(|| std::env::var("ANDROID_SERIAL").ok())
260         {
261             if !serials.contains(serial) {
262                 return Err(DeviceError::UnknownDevice(serial.clone()));
263             }
264 
265             return Ok(Device {
266                 host: self,
267                 serial: serial.to_owned(),
268             });
269         }
270 
271         if serials.len() > 1 {
272             return Err(DeviceError::MultipleDevices);
273         }
274 
275         if let Some(ref serial) = serials.first() {
276             return Ok(Device {
277                 host: self,
278                 serial: serial.to_string(),
279             });
280         }
281 
282         Err(DeviceError::Adb("No Android devices are online".to_owned()))
283     }
284 
connect(&self) -> Result<TcpStream>285     pub fn connect(&self) -> Result<TcpStream> {
286         let stream = TcpStream::connect(format!(
287             "{}:{}",
288             self.host.clone().unwrap_or_else(|| "localhost".to_owned()),
289             self.port.unwrap_or(5037)
290         ))?;
291         stream.set_read_timeout(self.read_timeout)?;
292         stream.set_write_timeout(self.write_timeout)?;
293         Ok(stream)
294     }
295 
execute_command( &self, command: &str, has_output: bool, has_length: bool, ) -> Result<String>296     pub fn execute_command(
297         &self,
298         command: &str,
299         has_output: bool,
300         has_length: bool,
301     ) -> Result<String> {
302         let mut stream = self.connect()?;
303 
304         stream.write_all(encode_message(command)?.as_bytes())?;
305         let bytes = read_response(&mut stream, has_output, has_length)?;
306         // TODO: should we assert no bytes were read?
307 
308         let response = std::str::from_utf8(&bytes)?;
309 
310         Ok(response.to_owned())
311     }
312 
execute_host_command( &self, host_command: &str, has_length: bool, has_output: bool, ) -> Result<String>313     pub fn execute_host_command(
314         &self,
315         host_command: &str,
316         has_length: bool,
317         has_output: bool,
318     ) -> Result<String> {
319         self.execute_command(&format!("host:{}", host_command), has_output, has_length)
320     }
321 
features<B: FromIterator<String>>(&self) -> Result<B>322     pub fn features<B: FromIterator<String>>(&self) -> Result<B> {
323         let features = self.execute_host_command("features", true, true)?;
324         Ok(features.split(',').map(|x| x.to_owned()).collect())
325     }
326 
devices<B: FromIterator<DeviceInfo>>(&self) -> Result<B>327     pub fn devices<B: FromIterator<DeviceInfo>>(&self) -> Result<B> {
328         let response = self.execute_host_command("devices-l", true, true)?;
329 
330         let infos: B = response.lines().filter_map(parse_device_info).collect();
331 
332         Ok(infos)
333     }
334 }
335 
336 /// Represents an ADB device.
337 #[derive(Debug)]
338 pub struct Device {
339     /// ADB host that controls this device.
340     pub host: Host,
341 
342     /// Serial number uniquely identifying this ADB device.
343     pub serial: DeviceSerial,
344 }
345 
346 impl Device {
execute_host_command( &self, command: &str, has_output: bool, has_length: bool, ) -> Result<String>347     pub fn execute_host_command(
348         &self,
349         command: &str,
350         has_output: bool,
351         has_length: bool,
352     ) -> Result<String> {
353         let mut stream = self.host.connect()?;
354 
355         let switch_command = format!("host:transport:{}", self.serial);
356         debug!("execute_host_command: >> {:?}", &switch_command);
357         stream.write_all(encode_message(&switch_command)?.as_bytes())?;
358         let _bytes = read_response(&mut stream, false, false)?;
359         debug!("execute_host_command: << {:?}", _bytes);
360         // TODO: should we assert no bytes were read?
361 
362         debug!("execute_host_command: >> {:?}", &command);
363         stream.write_all(encode_message(&command)?.as_bytes())?;
364         let bytes = read_response(&mut stream, has_output, has_length)?;
365 
366         let response = std::str::from_utf8(&bytes)?;
367         debug!("execute_host_command: << {:?}", response);
368 
369         Ok(response.to_owned())
370     }
371 
execute_host_shell_command(&self, shell_command: &str) -> Result<String>372     pub fn execute_host_shell_command(&self, shell_command: &str) -> Result<String> {
373         let response =
374             self.execute_host_command(&format!("shell:{}", shell_command), true, false)?;
375 
376         Ok(response)
377     }
378 
is_app_installed(&self, package: &str) -> Result<bool>379     pub fn is_app_installed(&self, package: &str) -> Result<bool> {
380         self.execute_host_shell_command(&format!("pm path {}", package))
381             .map(|v| v.contains("package:"))
382     }
383 
clear_app_data(&self, package: &str) -> Result<bool>384     pub fn clear_app_data(&self, package: &str) -> Result<bool> {
385         self.execute_host_shell_command(&format!("pm clear {}", package))
386             .map(|v| v.contains("Success"))
387     }
388 
launch<T: AsRef<str>>( &self, package: &str, activity: &str, am_start_args: &[T], ) -> Result<bool>389     pub fn launch<T: AsRef<str>>(
390         &self,
391         package: &str,
392         activity: &str,
393         am_start_args: &[T],
394     ) -> Result<bool> {
395         let mut am_start = format!("am start -W -n {}/{}", package, activity);
396 
397         for arg in am_start_args {
398             am_start.push_str(" ");
399             am_start.push_str(&shell::escape(arg.as_ref()));
400         }
401 
402         self.execute_host_shell_command(&am_start)
403             .map(|v| v.contains("Complete"))
404     }
405 
force_stop(&self, package: &str) -> Result<()>406     pub fn force_stop(&self, package: &str) -> Result<()> {
407         debug!("Force stopping Android package: {}", package);
408         self.execute_host_shell_command(&format!("am force-stop {}", package))
409             .and(Ok(()))
410     }
411 
forward_port(&self, local: u16, remote: u16) -> Result<u16>412     pub fn forward_port(&self, local: u16, remote: u16) -> Result<u16> {
413         let command = format!(
414             "host-serial:{}:forward:tcp:{};tcp:{}",
415             self.serial, local, remote
416         );
417         let response = self.host.execute_command(&command, true, false)?;
418 
419         if local == 0 {
420             Ok(response.parse::<u16>()?)
421         } else {
422             Ok(local)
423         }
424     }
425 
kill_forward_port(&self, local: u16) -> Result<()>426     pub fn kill_forward_port(&self, local: u16) -> Result<()> {
427         let command = format!("killforward:tcp:{}", local);
428         self.execute_host_command(&command, true, false).and(Ok(()))
429     }
430 
kill_forward_all_ports(&self) -> Result<()>431     pub fn kill_forward_all_ports(&self) -> Result<()> {
432         self.execute_host_command(&"killforward-all".to_owned(), false, false)
433             .and(Ok(()))
434     }
435 
reverse_port(&self, remote: u16, local: u16) -> Result<u16>436     pub fn reverse_port(&self, remote: u16, local: u16) -> Result<u16> {
437         let command = format!("reverse:forward:tcp:{};tcp:{}", remote, local);
438         let response = self.execute_host_command(&command, true, false)?;
439 
440         if remote == 0 {
441             Ok(response.parse::<u16>()?)
442         } else {
443             Ok(remote)
444         }
445     }
446 
kill_reverse_port(&self, remote: u16) -> Result<()>447     pub fn kill_reverse_port(&self, remote: u16) -> Result<()> {
448         let command = format!("reverse:killforward:tcp:{}", remote);
449         self.execute_host_command(&command, true, true).and(Ok(()))
450     }
451 
kill_reverse_all_ports(&self) -> Result<()>452     pub fn kill_reverse_all_ports(&self) -> Result<()> {
453         let command = "reverse:killforward-all".to_owned();
454         self.execute_host_command(&command, false, false)
455             .and(Ok(()))
456     }
457 
push(&self, buffer: &mut dyn Read, dest: &Path, mode: u32) -> Result<()>458     pub fn push(&self, buffer: &mut dyn Read, dest: &Path, mode: u32) -> Result<()> {
459         // Implement the ADB protocol to send a file to the device.
460         // The protocol consists of the following steps:
461         // * Send "host:transport" command with device serial
462         // * Send "sync:" command to initialize file transfer
463         // * Send "SEND" command with name and mode of the file
464         // * Send "DATA" command one or more times for the file content
465         // * Send "DONE" command to indicate end of file transfer
466         let mut stream = self.host.connect()?;
467 
468         let message = encode_message(&format!("host:transport:{}", self.serial))?;
469         stream.write_all(message.as_bytes())?;
470         let _bytes = read_response(&mut stream, false, true)?;
471 
472         let message = encode_message("sync:")?;
473         stream.write_all(message.as_bytes())?;
474         let _bytes = read_response(&mut stream, false, true)?;
475 
476         stream.write_all(SyncCommand::Send.code())?;
477         let args_ = format!("{},{}", dest.display(), mode);
478         let args = args_.as_bytes();
479         write_length_little_endian(&mut stream, args.len())?;
480         stream.write_all(args)?;
481 
482         // Use a 32KB buffer to transfer the file contents
483         // TODO: Maybe adjust to maxdata (256KB)
484         let mut buf = [0; 32 * 1024];
485 
486         loop {
487             let len = buffer.read(&mut buf)?;
488 
489             if len == 0 {
490                 break;
491             }
492 
493             stream.write_all(SyncCommand::Data.code())?;
494             write_length_little_endian(&mut stream, len)?;
495             stream.write_all(&buf[0..len])?;
496         }
497 
498         // https://android.googlesource.com/platform/system/core/+/master/adb/SYNC.TXT#66
499         //
500         // When the file is transferred a sync request "DONE" is sent, where length is set
501         // to the last modified time for the file. The server responds to this last
502         // request (but not to chunk requests) with an "OKAY" sync response (length can
503         // be ignored).
504         let time: u32 = ((SystemTime::now().duration_since(SystemTime::UNIX_EPOCH))
505             .unwrap()
506             .as_secs()
507             & 0xFFFF_FFFF) as u32;
508 
509         stream.write_all(SyncCommand::Done.code())?;
510         write_length_little_endian(&mut stream, time as usize)?;
511 
512         // Status.
513         stream.read_exact(&mut buf[0..4])?;
514 
515         if &buf[0..4] == SyncCommand::Okay.code() {
516             Ok(())
517         } else if &buf[0..4] == SyncCommand::Fail.code() {
518             let n = buf.len().min(read_length_little_endian(&mut stream)?);
519 
520             stream.read_exact(&mut buf[0..n])?;
521 
522             let message = std::str::from_utf8(&buf[0..n])
523                 .map(|s| format!("adb error: {}", s))
524                 .unwrap_or_else(|_| "adb error was not utf-8".into());
525 
526             Err(DeviceError::Adb(message))
527         } else {
528             Err(DeviceError::Adb("FAIL (unknown)".to_owned()))
529         }
530     }
531 
push_dir(&self, source: &Path, dest_dir: &Path, mode: u32) -> Result<()>532     pub fn push_dir(&self, source: &Path, dest_dir: &Path, mode: u32) -> Result<()> {
533         let walker = WalkDir::new(source).follow_links(false).into_iter();
534 
535         for entry in walker {
536             let entry = entry?;
537             let path = entry.path();
538 
539             if !entry.metadata()?.is_file() {
540                 continue;
541             }
542 
543             let mut file = File::open(path)?;
544 
545             let mut dest = dest_dir.to_path_buf();
546             dest.push(
547                 path.strip_prefix(source)
548                     .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?,
549             );
550 
551             self.push(&mut file, &dest, mode)?;
552         }
553 
554         Ok(())
555     }
556 }
557