1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #[macro_use]
6 extern crate log;
7 extern crate once_cell;
8 extern crate regex;
9 extern crate tempfile;
10 extern crate walkdir;
11 
12 pub mod adb;
13 pub mod shell;
14 
15 #[cfg(test)]
16 pub mod test;
17 
18 use once_cell::sync::Lazy;
19 use regex::Regex;
20 use std::collections::BTreeMap;
21 use std::convert::TryFrom;
22 use std::fmt;
23 use std::fs::File;
24 use std::io::{self, Read, Write};
25 use std::iter::FromIterator;
26 use std::net::TcpStream;
27 use std::num::{ParseIntError, TryFromIntError};
28 use std::path::{Path, PathBuf};
29 use std::str::{FromStr, Utf8Error};
30 use std::time::{Duration, SystemTime};
31 use uuid::Uuid;
32 use walkdir::WalkDir;
33 
34 use crate::adb::{DeviceSerial, SyncCommand};
35 
36 pub type Result<T> = std::result::Result<T, DeviceError>;
37 
38 static SYNC_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"[^A-Za-z0-9_@%+=:,./-]").unwrap());
39 
40 #[derive(Debug, Clone, Copy, PartialEq)]
41 pub enum AndroidStorageInput {
42     Auto,
43     App,
44     Internal,
45     Sdcard,
46 }
47 
48 impl FromStr for AndroidStorageInput {
49     type Err = DeviceError;
50 
from_str(s: &str) -> Result<Self>51     fn from_str(s: &str) -> Result<Self> {
52         match s {
53             "auto" => Ok(AndroidStorageInput::Auto),
54             "app" => Ok(AndroidStorageInput::App),
55             "internal" => Ok(AndroidStorageInput::Internal),
56             "sdcard" => Ok(AndroidStorageInput::Sdcard),
57             _ => Err(DeviceError::InvalidStorage),
58         }
59     }
60 }
61 
62 impl Default for AndroidStorageInput {
default() -> Self63     fn default() -> Self {
64         AndroidStorageInput::Auto
65     }
66 }
67 
68 #[derive(Debug, Clone, Copy, PartialEq)]
69 pub enum AndroidStorage {
70     App,
71     Internal,
72     Sdcard,
73 }
74 
75 #[derive(Debug)]
76 pub enum DeviceError {
77     Adb(String),
78     FromInt(TryFromIntError),
79     InvalidStorage,
80     Io(io::Error),
81     MissingPackage,
82     MultipleDevices,
83     ParseInt(ParseIntError),
84     UnknownDevice(String),
85     Utf8(Utf8Error),
86     WalkDir(walkdir::Error),
87 }
88 
89 impl fmt::Display for DeviceError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result90     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91         match *self {
92             DeviceError::Adb(ref message) => message.fmt(f),
93             DeviceError::FromInt(ref int) => int.fmt(f),
94             DeviceError::InvalidStorage => write!(f, "Invalid storage"),
95             DeviceError::Io(ref error) => error.fmt(f),
96             DeviceError::MissingPackage => write!(f, "Missing package"),
97             DeviceError::MultipleDevices => write!(f, "Multiple Android devices online"),
98             DeviceError::ParseInt(ref error) => error.fmt(f),
99             DeviceError::UnknownDevice(ref serial) => {
100                 write!(f, "Unknown Android device with serial '{}'", serial)
101             }
102             DeviceError::Utf8(ref error) => error.fmt(f),
103             DeviceError::WalkDir(ref error) => error.fmt(f),
104         }
105     }
106 }
107 
108 impl From<io::Error> for DeviceError {
from(value: io::Error) -> DeviceError109     fn from(value: io::Error) -> DeviceError {
110         DeviceError::Io(value)
111     }
112 }
113 
114 impl From<ParseIntError> for DeviceError {
from(value: ParseIntError) -> DeviceError115     fn from(value: ParseIntError) -> DeviceError {
116         DeviceError::ParseInt(value)
117     }
118 }
119 
120 impl From<TryFromIntError> for DeviceError {
from(value: TryFromIntError) -> DeviceError121     fn from(value: TryFromIntError) -> DeviceError {
122         DeviceError::FromInt(value)
123     }
124 }
125 
126 impl From<Utf8Error> for DeviceError {
from(value: Utf8Error) -> DeviceError127     fn from(value: Utf8Error) -> DeviceError {
128         DeviceError::Utf8(value)
129     }
130 }
131 
132 impl From<walkdir::Error> for DeviceError {
from(value: walkdir::Error) -> DeviceError133     fn from(value: walkdir::Error) -> DeviceError {
134         DeviceError::WalkDir(value)
135     }
136 }
137 
encode_message(payload: &str) -> Result<String>138 fn encode_message(payload: &str) -> Result<String> {
139     let hex_length = u16::try_from(payload.len()).map(|len| format!("{:0>4X}", len))?;
140 
141     Ok(format!("{}{}", hex_length, payload))
142 }
143 
parse_device_info(line: &str) -> Option<DeviceInfo>144 fn parse_device_info(line: &str) -> Option<DeviceInfo> {
145     // Turn "serial\tdevice key1:value1 key2:value2 ..." into a `DeviceInfo`.
146     let mut pairs = line.split_whitespace();
147     let serial = pairs.next();
148     let state = pairs.next();
149     if let (Some(serial), Some("device")) = (serial, state) {
150         let info: BTreeMap<String, String> = pairs
151             .filter_map(|pair| {
152                 let mut kv = pair.split(':');
153                 if let (Some(k), Some(v), None) = (kv.next(), kv.next(), kv.next()) {
154                     Some((k.to_owned(), v.to_owned()))
155                 } else {
156                     None
157                 }
158             })
159             .collect();
160 
161         Some(DeviceInfo {
162             serial: serial.to_owned(),
163             info,
164         })
165     } else {
166         None
167     }
168 }
169 
170 /// Reads the payload length of a host message from the stream.
read_length<R: Read>(stream: &mut R) -> Result<usize>171 fn read_length<R: Read>(stream: &mut R) -> Result<usize> {
172     let mut bytes: [u8; 4] = [0; 4];
173     stream.read_exact(&mut bytes)?;
174 
175     let response = std::str::from_utf8(&bytes)?;
176 
177     Ok(usize::from_str_radix(&response, 16)?)
178 }
179 
180 /// Reads the payload length of a device message from the stream.
read_length_little_endian(reader: &mut dyn Read) -> Result<usize>181 fn read_length_little_endian(reader: &mut dyn Read) -> Result<usize> {
182     let mut bytes: [u8; 4] = [0; 4];
183     reader.read_exact(&mut bytes)?;
184 
185     let n: usize = (bytes[0] as usize)
186         + ((bytes[1] as usize) << 8)
187         + ((bytes[2] as usize) << 16)
188         + ((bytes[3] as usize) << 24);
189 
190     Ok(n)
191 }
192 
193 /// Writes the payload length of a device message to the stream.
write_length_little_endian(writer: &mut dyn Write, n: usize) -> Result<usize>194 fn write_length_little_endian(writer: &mut dyn Write, n: usize) -> Result<usize> {
195     let mut bytes = [0; 4];
196     bytes[0] = (n & 0xFF) as u8;
197     bytes[1] = ((n >> 8) & 0xFF) as u8;
198     bytes[2] = ((n >> 16) & 0xFF) as u8;
199     bytes[3] = ((n >> 24) & 0xFF) as u8;
200 
201     writer.write(&bytes[..]).map_err(DeviceError::Io)
202 }
203 
read_response(stream: &mut TcpStream, has_output: bool, has_length: bool) -> Result<Vec<u8>>204 fn read_response(stream: &mut TcpStream, has_output: bool, has_length: bool) -> Result<Vec<u8>> {
205     let mut bytes: [u8; 1024] = [0; 1024];
206 
207     stream.read_exact(&mut bytes[0..4])?;
208 
209     if &bytes[0..4] != SyncCommand::Okay.code() {
210         let n = bytes.len().min(read_length(stream)?);
211         stream.read_exact(&mut bytes[0..n])?;
212 
213         let message = std::str::from_utf8(&bytes[0..n]).map(|s| format!("adb error: {}", s))?;
214 
215         return Err(DeviceError::Adb(message));
216     }
217 
218     let mut response = Vec::new();
219 
220     if has_output {
221         stream.read_to_end(&mut response)?;
222 
223         if response.len() >= 4 && &response[0..4] == SyncCommand::Okay.code() {
224             // Sometimes the server produces OKAYOKAY.  Sometimes there is a transport OKAY and
225             // then the underlying command OKAY.  This is straight from `chromedriver`.
226             response = response.split_off(4);
227         }
228 
229         if response.len() >= 4 && &response[0..4] == SyncCommand::Fail.code() {
230             // The server may even produce OKAYFAIL, which means the underlying
231             // command failed. First split-off the `FAIL` and length of the message.
232             response = response.split_off(8);
233 
234             let message = std::str::from_utf8(&*response).map(|s| format!("adb error: {}", s))?;
235 
236             return Err(DeviceError::Adb(message));
237         }
238 
239         if has_length {
240             if response.len() >= 4 {
241                 let message = response.split_off(4);
242                 let slice: &mut &[u8] = &mut &*response;
243 
244                 let n = read_length(slice)?;
245                 warn!(
246                     "adb server response contained hexstring length {} and message length was {} \
247                      and message was {:?}",
248                     n,
249                     message.len(),
250                     std::str::from_utf8(&message)?
251                 );
252 
253                 return Ok(message);
254             } else {
255                 return Err(DeviceError::Adb(format!(
256                     "adb server response did not contain expected hexstring length: {:?}",
257                     std::str::from_utf8(&response)?
258                 )));
259             }
260         }
261     }
262 
263     Ok(response)
264 }
265 
266 /// Detailed information about an ADB device.
267 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
268 pub struct DeviceInfo {
269     pub serial: DeviceSerial,
270     pub info: BTreeMap<String, String>,
271 }
272 
273 /// Represents a connection to an ADB host, which multiplexes the connections to
274 /// individual devices.
275 #[derive(Debug)]
276 pub struct Host {
277     /// The TCP host to connect to.  Defaults to `"localhost"`.
278     pub host: Option<String>,
279     /// The TCP port to connect to.  Defaults to `5037`.
280     pub port: Option<u16>,
281     /// Optional TCP read timeout duration.  Defaults to 2s.
282     pub read_timeout: Option<Duration>,
283     /// Optional TCP write timeout duration.  Defaults to 2s.
284     pub write_timeout: Option<Duration>,
285 }
286 
287 impl Default for Host {
default() -> Host288     fn default() -> Host {
289         Host {
290             host: Some("localhost".to_string()),
291             port: Some(5037),
292             read_timeout: Some(Duration::from_secs(2)),
293             write_timeout: Some(Duration::from_secs(2)),
294         }
295     }
296 }
297 
298 impl Host {
299     /// Searches for available devices, and selects the one as specified by `device_serial`.
300     ///
301     /// If multiple devices are online, and no device has been specified,
302     /// the `ANDROID_SERIAL` environment variable can be used to select one.
device_or_default<T: AsRef<str>>( self, device_serial: Option<&T>, storage: AndroidStorageInput, ) -> Result<Device>303     pub fn device_or_default<T: AsRef<str>>(
304         self,
305         device_serial: Option<&T>,
306         storage: AndroidStorageInput,
307     ) -> Result<Device> {
308         let serials: Vec<String> = self
309             .devices::<Vec<_>>()?
310             .into_iter()
311             .map(|d| d.serial)
312             .collect();
313 
314         if let Some(ref serial) = device_serial
315             .map(|v| v.as_ref().to_owned())
316             .or_else(|| std::env::var("ANDROID_SERIAL").ok())
317         {
318             if !serials.contains(serial) {
319                 return Err(DeviceError::UnknownDevice(serial.clone()));
320             }
321 
322             return Device::new(self, serial.to_owned(), storage);
323         }
324 
325         if serials.len() > 1 {
326             return Err(DeviceError::MultipleDevices);
327         }
328 
329         if let Some(ref serial) = serials.first() {
330             return Device::new(self, serial.to_owned().to_string(), storage);
331         }
332 
333         Err(DeviceError::Adb("No Android devices are online".to_owned()))
334     }
335 
connect(&self) -> Result<TcpStream>336     pub fn connect(&self) -> Result<TcpStream> {
337         let stream = TcpStream::connect(format!(
338             "{}:{}",
339             self.host.clone().unwrap_or_else(|| "localhost".to_owned()),
340             self.port.unwrap_or(5037)
341         ))?;
342         stream.set_read_timeout(self.read_timeout)?;
343         stream.set_write_timeout(self.write_timeout)?;
344         Ok(stream)
345     }
346 
execute_command( &self, command: &str, has_output: bool, has_length: bool, ) -> Result<String>347     pub fn execute_command(
348         &self,
349         command: &str,
350         has_output: bool,
351         has_length: bool,
352     ) -> Result<String> {
353         let mut stream = self.connect()?;
354 
355         stream.write_all(encode_message(command)?.as_bytes())?;
356         let bytes = read_response(&mut stream, has_output, has_length)?;
357         // TODO: should we assert no bytes were read?
358 
359         let response = std::str::from_utf8(&bytes)?;
360 
361         Ok(response.to_owned())
362     }
363 
execute_host_command( &self, host_command: &str, has_length: bool, has_output: bool, ) -> Result<String>364     pub fn execute_host_command(
365         &self,
366         host_command: &str,
367         has_length: bool,
368         has_output: bool,
369     ) -> Result<String> {
370         self.execute_command(&format!("host:{}", host_command), has_output, has_length)
371     }
372 
features<B: FromIterator<String>>(&self) -> Result<B>373     pub fn features<B: FromIterator<String>>(&self) -> Result<B> {
374         let features = self.execute_host_command("features", true, true)?;
375         Ok(features.split(',').map(|x| x.to_owned()).collect())
376     }
377 
devices<B: FromIterator<DeviceInfo>>(&self) -> Result<B>378     pub fn devices<B: FromIterator<DeviceInfo>>(&self) -> Result<B> {
379         let response = self.execute_host_command("devices-l", true, true)?;
380 
381         let infos: B = response.lines().filter_map(parse_device_info).collect();
382 
383         Ok(infos)
384     }
385 }
386 
387 /// Represents an ADB device.
388 #[derive(Debug)]
389 pub struct Device {
390     /// ADB host that controls this device.
391     pub host: Host,
392 
393     /// Serial number uniquely identifying this ADB device.
394     pub serial: DeviceSerial,
395 
396     /// adb running as root
397     pub adbd_root: bool,
398 
399     /// Flag for rooted device
400     pub is_rooted: bool,
401 
402     /// "su 0" command available
403     pub su_0_root: bool,
404 
405     /// "su -c" command available
406     pub su_c_root: bool,
407 
408     pub run_as_package: Option<String>,
409 
410     pub storage: AndroidStorage,
411 
412     /// Cache intermediate tempfile name used in pushing via run_as.
413     pub tempfile: PathBuf,
414 }
415 
416 impl Device {
new(host: Host, serial: DeviceSerial, storage: AndroidStorageInput) -> Result<Device>417     pub fn new(host: Host, serial: DeviceSerial, storage: AndroidStorageInput) -> Result<Device> {
418         let mut device = Device {
419             host,
420             serial,
421             adbd_root: false,
422             is_rooted: false,
423             run_as_package: None,
424             storage: AndroidStorage::App,
425             su_c_root: false,
426             su_0_root: false,
427             tempfile: PathBuf::from("/data/local/tmp"),
428         };
429         device
430             .tempfile
431             .push(Uuid::new_v4().to_hyphenated().to_string());
432 
433         // check for rooted devices
434         let uid_check = |id: String| id.contains("uid=0");
435         device.adbd_root = device
436             .execute_host_shell_command("id")
437             .map_or(false, uid_check);
438         device.su_0_root = device
439             .execute_host_shell_command("su 0 id")
440             .map_or(false, uid_check);
441         device.su_c_root = device
442             .execute_host_shell_command("su -c id")
443             .map_or(false, uid_check);
444         device.is_rooted = device.adbd_root || device.su_0_root || device.su_c_root;
445 
446         device.storage = match storage {
447             AndroidStorageInput::App => AndroidStorage::App,
448             AndroidStorageInput::Internal => AndroidStorage::Internal,
449             AndroidStorageInput::Sdcard => AndroidStorage::Sdcard,
450             AndroidStorageInput::Auto => AndroidStorage::Sdcard,
451         };
452 
453         if device.is_rooted {
454             debug!("Device is rooted");
455 
456             // Set Permissive=1 if we have root.
457             device.execute_host_shell_command("setenforce permissive")?;
458         } else {
459             debug!("Device is unrooted");
460         }
461 
462         Ok(device)
463     }
464 
clear_app_data(&self, package: &str) -> Result<bool>465     pub fn clear_app_data(&self, package: &str) -> Result<bool> {
466         self.execute_host_shell_command(&format!("pm clear {}", package))
467             .map(|v| v.contains("Success"))
468     }
469 
create_dir(&self, path: &Path) -> Result<()>470     pub fn create_dir(&self, path: &Path) -> Result<()> {
471         debug!("Creating {}", path.display());
472 
473         let enable_run_as = self.enable_run_as_for_path(&path);
474         self.execute_host_shell_command_as(&format!("mkdir -p {}", path.display()), enable_run_as)?;
475 
476         Ok(())
477     }
478 
chmod(&self, path: &Path, mask: &str, recursive: bool) -> Result<()>479     pub fn chmod(&self, path: &Path, mask: &str, recursive: bool) -> Result<()> {
480         let enable_run_as = self.enable_run_as_for_path(&path);
481 
482         let recursive = match recursive {
483             true => " -R",
484             false => "",
485         };
486 
487         self.execute_host_shell_command_as(
488             &format!("chmod {} {} {}", recursive, mask, path.display()),
489             enable_run_as,
490         )?;
491 
492         Ok(())
493     }
494 
execute_host_command( &self, command: &str, has_output: bool, has_length: bool, ) -> Result<String>495     pub fn execute_host_command(
496         &self,
497         command: &str,
498         has_output: bool,
499         has_length: bool,
500     ) -> Result<String> {
501         let mut stream = self.host.connect()?;
502 
503         let switch_command = format!("host:transport:{}", self.serial);
504         debug!("execute_host_command: >> {:?}", &switch_command);
505         stream.write_all(encode_message(&switch_command)?.as_bytes())?;
506         let _bytes = read_response(&mut stream, false, false)?;
507         debug!("execute_host_command: << {:?}", _bytes);
508         // TODO: should we assert no bytes were read?
509 
510         debug!("execute_host_command: >> {:?}", &command);
511         stream.write_all(encode_message(&command)?.as_bytes())?;
512         let bytes = read_response(&mut stream, has_output, has_length)?;
513 
514         let response = std::str::from_utf8(&bytes)?;
515         debug!("execute_host_command: << {:?}", response);
516 
517         // Unify new lines by removing possible carriage returns
518         Ok(response.replace("\r\n", "\n"))
519     }
520 
enable_run_as_for_path(&self, path: &Path) -> bool521     pub fn enable_run_as_for_path(&self, path: &Path) -> bool {
522         match &self.run_as_package {
523             Some(package) => {
524                 let mut p = PathBuf::from("/data/data/");
525                 p.push(package);
526                 path.starts_with(p)
527             }
528             None => false,
529         }
530     }
531 
execute_host_shell_command(&self, shell_command: &str) -> Result<String>532     pub fn execute_host_shell_command(&self, shell_command: &str) -> Result<String> {
533         self.execute_host_shell_command_as(shell_command, false)
534     }
535 
execute_host_shell_command_as( &self, shell_command: &str, enable_run_as: bool, ) -> Result<String>536     pub fn execute_host_shell_command_as(
537         &self,
538         shell_command: &str,
539         enable_run_as: bool,
540     ) -> Result<String> {
541         // We don't want to duplicate su invocations.
542         if shell_command.starts_with("su") {
543             return self.execute_host_command(&format!("shell:{}", shell_command), true, false);
544         }
545 
546         let has_outer_quotes = shell_command.starts_with('"') && shell_command.ends_with('"')
547             || shell_command.starts_with('\'') && shell_command.ends_with('\'');
548 
549         if self.adbd_root {
550             return self.execute_host_command(&format!("shell:{}", shell_command), true, false);
551         }
552 
553         if self.su_0_root {
554             return self.execute_host_command(
555                 &format!("shell:su 0 {}", shell_command),
556                 true,
557                 false,
558             );
559         }
560 
561         if self.su_c_root {
562             if has_outer_quotes {
563                 return self.execute_host_command(
564                     &format!("shell:su -c {}", shell_command),
565                     true,
566                     false,
567                 );
568             }
569 
570             if SYNC_REGEX.is_match(shell_command) {
571                 let arg: &str = &shell_command.replace("'", "'\"'\"'")[..];
572                 return self.execute_host_command(&format!("shell:su -c '{}'", arg), true, false);
573             }
574 
575             return self.execute_host_command(
576                 &format!("shell:su -c \"{}\"", shell_command),
577                 true,
578                 false,
579             );
580         }
581 
582         // Execute command as package
583         if enable_run_as {
584             let run_as_package = self
585                 .run_as_package
586                 .as_ref()
587                 .ok_or(DeviceError::MissingPackage)?;
588 
589             if has_outer_quotes {
590                 return self.execute_host_command(
591                     &format!("shell:run-as {} {}", run_as_package, shell_command),
592                     true,
593                     false,
594                 );
595             }
596 
597             if SYNC_REGEX.is_match(shell_command) {
598                 let arg: &str = &shell_command.replace("'", "'\"'\"'")[..];
599                 return self.execute_host_command(
600                     &format!("shell:run-as {} {}", run_as_package, arg),
601                     true,
602                     false,
603                 );
604             }
605 
606             return self.execute_host_command(
607                 &format!("shell:run-as {} \"{}\"", run_as_package, shell_command),
608                 true,
609                 false,
610             );
611         }
612 
613         self.execute_host_command(&format!("shell:{}", shell_command), true, false)
614     }
615 
is_app_installed(&self, package: &str) -> Result<bool>616     pub fn is_app_installed(&self, package: &str) -> Result<bool> {
617         self.execute_host_shell_command(&format!("pm path {}", package))
618             .map(|v| v.contains("package:"))
619     }
620 
launch<T: AsRef<str>>( &self, package: &str, activity: &str, am_start_args: &[T], ) -> Result<bool>621     pub fn launch<T: AsRef<str>>(
622         &self,
623         package: &str,
624         activity: &str,
625         am_start_args: &[T],
626     ) -> Result<bool> {
627         let mut am_start = format!("am start -W -n {}/{}", package, activity);
628 
629         for arg in am_start_args {
630             am_start.push(' ');
631             if SYNC_REGEX.is_match(arg.as_ref()) {
632                 am_start.push_str(&format!("\"{}\"", &shell::escape(arg.as_ref())));
633             } else {
634                 am_start.push_str(&shell::escape(arg.as_ref()));
635             };
636         }
637 
638         self.execute_host_shell_command(&am_start)
639             .map(|v| v.contains("Complete"))
640     }
641 
force_stop(&self, package: &str) -> Result<()>642     pub fn force_stop(&self, package: &str) -> Result<()> {
643         debug!("Force stopping Android package: {}", package);
644         self.execute_host_shell_command(&format!("am force-stop {}", package))
645             .and(Ok(()))
646     }
647 
forward_port(&self, local: u16, remote: u16) -> Result<u16>648     pub fn forward_port(&self, local: u16, remote: u16) -> Result<u16> {
649         let command = format!(
650             "host-serial:{}:forward:tcp:{};tcp:{}",
651             self.serial, local, remote
652         );
653         let response = self.host.execute_command(&command, true, false)?;
654 
655         if local == 0 {
656             Ok(response.parse::<u16>()?)
657         } else {
658             Ok(local)
659         }
660     }
661 
kill_forward_port(&self, local: u16) -> Result<()>662     pub fn kill_forward_port(&self, local: u16) -> Result<()> {
663         let command = format!("killforward:tcp:{}", local);
664         self.execute_host_command(&command, true, false).and(Ok(()))
665     }
666 
kill_forward_all_ports(&self) -> Result<()>667     pub fn kill_forward_all_ports(&self) -> Result<()> {
668         self.execute_host_command(&"killforward-all".to_owned(), false, false)
669             .and(Ok(()))
670     }
671 
reverse_port(&self, remote: u16, local: u16) -> Result<u16>672     pub fn reverse_port(&self, remote: u16, local: u16) -> Result<u16> {
673         let command = format!("reverse:forward:tcp:{};tcp:{}", remote, local);
674         let response = self.execute_host_command(&command, true, false)?;
675 
676         if remote == 0 {
677             Ok(response.parse::<u16>()?)
678         } else {
679             Ok(remote)
680         }
681     }
682 
kill_reverse_port(&self, remote: u16) -> Result<()>683     pub fn kill_reverse_port(&self, remote: u16) -> Result<()> {
684         let command = format!("reverse:killforward:tcp:{}", remote);
685         self.execute_host_command(&command, true, true).and(Ok(()))
686     }
687 
kill_reverse_all_ports(&self) -> Result<()>688     pub fn kill_reverse_all_ports(&self) -> Result<()> {
689         let command = "reverse:killforward-all".to_owned();
690         self.execute_host_command(&command, false, false)
691             .and(Ok(()))
692     }
693 
path_exists(&self, path: &Path, enable_run_as: bool) -> Result<bool>694     pub fn path_exists(&self, path: &Path, enable_run_as: bool) -> Result<bool> {
695         self.execute_host_shell_command_as(format!("ls {}", path.display()).as_str(), enable_run_as)
696             .map(|path| !path.contains("No such file or directory"))
697     }
698 
push(&self, buffer: &mut dyn Read, dest: &Path, mode: u32) -> Result<()>699     pub fn push(&self, buffer: &mut dyn Read, dest: &Path, mode: u32) -> Result<()> {
700         // Implement the ADB protocol to send a file to the device.
701         // The protocol consists of the following steps:
702         // * Send "host:transport" command with device serial
703         // * Send "sync:" command to initialize file transfer
704         // * Send "SEND" command with name and mode of the file
705         // * Send "DATA" command one or more times for the file content
706         // * Send "DONE" command to indicate end of file transfer
707 
708         let enable_run_as = self.enable_run_as_for_path(&dest.to_path_buf());
709         let dest1 = match enable_run_as {
710             true => self.tempfile.as_path(),
711             false => Path::new(dest),
712         };
713 
714         // If the destination directory does not exist, adb will
715         // create it and any necessary ancestors however it will not
716         // set the directory permissions to 0o777.  In addition,
717         // Android 9 (P) has a bug in its push implementation which
718         // will cause a push which creates directories to fail with
719         // the error `secure_mkdirs failed: Operation not
720         // permitted`. We can work around this by creating the
721         // destination directories prior to the push.  Collect the
722         // ancestors of the destination directory which do not yet
723         // exist so we can create them and adjust their permissions
724         // prior to performing the push.
725         let mut current = dest.parent();
726         let mut leaf: Option<&Path> = None;
727         let mut root: Option<&Path> = None;
728 
729         while let Some(path) = current {
730             if self.path_exists(path, enable_run_as)? {
731                 break;
732             }
733             if leaf.is_none() {
734                 leaf = Some(path);
735             }
736             root = Some(path);
737             current = path.parent();
738         }
739 
740         if let Some(path) = leaf {
741             self.create_dir(&path)?;
742         }
743 
744         if let Some(path) = root {
745             self.chmod(&path, "777", true)?;
746         }
747 
748         let mut stream = self.host.connect()?;
749 
750         let message = encode_message(&format!("host:transport:{}", self.serial))?;
751         stream.write_all(message.as_bytes())?;
752         let _bytes = read_response(&mut stream, false, true)?;
753 
754         let message = encode_message("sync:")?;
755         stream.write_all(message.as_bytes())?;
756         let _bytes = read_response(&mut stream, false, true)?;
757 
758         stream.write_all(SyncCommand::Send.code())?;
759         let args_ = format!("{},{}", dest1.display(), mode);
760         let args = args_.as_bytes();
761         write_length_little_endian(&mut stream, args.len())?;
762         stream.write_all(args)?;
763 
764         // Use a 32KB buffer to transfer the file contents
765         // TODO: Maybe adjust to maxdata (256KB)
766         let mut buf = [0; 32 * 1024];
767 
768         loop {
769             let len = buffer.read(&mut buf)?;
770 
771             if len == 0 {
772                 break;
773             }
774 
775             stream.write_all(SyncCommand::Data.code())?;
776             write_length_little_endian(&mut stream, len)?;
777             stream.write_all(&buf[0..len])?;
778         }
779 
780         // https://android.googlesource.com/platform/system/core/+/master/adb/SYNC.TXT#66
781         //
782         // When the file is transferred a sync request "DONE" is sent, where length is set
783         // to the last modified time for the file. The server responds to this last
784         // request (but not to chunk requests) with an "OKAY" sync response (length can
785         // be ignored).
786         let time: u32 = ((SystemTime::now().duration_since(SystemTime::UNIX_EPOCH))
787             .unwrap()
788             .as_secs()
789             & 0xFFFF_FFFF) as u32;
790 
791         stream.write_all(SyncCommand::Done.code())?;
792         write_length_little_endian(&mut stream, time as usize)?;
793 
794         // Status.
795         stream.read_exact(&mut buf[0..4])?;
796 
797         if &buf[0..4] == SyncCommand::Okay.code() {
798             if enable_run_as {
799                 // Use cp -a to preserve the permissions set by push.
800                 let result = self.execute_host_shell_command_as(
801                     format!("cp -aR {} {}", dest1.display(), dest.display()).as_str(),
802                     enable_run_as,
803                 );
804                 if self.remove(dest1).is_err() {
805                     debug!("Failed to remove {}", dest1.display());
806                 }
807                 result?;
808             }
809             Ok(())
810         } else if &buf[0..4] == SyncCommand::Fail.code() {
811             if enable_run_as && self.remove(dest1).is_err() {
812                 debug!("Failed to remove {}", dest1.display());
813             }
814             let n = buf.len().min(read_length_little_endian(&mut stream)?);
815 
816             stream.read_exact(&mut buf[0..n])?;
817 
818             let message = std::str::from_utf8(&buf[0..n])
819                 .map(|s| format!("adb error: {}", s))
820                 .unwrap_or_else(|_| "adb error was not utf-8".into());
821 
822             Err(DeviceError::Adb(message))
823         } else {
824             if self.remove(dest1).is_err() {
825                 debug!("Failed to remove {}", dest1.display());
826             }
827             Err(DeviceError::Adb("FAIL (unknown)".to_owned()))
828         }
829     }
830 
push_dir(&self, source: &Path, dest_dir: &Path, mode: u32) -> Result<()>831     pub fn push_dir(&self, source: &Path, dest_dir: &Path, mode: u32) -> Result<()> {
832         debug!("Pushing {} to {}", source.display(), dest_dir.display());
833 
834         let walker = WalkDir::new(source).follow_links(false).into_iter();
835 
836         for entry in walker {
837             let entry = entry?;
838             let path = entry.path();
839 
840             if !entry.metadata()?.is_file() {
841                 continue;
842             }
843 
844             let mut file = File::open(path)?;
845 
846             let mut dest = dest_dir.to_path_buf();
847             dest.push(
848                 path.strip_prefix(source)
849                     .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?,
850             );
851 
852             self.push(&mut file, &dest, mode)?;
853         }
854 
855         Ok(())
856     }
857 
remove(&self, path: &Path) -> Result<()>858     pub fn remove(&self, path: &Path) -> Result<()> {
859         debug!("Deleting {}", path.display());
860 
861         self.execute_host_shell_command_as(
862             &format!("rm -rf {}", path.display()),
863             self.enable_run_as_for_path(&path),
864         )?;
865 
866         Ok(())
867     }
868 }
869