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