1mod devices;
26mod error;
27mod wire;
28
29use std::fs::File;
30use std::io::{self, ErrorKind, Read, Write};
31use std::net::{Ipv4Addr, SocketAddr, TcpStream};
32use std::path::Path;
33use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
34
35use wire::{SYNC_MAX_PATH, Status, shell_id};
36
37pub use devices::{DeviceEntry, PROP_QUERY, State, Target, parse_devices};
38pub use error::Error;
39pub use wire::{Rule, SYNC_MAX_CHUNK, ShellOutput, ShellPacket};
40
41pub type Result<T> = std::result::Result<T, Error>;
43
44pub const DEFAULT_SERVER_PORT: u16 = 5037;
46
47pub const SERVER_PORT_VAR: &str = "ANDROID_ADB_SERVER_PORT";
49
50pub const CONNECT_TIMEOUT: Duration = Duration::from_secs(2);
52
53pub const DEFAULT_IO_TIMEOUT: Duration = Duration::from_secs(10);
55
56pub fn server_port(value: Option<&str>) -> u16 {
59 value
60 .and_then(|v| v.trim().parse().ok())
61 .unwrap_or(DEFAULT_SERVER_PORT)
62}
63
64fn io_error(request: &str) -> impl FnOnce(io::Error) -> Error + '_ {
66 move |source| Error::Io {
67 request: request.to_owned(),
68 source,
69 }
70}
71
72fn encoded(service: &str) -> Result<Vec<u8>> {
74 wire::encode_request(service).ok_or_else(|| Error::Invalid {
75 request: service.chars().take(80).collect(),
76 detail: format!(
77 "{} bytes is longer than the {}-byte limit of a request",
78 service.len(),
79 wire::MAX_REQUEST_LEN
80 ),
81 })
82}
83
84fn send_request(s: &mut TcpStream, service: &str, bytes: &[u8]) -> Result<()> {
86 s.write_all(bytes).map_err(io_error(service))?;
87 expect_okay(s, service)
88}
89
90fn expect_okay(s: &mut TcpStream, request: &str) -> Result<()> {
92 match wire::read_status(s).map_err(io_error(request))? {
93 Status::Okay => Ok(()),
94 Status::Fail(message) => Err(Error::Fail {
95 request: request.to_owned(),
96 message,
97 }),
98 }
99}
100
101fn read_rest(s: &mut TcpStream, request: &str) -> Result<Vec<u8>> {
103 let mut rest = Vec::new();
104 s.read_to_end(&mut rest).map_err(io_error(request))?;
105 Ok(rest)
106}
107
108fn bound_port(rest: &[u8], request: &str) -> Result<Option<u16>> {
111 if rest.is_empty() {
112 return Ok(None);
113 }
114 let protocol = |detail: String| Error::Protocol {
115 request: request.to_owned(),
116 detail,
117 };
118 let body = wire::read_hex_block(&mut &rest[..])
119 .map_err(|e| protocol(format!("unreadable port after the status: {e}")))?;
120 let text = String::from_utf8_lossy(&body);
121 text.trim()
122 .parse()
123 .map(Some)
124 .map_err(|_| protocol(format!("bound port {text:?} is not a port number")))
125}
126
127fn read_full(r: &mut impl Read, buf: &mut [u8]) -> io::Result<usize> {
129 let mut filled = 0;
130 while filled < buf.len() {
131 match r.read(&mut buf[filled..]) {
132 Ok(0) => break,
133 Ok(n) => filled += n,
134 Err(e) if e.kind() == ErrorKind::Interrupted => {}
135 Err(e) => return Err(e),
136 }
137 }
138 Ok(filled)
139}
140
141enum Source<'a> {
143 Bytes(&'a [u8]),
144 File(&'a Path, File),
145}
146
147#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
149pub enum Stdio {
150 #[default]
153 Raw,
154 Pty,
157}
158
159#[derive(Debug, Clone)]
161pub struct AdbServer {
162 addr: SocketAddr,
163 io_timeout: Duration,
164}
165
166impl AdbServer {
167 pub fn new(addr: SocketAddr) -> Self {
168 Self {
169 addr,
170 io_timeout: DEFAULT_IO_TIMEOUT,
171 }
172 }
173
174 pub fn from_env() -> Self {
176 let port = server_port(std::env::var(SERVER_PORT_VAR).ok().as_deref());
177 Self::new(SocketAddr::from((Ipv4Addr::LOCALHOST, port)))
178 }
179
180 pub fn with_io_timeout(mut self, timeout: Duration) -> Self {
182 self.io_timeout = timeout;
183 self
184 }
185
186 pub fn addr(&self) -> SocketAddr {
187 self.addr
188 }
189
190 pub fn devices(&self) -> Result<Vec<DeviceEntry>> {
192 let request = "host:devices-l";
193 let mut s = self.request(request)?;
194 let listing = wire::read_hex_block(&mut s).map_err(io_error(request))?;
195 Ok(parse_devices(&String::from_utf8_lossy(&listing)))
196 }
197
198 pub fn targets(&self) -> Result<Vec<Target>> {
202 Ok(self
203 .devices()?
204 .into_iter()
205 .map(|entry| {
206 let props = (entry.state == State::Device)
207 .then(|| self.device(&entry.serial).shell(PROP_QUERY).ok())
208 .flatten()
209 .filter(ShellOutput::success)
210 .map(|out| out.stdout_text());
211 Target::new(entry, props.as_deref())
212 })
213 .collect())
214 }
215
216 fn connect(&self) -> Result<TcpStream> {
218 let s = TcpStream::connect_timeout(&self.addr, CONNECT_TIMEOUT).map_err(|source| {
219 Error::ServerUnavailable {
220 addr: self.addr,
221 source,
222 }
223 })?;
224 let request = "connect";
225 s.set_nodelay(true).map_err(io_error(request))?;
226 s.set_read_timeout(Some(self.io_timeout))
227 .map_err(io_error(request))?;
228 s.set_write_timeout(Some(self.io_timeout))
229 .map_err(io_error(request))?;
230 Ok(s)
231 }
232
233 fn request(&self, service: &str) -> Result<TcpStream> {
235 let bytes = encoded(service)?;
236 let mut s = self.connect()?;
237 send_request(&mut s, service, &bytes)?;
238 Ok(s)
239 }
240
241 pub fn device(&self, serial: &str) -> Device {
243 Device {
244 server: self.clone(),
245 serial: serial.to_owned(),
246 }
247 }
248}
249
250#[derive(Debug, Clone)]
252pub struct Device {
253 server: AdbServer,
254 serial: String,
255}
256
257impl Device {
258 pub fn serial(&self) -> &str {
259 &self.serial
260 }
261
262 pub fn shell(&self, command: &str) -> Result<ShellOutput> {
265 let mut stream = self.shell_stream(command)?;
266 let _ = stream.close_stdin();
269 stream.finish()
270 }
271
272 pub fn shell_until(&self, command: &str, deadline: Instant) -> Result<ShellOutput> {
282 let begun = Instant::now();
283 let request = format!("shell,v2,raw:{command}");
284 let past = |request: String| Error::Io {
285 request,
286 source: io::Error::new(
287 ErrorKind::TimedOut,
288 format!(
289 "the command had not ended by its deadline, {} ms after it began",
290 deadline.saturating_duration_since(begun).as_millis()
291 ),
292 ),
293 };
294 if deadline <= begun {
295 return Err(past(request));
296 }
297 let mut stream = self.shell_stream(command)?;
298 let _ = stream.close_stdin();
301 let mut output = ShellOutput::default();
302 loop {
303 let left = deadline.saturating_duration_since(Instant::now());
304 if left.is_zero() {
305 return Err(past(request));
306 }
307 stream.set_read_timeout(Some(left))?;
308 match stream.read_packet() {
309 Ok(Some(ShellPacket::Stdout(data))) => output.stdout.extend_from_slice(&data),
310 Ok(Some(ShellPacket::Stderr(data))) => output.stderr.extend_from_slice(&data),
311 Ok(Some(ShellPacket::Exit(status))) => {
312 output.exit = Some(status);
313 return Ok(output);
314 }
315 Ok(None) => return Ok(output),
316 Err(error) if error.is_timeout() => return Err(past(request)),
319 Err(error) => return Err(error),
320 }
321 }
322 }
323
324 pub fn shell_stream(&self, command: &str) -> Result<ShellStream> {
328 self.shell_stream_with(command, Stdio::Raw)
329 }
330
331 pub fn shell_stream_with(&self, command: &str, stdio: Stdio) -> Result<ShellStream> {
334 let streams = match stdio {
335 Stdio::Raw => "raw",
336 Stdio::Pty => "pty",
337 };
338 let request = format!("shell,v2,{streams}:{command}");
339 let stream = self.service(&request)?;
340 Ok(ShellStream { stream, request })
341 }
342
343 pub fn push(&self, local: &Path, remote: &str, mode: u32) -> Result<()> {
348 let file = File::open(local).map_err(|source| Error::Local {
349 path: local.to_owned(),
350 source,
351 })?;
352 self.send(Source::File(local, file), remote, mode)
353 }
354
355 pub fn push_bytes(&self, data: &[u8], remote: &str, mode: u32) -> Result<()> {
358 self.send(Source::Bytes(data), remote, mode)
359 }
360
361 fn send(&self, source: Source<'_>, remote: &str, mode: u32) -> Result<()> {
364 let request = format!("push {remote}");
365 if remote.len() > SYNC_MAX_PATH {
366 return Err(Error::Invalid {
367 request,
368 detail: format!("remote paths are at most {SYNC_MAX_PATH} bytes"),
369 });
370 }
371 let mut s = self.service("sync:")?;
372 let mut buf = wire::sync_request(b"SEND", wire::send_arg(remote, mode).as_bytes());
373 let mut send_chunk = |buf: &mut Vec<u8>, chunk: &[u8]| {
374 wire::push_data_chunk(buf, chunk);
375 let sent = write_or_sync_fail(&mut s, buf, &request);
376 buf.clear();
377 sent
378 };
379 match source {
380 Source::Bytes(data) => {
381 for chunk in data.chunks(wire::SYNC_MAX_CHUNK) {
382 send_chunk(&mut buf, chunk)?;
383 }
384 }
385 Source::File(path, mut file) => {
386 let mut chunk = vec![0; wire::SYNC_MAX_CHUNK];
387 loop {
388 let n = read_full(&mut file, &mut chunk).map_err(|source| Error::Local {
389 path: path.to_owned(),
390 source,
391 })?;
392 if n == 0 {
393 break;
394 }
395 send_chunk(&mut buf, &chunk[..n])?;
396 }
397 }
398 }
399 let mtime = SystemTime::now()
400 .duration_since(UNIX_EPOCH)
401 .map_or(0, |d| d.as_secs() as u32);
402 wire::push_done(&mut buf, mtime);
403 write_or_sync_fail(&mut s, &buf, &request)?;
404 match wire::read_sync_status(&mut s).map_err(io_error(&request))? {
405 Ok(()) => {
406 let _ = s.write_all(b"QUIT\0\0\0\0");
408 Ok(())
409 }
410 Err(message) => Err(Error::Fail { request, message }),
411 }
412 }
413
414 pub fn forward(&self, host: &str, device: &str) -> Result<Option<u16>> {
417 let request = self.host_request(&format!("forward:{host};{device}"));
418 let mut s = self.server.request(&request)?;
419 expect_okay(&mut s, &request)?;
420 let rest = read_rest(&mut s, &request)?;
422 bound_port(&rest, &request)
423 }
424
425 pub fn forward_tcp(&self, device: &str) -> Result<u16> {
427 let request = self.host_request(&format!("forward:tcp:0;{device}"));
428 self.forward("tcp:0", device)?
429 .ok_or_else(|| Error::Protocol {
430 request,
431 detail: "the server bound no TCP port".into(),
432 })
433 }
434
435 pub fn remove_forward(&self, host: &str) -> Result<()> {
437 let request = self.host_request(&format!("killforward:{host}"));
438 let mut s = self.server.request(&request)?;
439 expect_okay(&mut s, &request)
440 }
441
442 pub fn forwards(&self) -> Result<Vec<Rule>> {
444 let request = self.host_request("list-forward");
445 let mut s = self.server.request(&request)?;
446 let listing = wire::read_hex_block(&mut s).map_err(io_error(&request))?;
447 Ok(wire::parse_forward_listing(
448 &String::from_utf8_lossy(&listing),
449 &self.serial,
450 ))
451 }
452
453 pub fn reverse(&self, device: &str, host: &str) -> Result<()> {
461 self.adbd_request(&format!("reverse:forward:{device};{host}"))
462 }
463
464 pub fn remove_reverse(&self, device: &str) -> Result<()> {
466 self.adbd_request(&format!("reverse:killforward:{device}"))
467 }
468
469 pub fn reverses(&self) -> Result<Vec<Rule>> {
471 let request = "reverse:list-forward";
472 let mut s = self.service(request)?;
474 let listing = wire::read_hex_block(&mut s).map_err(io_error(request))?;
475 Ok(wire::parse_reverse_listing(&String::from_utf8_lossy(
476 &listing,
477 )))
478 }
479
480 pub fn open_service(&self, service: &str) -> Result<TcpStream> {
485 let s = self.service(service)?;
486 s.set_read_timeout(None).map_err(io_error(service))?;
487 s.set_write_timeout(None).map_err(io_error(service))?;
488 Ok(s)
489 }
490
491 pub fn open_abstract(&self, name: &str) -> Result<TcpStream> {
493 self.open_service(&format!("localabstract:{name}"))
494 }
495
496 fn host_request(&self, request: &str) -> String {
498 format!("host-serial:{}:{request}", self.serial)
499 }
500
501 fn service(&self, service: &str) -> Result<TcpStream> {
505 let transport = format!("host:transport:{}", self.serial);
506 let switch = encoded(&transport)?;
507 let open = encoded(service)?;
508 let mut s = self.server.connect()?;
509 send_request(&mut s, &transport, &switch)?;
510 send_request(&mut s, service, &open)?;
511 Ok(s)
512 }
513
514 fn adbd_request(&self, request: &str) -> Result<()> {
517 let mut s = self.service(request)?;
518 expect_okay(&mut s, request)
519 }
520}
521
522fn write_or_sync_fail(s: &mut TcpStream, bytes: &[u8], request: &str) -> Result<()> {
526 let Err(write_error) = s.write_all(bytes) else {
527 return Ok(());
528 };
529 match wire::read_sync_status(s) {
530 Ok(Err(message)) => Err(Error::Fail {
531 request: request.to_owned(),
532 message,
533 }),
534 _ => Err(io_error(request)(write_error)),
535 }
536}
537
538#[derive(Debug)]
540pub struct ShellStream {
541 stream: TcpStream,
542 request: String,
543}
544
545impl ShellStream {
546 pub fn read_packet(&mut self) -> Result<Option<ShellPacket>> {
548 wire::read_shell_packet(&mut self.stream).map_err(io_error(&self.request))
549 }
550
551 pub fn write_stdin(&mut self, data: &[u8]) -> Result<()> {
553 self.stream
554 .write_all(&wire::shell_packet(shell_id::STDIN, data))
555 .map_err(io_error(&self.request))
556 }
557
558 pub fn close_stdin(&mut self) -> Result<()> {
560 self.stream
561 .write_all(&wire::shell_packet(shell_id::CLOSE_STDIN, b""))
562 .map_err(io_error(&self.request))
563 }
564
565 pub fn set_read_timeout(&mut self, timeout: Option<Duration>) -> Result<()> {
567 self.stream
568 .set_read_timeout(timeout)
569 .map_err(io_error(&self.request))
570 }
571
572 pub fn finish(mut self) -> Result<ShellOutput> {
575 wire::read_shell_output(&mut self.stream).map_err(io_error(&self.request))
576 }
577}
578
579#[cfg(test)]
580mod tests {
581 use super::wire::{shell_id, shell_packet};
582 use super::*;
583 use std::net::TcpListener;
584 use std::thread::{self, JoinHandle};
585
586 type Script = Box<dyn FnOnce(&mut TcpStream) + Send>;
587
588 struct FakeServer {
592 addr: SocketAddr,
593 handle: Option<JoinHandle<()>>,
594 }
595
596 impl FakeServer {
597 fn start(scripts: Vec<Script>) -> Self {
598 let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).unwrap();
599 listener.set_nonblocking(true).unwrap();
600 let addr = listener.local_addr().unwrap();
601 let handle = thread::spawn(move || {
602 for (i, script) in scripts.into_iter().enumerate() {
603 let deadline = Instant::now() + Duration::from_secs(5);
604 let mut s = loop {
605 match listener.accept() {
606 Ok((s, _)) => break s,
607 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
608 assert!(Instant::now() < deadline, "no connection for script {i}");
609 thread::sleep(Duration::from_millis(2));
610 }
611 Err(e) => panic!("accept: {e}"),
612 }
613 };
614 s.set_nonblocking(false).unwrap();
615 s.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
616 script(&mut s);
617 }
618 });
619 Self {
620 addr,
621 handle: Some(handle),
622 }
623 }
624
625 fn server(&self) -> AdbServer {
626 AdbServer::new(self.addr).with_io_timeout(Duration::from_secs(2))
627 }
628
629 fn finish(mut self) {
631 if let Err(panic) = self.handle.take().unwrap().join() {
632 std::panic::resume_unwind(panic);
633 }
634 }
635 }
636
637 fn expect_request(s: &mut TcpStream, want: &str) {
639 let body = wire::read_hex_block(s).unwrap();
640 assert_eq!(String::from_utf8_lossy(&body), want);
641 }
642
643 fn expect_bytes(s: &mut TcpStream, want: &[u8]) {
644 let mut got = vec![0; want.len()];
645 s.read_exact(&mut got).unwrap();
646 assert_eq!(got, want);
647 }
648
649 fn reply(s: &mut TcpStream, bytes: &[u8]) {
650 s.write_all(bytes).unwrap();
651 }
652
653 fn fail(message: &str) -> Vec<u8> {
654 format!("FAIL{:04x}{message}", message.len()).into_bytes()
655 }
656
657 fn block(body: &str) -> Vec<u8> {
658 format!("{:04x}{body}", body.len()).into_bytes()
659 }
660
661 fn switch_to(s: &mut TcpStream, serial: &str) {
663 expect_request(s, &format!("host:transport:{serial}"));
664 reply(s, b"OKAY");
665 }
666
667 const LISTING: &str = "emulator-5554 device product:sdk_gphone64_arm64 model:sdk_gphone64_arm64 device:emu64a transport_id:1\n\
668 R58M123ABC unauthorized usb:1-1 transport_id:2\n\
669 emulator-5556 offline transport_id:3\n\
670 0123456789ABCDEF no permissions (missing udev rules? user is in the plugdev group); see [http://developer.android.com/tools/device.html] usb:1-2 transport_id:4\n";
671
672 fn serve_listing(s: &mut TcpStream) {
673 expect_request(s, "host:devices-l");
674 reply(s, b"OKAY");
675 reply(s, &block(LISTING));
676 }
677
678 #[test]
679 fn the_server_port_comes_from_the_environment_value() {
680 assert_eq!(server_port(None), DEFAULT_SERVER_PORT);
681 assert_eq!(server_port(Some("")), DEFAULT_SERVER_PORT);
682 assert_eq!(server_port(Some("5038")), 5038);
683 assert_eq!(server_port(Some(" 5039 ")), 5039);
684 assert_eq!(server_port(Some("not a port")), DEFAULT_SERVER_PORT);
685 assert_eq!(server_port(Some("70000")), DEFAULT_SERVER_PORT);
686 }
687
688 #[test]
689 fn devices_keep_every_state() {
690 let fake = FakeServer::start(vec![Box::new(serve_listing)]);
691 let entries = fake.server().devices().unwrap();
692 fake.finish();
693 let states: Vec<_> = entries.iter().map(|e| e.state.clone()).collect();
694 assert_eq!(
695 states,
696 [
697 State::Device,
698 State::Unauthorized,
699 State::Offline,
700 State::NoPermissions(
701 "no permissions (missing udev rules? user is in the plugdev group); see [http://developer.android.com/tools/device.html]".into()
702 ),
703 ]
704 );
705 }
706
707 #[test]
709 fn an_unauthorized_device_is_listed_with_instructions() {
710 let fake = FakeServer::start(vec![
711 Box::new(serve_listing),
712 Box::new(|s| {
714 switch_to(s, "emulator-5554");
715 expect_request(s, &format!("shell,v2,raw:{PROP_QUERY}"));
716 reply(s, b"OKAY");
717 expect_bytes(s, &shell_packet(shell_id::CLOSE_STDIN, b""));
718 reply(
719 s,
720 &shell_packet(shell_id::STDOUT, b"36\n1\n\nsdk_gphone64_arm64\n"),
721 );
722 reply(s, &shell_packet(shell_id::EXIT, &[0]));
723 }),
724 ]);
725 let targets = fake.server().targets().unwrap();
726 fake.finish();
727
728 let serials: Vec<_> = targets.iter().map(|t| t.serial.as_str()).collect();
729 assert_eq!(
730 serials,
731 [
732 "emulator-5554",
733 "R58M123ABC",
734 "emulator-5556",
735 "0123456789ABCDEF"
736 ]
737 );
738 let online = &targets[0];
739 assert_eq!(online.api_level, Some(36));
740 assert!(online.emulator);
741 assert_eq!(online.advice(), None);
742
743 let unauthorized = &targets[1];
744 assert_eq!(unauthorized.state, State::Unauthorized);
745 assert_eq!(unauthorized.api_level, None);
746 assert!(!unauthorized.emulator);
747 let advice = unauthorized
748 .advice()
749 .expect("advice for an unauthorized target");
750 assert!(advice.contains("Allow USB debugging"), "{advice}");
751 assert!(advice.contains("on the device"), "{advice}");
752
753 assert_eq!(targets[2].state, State::Offline);
754 assert!(targets[2].advice().is_some());
755 assert!(targets[3].advice().unwrap().contains("udev"));
756 }
757
758 #[test]
759 fn a_target_whose_properties_cannot_be_read_stays_listed() {
760 let fake = FakeServer::start(vec![
761 Box::new(|s| {
762 expect_request(s, "host:devices-l");
763 reply(s, b"OKAY");
764 reply(s, &block("emulator-5554 device transport_id:1\n"));
765 }),
766 Box::new(|s| {
767 expect_request(s, "host:transport:emulator-5554");
768 reply(s, &fail("device offline"));
769 }),
770 ]);
771 let targets = fake.server().targets().unwrap();
772 fake.finish();
773 assert_eq!(targets.len(), 1);
774 assert_eq!(targets[0].state, State::Device);
775 assert_eq!(targets[0].api_level, None);
776 assert!(targets[0].emulator);
777 }
778
779 #[test]
780 fn shell_closes_stdin_and_splits_stdout_stderr_and_exit() {
781 let fake = FakeServer::start(vec![Box::new(|s| {
782 switch_to(s, "emulator-5554");
783 expect_request(s, "shell,v2,raw:echo out; echo err >&2; exit 3");
784 reply(s, b"OKAY");
785 expect_bytes(s, &shell_packet(shell_id::CLOSE_STDIN, b""));
786 reply(s, &shell_packet(shell_id::STDOUT, b"out\n"));
787 reply(s, &shell_packet(shell_id::STDERR, b"err\n"));
788 reply(s, &shell_packet(shell_id::EXIT, &[3]));
789 })]);
790 let out = fake
791 .server()
792 .device("emulator-5554")
793 .shell("echo out; echo err >&2; exit 3")
794 .unwrap();
795 fake.finish();
796 assert_eq!(out.stdout, b"out\n");
797 assert_eq!(out.stderr, b"err\n");
798 assert_eq!(out.exit, Some(3));
799 }
800
801 fn slow_launch(silent: Duration, answers: bool) -> Script {
806 Box::new(move |s| {
807 switch_to(s, "emulator-5554");
808 expect_request(s, "shell,v2,raw:am start -W -n app/.Main");
809 reply(s, b"OKAY");
810 expect_bytes(s, &shell_packet(shell_id::CLOSE_STDIN, b""));
811 reply(
812 s,
813 &shell_packet(shell_id::STDOUT, b"Starting: Intent { cmp=app/.Main }\n"),
814 );
815 thread::sleep(silent);
816 if answers {
817 let result = b"Status: ok\nLaunchState: COLD\nTotalTime: 600\nComplete\n";
818 let _ = s.write_all(&shell_packet(shell_id::STDOUT, result));
819 let _ = s.write_all(&shell_packet(shell_id::EXIT, &[0]));
820 }
821 })
822 }
823
824 #[test]
829 fn a_shell_command_silent_past_the_read_timeout_completes_within_its_deadline() {
830 let fake = FakeServer::start(vec![slow_launch(Duration::from_millis(600), true)]);
831 let device = AdbServer::new(fake.addr)
832 .with_io_timeout(Duration::from_millis(200))
833 .device("emulator-5554");
834 let out = device
835 .shell_until(
836 "am start -W -n app/.Main",
837 Instant::now() + Duration::from_secs(3),
838 )
839 .unwrap();
840 fake.finish();
841 assert!(out.stdout_text().contains("Status: ok"), "{out:?}");
842 assert_eq!(out.exit, Some(0));
843 }
844
845 #[test]
850 fn a_shell_command_still_silent_at_its_deadline_fails_with_a_timeout_at_the_deadline() {
851 let fake = FakeServer::start(vec![slow_launch(Duration::from_millis(1500), false)]);
852 let device = AdbServer::new(fake.addr)
853 .with_io_timeout(Duration::from_millis(200))
854 .device("emulator-5554");
855 let started = Instant::now();
856 let err = device
857 .shell_until(
858 "am start -W -n app/.Main",
859 started + Duration::from_millis(700),
860 )
861 .unwrap_err();
862 let elapsed = started.elapsed();
863 fake.finish();
864 assert!(err.is_timeout(), "{err:?}");
865 assert!(
866 (Duration::from_millis(650)..Duration::from_millis(1200)).contains(&elapsed),
867 "failed after {elapsed:?}: {err}"
868 );
869 let message = err.to_string();
870 assert!(
871 message.contains("deadline") && !message.contains("os error"),
872 "{message}"
873 );
874 }
875
876 #[test]
877 fn a_shell_stream_hands_over_packets_as_they_arrive() {
878 let fake = FakeServer::start(vec![Box::new(|s| {
879 switch_to(s, "emulator-5554");
880 expect_request(s, "shell,v2,raw:getevent -l");
881 reply(s, b"OKAY");
882 reply(s, &shell_packet(shell_id::STDOUT, b"add device 1\n"));
883 expect_bytes(s, &shell_packet(shell_id::STDIN, b"x"));
884 reply(s, &shell_packet(shell_id::STDERR, b"late\n"));
885 reply(s, &shell_packet(shell_id::EXIT, &[0]));
886 })]);
887 let mut stream = fake
888 .server()
889 .device("emulator-5554")
890 .shell_stream("getevent -l")
891 .unwrap();
892 assert_eq!(
893 stream.read_packet().unwrap(),
894 Some(ShellPacket::Stdout(b"add device 1\n".to_vec()))
895 );
896 stream.write_stdin(b"x").unwrap();
897 let rest = stream.finish().unwrap();
898 fake.finish();
899 assert_eq!(rest.stderr, b"late\n");
900 assert_eq!(rest.exit, Some(0));
901 }
902
903 #[test]
904 fn a_pty_stream_asks_adbd_for_a_terminal() {
905 let fake = FakeServer::start(vec![Box::new(|s| {
906 switch_to(s, "emulator-5554");
907 expect_request(s, "shell,v2,pty:getevent -l");
908 reply(s, b"OKAY");
909 reply(s, &shell_packet(shell_id::EXIT, &[0]));
910 })]);
911 let stream = fake
912 .server()
913 .device("emulator-5554")
914 .shell_stream_with("getevent -l", Stdio::Pty)
915 .unwrap();
916 assert_eq!(stream.finish().unwrap().exit, Some(0));
917 fake.finish();
918 }
919
920 #[test]
921 fn push_sends_send_data_done_and_quit() {
922 let data: Vec<u8> = (0..70_000u32).map(|i| (i % 251) as u8).collect();
923 let expected = data.clone();
924 let fake = FakeServer::start(vec![Box::new(move |s| {
925 switch_to(s, "emulator-5554");
926 expect_request(s, "sync:");
927 reply(s, b"OKAY");
928 let arg = b"/data/local/tmp/agent.jar,33188";
929 expect_bytes(s, &wire::sync_request(b"SEND", arg));
930 let mut received = Vec::new();
931 for len in [SYNC_MAX_CHUNK, expected.len() - SYNC_MAX_CHUNK] {
932 let mut header = [0u8; 8];
933 s.read_exact(&mut header).unwrap();
934 assert_eq!(&header[..4], b"DATA");
935 assert_eq!(
936 u32::from_le_bytes(header[4..].try_into().unwrap()) as usize,
937 len
938 );
939 let mut chunk = vec![0; len];
940 s.read_exact(&mut chunk).unwrap();
941 received.extend(chunk);
942 }
943 assert_eq!(received, expected);
944 let mut done = [0u8; 8];
945 s.read_exact(&mut done).unwrap();
946 assert_eq!(&done[..4], b"DONE");
947 reply(s, b"OKAY\0\0\0\0");
948 expect_bytes(s, b"QUIT\0\0\0\0");
949 })]);
950 fake.server()
951 .device("emulator-5554")
952 .push_bytes(&data, "/data/local/tmp/agent.jar", 0o644)
953 .unwrap();
954 fake.finish();
955 }
956
957 #[test]
958 fn a_push_adbd_refuses_reports_its_message() {
959 let fake = FakeServer::start(vec![Box::new(|s| {
960 switch_to(s, "emulator-5554");
961 expect_request(s, "sync:");
962 reply(s, b"OKAY");
963 expect_bytes(s, &wire::sync_request(b"SEND", b"/system/x,33188"));
964 let mut rest = [0u8; 8 + 1 + 8];
965 s.read_exact(&mut rest).unwrap();
966 let message = "couldn't create file: Read-only file system";
967 reply(
968 s,
969 &[
970 b"FAIL".as_slice(),
971 &(message.len() as u32).to_le_bytes(),
972 message.as_bytes(),
973 ]
974 .concat(),
975 );
976 })]);
977 let err = fake
978 .server()
979 .device("emulator-5554")
980 .push_bytes(b"x", "/system/x", 0o644)
981 .unwrap_err();
982 fake.finish();
983 assert_eq!(
984 err.fail_message(),
985 Some("couldn't create file: Read-only file system"),
986 "{err}"
987 );
988 }
989
990 #[test]
991 fn forward_reads_both_statuses_and_the_bound_port() {
992 let fake = FakeServer::start(vec![Box::new(|s| {
993 expect_request(
994 s,
995 "host-serial:emulator-5554:forward:tcp:0;localabstract:mobile-agent",
996 );
997 reply(s, b"OKAYOKAY");
998 reply(s, &block("40123"));
999 })]);
1000 let port = fake
1001 .server()
1002 .device("emulator-5554")
1003 .forward_tcp("localabstract:mobile-agent")
1004 .unwrap();
1005 fake.finish();
1006 assert_eq!(port, 40123);
1007 }
1008
1009 #[test]
1010 fn a_forward_without_a_tcp_host_side_has_no_port() {
1011 let fake = FakeServer::start(vec![Box::new(|s| {
1012 expect_request(
1013 s,
1014 "host-serial:emulator-5554:forward:localabstract:host-side;localabstract:agent",
1015 );
1016 reply(s, b"OKAYOKAY");
1017 })]);
1018 let port = fake
1019 .server()
1020 .device("emulator-5554")
1021 .forward("localabstract:host-side", "localabstract:agent")
1022 .unwrap();
1023 fake.finish();
1024 assert_eq!(port, None);
1025 }
1026
1027 #[test]
1028 fn a_forward_the_server_refuses_reports_its_message() {
1029 let fake = FakeServer::start(vec![Box::new(|s| {
1030 expect_request(s, "host-serial:emulator-5554:forward:tcp:1;tcp:2");
1031 reply(s, &fail("cannot bind listener: Permission denied"));
1032 })]);
1033 let err = fake
1034 .server()
1035 .device("emulator-5554")
1036 .forward("tcp:1", "tcp:2")
1037 .unwrap_err();
1038 fake.finish();
1039 assert_eq!(
1040 err.fail_message(),
1041 Some("cannot bind listener: Permission denied")
1042 );
1043 }
1044
1045 #[test]
1046 fn reverse_reads_adbds_own_status() {
1047 let fake = FakeServer::start(vec![Box::new(|s| {
1048 switch_to(s, "emulator-5554");
1049 expect_request(s, "reverse:forward:tcp:70000;tcp:1");
1050 reply(s, b"OKAY");
1051 reply(s, &fail("cannot bind listener: bad port number '70000'"));
1052 })]);
1053 let err = fake
1054 .server()
1055 .device("emulator-5554")
1056 .reverse("tcp:70000", "tcp:1")
1057 .unwrap_err();
1058 fake.finish();
1059 assert_eq!(
1060 err.fail_message(),
1061 Some("cannot bind listener: bad port number '70000'")
1062 );
1063 }
1064
1065 #[test]
1066 fn rules_are_listed_and_removed_with_serial_scoped_requests() {
1067 let fake = FakeServer::start(vec![
1068 Box::new(|s| {
1069 expect_request(s, "host-serial:emulator-5554:list-forward");
1070 reply(s, b"OKAY");
1071 reply(
1072 s,
1073 &block("emulator-5554 tcp:40123 localabstract:a\nemulator-5556 tcp:1 tcp:2\n"),
1074 );
1075 }),
1076 Box::new(|s| {
1077 expect_request(s, "host-serial:emulator-5554:killforward:tcp:40123");
1078 reply(s, b"OKAYOKAY");
1079 }),
1080 Box::new(|s| {
1081 switch_to(s, "emulator-5554");
1082 expect_request(s, "reverse:list-forward");
1083 reply(s, b"OKAY");
1084 reply(s, &block("host-16 localabstract:b tcp:5000\n"));
1085 }),
1086 Box::new(|s| {
1087 switch_to(s, "emulator-5554");
1088 expect_request(s, "reverse:killforward:localabstract:b");
1089 reply(s, b"OKAYOKAY");
1090 }),
1091 ]);
1092 let device = fake.server().device("emulator-5554");
1093 assert_eq!(
1094 device.forwards().unwrap(),
1095 [Rule {
1096 host: "tcp:40123".into(),
1097 device: "localabstract:a".into()
1098 }]
1099 );
1100 device.remove_forward("tcp:40123").unwrap();
1101 assert_eq!(
1102 device.reverses().unwrap(),
1103 [Rule {
1104 host: "tcp:5000".into(),
1105 device: "localabstract:b".into()
1106 }]
1107 );
1108 device.remove_reverse("localabstract:b").unwrap();
1109 fake.finish();
1110 }
1111
1112 #[test]
1113 fn a_service_stream_opens_through_the_transport() {
1114 let fake = FakeServer::start(vec![Box::new(|s| {
1115 switch_to(s, "emulator-5554");
1116 expect_request(s, "localabstract:mobile-agent");
1117 reply(s, b"OKAY");
1118 reply(s, b"hello");
1119 })]);
1120 let mut stream = fake
1121 .server()
1122 .device("emulator-5554")
1123 .open_abstract("mobile-agent")
1124 .unwrap();
1125 let mut hello = [0u8; 5];
1126 stream.read_exact(&mut hello).unwrap();
1127 fake.finish();
1128 assert_eq!(&hello, b"hello");
1129 assert_eq!(stream.read_timeout().unwrap(), None);
1130 }
1131
1132 #[test]
1133 fn a_service_stream_to_a_missing_socket_fails_with_closed() {
1134 let fake = FakeServer::start(vec![Box::new(|s| {
1135 switch_to(s, "emulator-5554");
1136 expect_request(s, "localabstract:nobody");
1137 reply(s, &fail("closed"));
1138 })]);
1139 let err = fake
1140 .server()
1141 .device("emulator-5554")
1142 .open_abstract("nobody")
1143 .unwrap_err();
1144 fake.finish();
1145 assert_eq!(err.fail_message(), Some("closed"), "{err}");
1146 }
1147
1148 #[test]
1149 fn no_server_is_an_actionable_error() {
1150 let addr = {
1151 let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).unwrap();
1152 listener.local_addr().unwrap()
1153 };
1154 let err = AdbServer::new(addr).devices().unwrap_err();
1155 assert!(matches!(err, Error::ServerUnavailable { .. }), "{err:?}");
1156 assert!(err.to_string().contains("adb start-server"), "{err}");
1157 }
1158
1159 #[test]
1160 fn a_silent_server_fails_at_the_io_deadline() {
1161 let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).unwrap();
1162 let addr = listener.local_addr().unwrap();
1163 let (done, wait) = std::sync::mpsc::channel::<()>();
1164 let holder = thread::spawn(move || {
1165 let (s, _) = listener.accept().unwrap();
1167 let _ = wait.recv_timeout(Duration::from_secs(5));
1168 drop(s);
1169 });
1170 let started = Instant::now();
1171 let err = AdbServer::new(addr)
1172 .with_io_timeout(Duration::from_millis(200))
1173 .devices()
1174 .unwrap_err();
1175 let elapsed = started.elapsed();
1176 done.send(()).unwrap();
1177 holder.join().unwrap();
1178 assert!(err.is_timeout(), "{err:?}");
1179 assert!(elapsed < Duration::from_secs(1), "took {elapsed:?}");
1180 }
1181
1182 #[test]
1183 fn an_oversized_request_is_refused_before_connecting() {
1184 let addr = SocketAddr::from((Ipv4Addr::LOCALHOST, 9));
1185 let command = "x".repeat(wire::MAX_REQUEST_LEN);
1186 let err = AdbServer::new(addr)
1187 .device("emulator-5554")
1188 .shell(&command)
1189 .unwrap_err();
1190 assert!(matches!(err, Error::Invalid { .. }), "{err:?}");
1191 }
1192}