• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! This library provides APIs for receiving, processing and replying to fastboot commands. To use
16 //! the library:
17 //!
18 //! 1. Provide a transport backend by implementing the `Transport` trait.
19 //!
20 //! ```
21 //!
22 //! struct FastbootTransport {}
23 //!
24 //! impl Transport<MyErrorType> for TestTransport {
25 //!     fn receive_packet(&mut self, out: &mut [u8]) -> Result<usize, TransportError> {
26 //!         todo!();
27 //!     }
28 //!
29 //!     fn send_packet(&mut self, packet: &[u8]) -> Result<(), TransportError> {
30 //!         todo!();
31 //!     }
32 //! }
33 //! ```
34 //!
35 //! 2. Provide a fastboot command backend by implementing the `FastbootImplementation` trait.
36 //!    i.e.
37 //!
38 //! ```
39 //!
40 //! struct FastbootCommand {}
41 //!
42 //! impl FastbootImplementation for FastbootTest {
43 //!     fn get_var(
44 //!         &mut self,
45 //!         var: &str,
46 //!         args: Split<char>,
47 //!         out: &mut [u8],
48 //!     ) -> CommandResult<usize> {
49 //!         todo!();
50 //!     }
51 //!
52 //!     ...
53 //! }
54 //!```
55 //!
56 //! 3. Construct a `Fastboot` object with a given download buffer. Pass the transport, command
57 //!    implementation and call the `run()` method:
58 //!
59 //! ```
60 //! let mut fastboot_impl: FastbootCommand = ...;
61 //! let mut transport: TestTransport = ...;
62 //! let download_buffer: &mut [u8] = ...;
63 //! let mut fastboot = Fastboot::new();
64 //! let result = run(&mut transport, &mut fastboot_impl, &[]);
65 //! ```
66 
67 #![cfg_attr(not(test), no_std)]
68 #![allow(async_fn_in_trait)]
69 
70 use core::{
71     ffi::CStr,
72     fmt::{Debug, Display, Formatter, Write},
73     str::{from_utf8, Split},
74 };
75 use gbl_async::{block_on, yield_now};
76 use liberror::{Error, Result};
77 use libutils::{snprintf, FormattedBytes};
78 
79 /// Local session module
80 pub mod local_session;
81 
82 /// Maximum packet size that can be accepted from the host.
83 ///
84 /// The transport layer may have its own size limits that reduce the packet size further.
85 pub const MAX_COMMAND_SIZE: usize = 4096;
86 /// Maximum packet size that will be sent to the host.
87 ///
88 /// The `fastboot` host tool originally had a 64-byte packet size max, but this was increased
89 /// to 256 in 2020, so any reasonably recent host binary should be able to support 256.
90 ///
91 /// The transport layer may have its own size limits that reduce the packet size further.
92 pub const MAX_RESPONSE_SIZE: usize = 256;
93 
94 /// Trait to provide the transport layer for a fastboot implementation.
95 ///
96 /// Fastboot supports these transports:
97 /// * USB
98 /// * TCP
99 /// * UDP
100 pub trait Transport {
101     /// Fetches the next fastboot packet into `out`.
102     ///
103     /// Returns the actual size of the packet on success.
104     ///
105     /// TODO(b/322540167): In the future, we may want to support using `[MaybeUninit<u8>]` as the
106     /// download buffer to avoid expensive initialization at the beginning. This would require an
107     /// interface where the implementation provides the buffer for us to copy instead of us.
receive_packet(&mut self, out: &mut [u8]) -> Result<usize>108     async fn receive_packet(&mut self, out: &mut [u8]) -> Result<usize>;
109 
110     /// Sends a fastboot packet.
111     ///
112     /// The method assumes `packet` is sent or at least copied to queue after it returns, where
113     /// the buffer can go out of scope without affecting anything.
send_packet(&mut self, packet: &[u8]) -> Result<()>114     async fn send_packet(&mut self, packet: &[u8]) -> Result<()>;
115 }
116 
117 /// For now, we hardcode the expected version, until we need to distinguish between multiple
118 /// versions.
119 const TCP_HANDSHAKE_MESSAGE: &[u8] = b"FB01";
120 
121 /// A trait representing a TCP stream reader/writer. Fastboot over TCP has additional handshake
122 /// process and uses a length-prefixed wire message format. It is recommended that caller
123 /// implements this trait instead of `Transport`, and uses the API `Fastboot::run_tcp_session()`
124 /// to perform fastboot over TCP. It internally handles handshake and wire message parsing.
125 pub trait TcpStream {
126     /// Reads to `out` for exactly `out.len()` number bytes from the TCP connection.
read_exact(&mut self, out: &mut [u8]) -> Result<()>127     async fn read_exact(&mut self, out: &mut [u8]) -> Result<()>;
128 
129     /// Sends exactly `data.len()` number bytes from `data` to the TCP connection.
write_exact(&mut self, data: &[u8]) -> Result<()>130     async fn write_exact(&mut self, data: &[u8]) -> Result<()>;
131 }
132 
133 /// Implements [Transport] on a [TcpStream].
134 pub struct TcpTransport<'a, T: TcpStream>(&'a mut T);
135 
136 impl<'a, T: TcpStream> TcpTransport<'a, T> {
137     /// Creates an instance from a newly connected TcpStream and performs handshake.
new_and_handshake(tcp_stream: &'a mut T) -> Result<Self>138     pub fn new_and_handshake(tcp_stream: &'a mut T) -> Result<Self> {
139         let mut handshake = [0u8; 4];
140         block_on(tcp_stream.write_exact(TCP_HANDSHAKE_MESSAGE))?;
141         block_on(tcp_stream.read_exact(&mut handshake[..]))?;
142         match handshake == *TCP_HANDSHAKE_MESSAGE {
143             true => Ok(Self(tcp_stream)),
144             _ => Err(Error::InvalidHandshake),
145         }
146     }
147 }
148 
149 impl<'a, T: TcpStream> Transport for TcpTransport<'a, T> {
receive_packet(&mut self, out: &mut [u8]) -> Result<usize>150     async fn receive_packet(&mut self, out: &mut [u8]) -> Result<usize> {
151         let mut length_prefix = [0u8; 8];
152         self.0.read_exact(&mut length_prefix[..]).await?;
153         let packet_size: usize = u64::from_be_bytes(length_prefix).try_into()?;
154         match out.len() < packet_size {
155             true => Err(Error::InvalidInput),
156             _ => {
157                 self.0.read_exact(&mut out[..packet_size]).await?;
158                 Ok(packet_size)
159             }
160         }
161     }
162 
send_packet(&mut self, packet: &[u8]) -> Result<()>163     async fn send_packet(&mut self, packet: &[u8]) -> Result<()> {
164         self.0.write_exact(&mut u64::try_from(packet.len())?.to_be_bytes()[..]).await?;
165         self.0.write_exact(packet).await
166     }
167 }
168 
169 const COMMAND_ERROR_LENGTH: usize = MAX_RESPONSE_SIZE - 4;
170 
171 /// `CommandError` is the return error type for methods in trait `FastbootImplementation` when
172 /// they fail. It will be converted into string and sent as fastboot error message "FAIL<string>".
173 ///
174 /// Any type that implements `Display` trait can be converted into it. However, because fastboot
175 /// response message is limited to `MAX_RESPONSE_SIZE`. If the final displayed string length
176 /// exceeds it, the rest of the content is ignored.
177 pub struct CommandError(FormattedBytes<[u8; COMMAND_ERROR_LENGTH]>);
178 
179 impl CommandError {
180     /// Converts to string.
to_str(&self) -> &str181     pub fn to_str(&self) -> &str {
182         self.0.to_str()
183     }
184 
185     /// Clones the error.
clone(&self) -> Self186     pub fn clone(&self) -> Self {
187         self.to_str().into()
188     }
189 }
190 
191 impl Debug for CommandError {
fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result192     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
193         write!(f, "{}", self.to_str())
194     }
195 }
196 
197 impl<T: Display> From<T> for CommandError {
from(val: T) -> Self198     fn from(val: T) -> Self {
199         let mut res = CommandError(FormattedBytes::new([0u8; COMMAND_ERROR_LENGTH]));
200         write!(res.0, "{}", val).unwrap();
201         res
202     }
203 }
204 
205 /// Type alias for Result that wraps a CommandError
206 pub type CommandResult<T> = core::result::Result<T, CommandError>;
207 
208 /// Fastboot reboot mode
209 #[derive(Debug, Copy, Clone, PartialEq)]
210 pub enum RebootMode {
211     /// "fastboot reboot". Normal reboot.
212     Normal,
213     /// "fastboot reboot-bootloader". Reboot to bootloader.
214     Bootloader,
215     /// "fastboot reboot-fastboot". Reboot to userspace fastboot.
216     Fastboot,
217     /// "fastboot reboot-recovery". Reboot to recovery.
218     Recovery,
219 }
220 
221 /// Implementation for Fastboot command backends.
222 pub trait FastbootImplementation {
223     /// Backend for `fastboot getvar ...`
224     ///
225     /// Gets the value of a variable specified by name and configuration represented by list of
226     /// additional arguments in `args`.
227     ///
228     /// Variable `max-download-size`, `version` are reserved by the library.
229     ///
230     /// # Args
231     ///
232     /// * `var`: Name of the variable.
233     /// * `args`: Additional arguments.
234     /// * `out`: Output buffer for storing the variable value.
235     /// * `responder`: An instance of `InfoSender`.
236     ///
237     /// TODO(b/322540167): Figure out other reserved variables.
get_var( &mut self, var: &CStr, args: impl Iterator<Item = &'_ CStr> + Clone, out: &mut [u8], responder: impl InfoSender, ) -> CommandResult<usize>238     async fn get_var(
239         &mut self,
240         var: &CStr,
241         args: impl Iterator<Item = &'_ CStr> + Clone,
242         out: &mut [u8],
243         responder: impl InfoSender,
244     ) -> CommandResult<usize>;
245 
246     /// A helper API for getting the value of a fastboot variable and decoding it into string.
get_var_as_str<'s>( &mut self, var: &CStr, args: impl Iterator<Item = &'_ CStr> + Clone, responder: impl InfoSender, out: &'s mut [u8], ) -> CommandResult<&'s str>247     async fn get_var_as_str<'s>(
248         &mut self,
249         var: &CStr,
250         args: impl Iterator<Item = &'_ CStr> + Clone,
251         responder: impl InfoSender,
252         out: &'s mut [u8],
253     ) -> CommandResult<&'s str> {
254         let size = self.get_var(var, args, out, responder).await?;
255         Ok(from_utf8(out.get(..size).ok_or("Invalid variable size")?)
256             .map_err(|_| "Value is not string")?)
257     }
258 
259     /// Backend for `fastboot getvar all`.
260     ///
261     /// Iterates all combinations of fastboot variable, configurations and values that need to be
262     /// included in the response to `fastboot getvar all`.
263     ///
264     /// # Args
265     ///
266     /// * `responder`: An implementation VarInfoSender. Implementation should call
267     ///   `VarInfoSender::send` for all combinations of Fastboot variable/argument/value that needs
268     ///   to be included in the response to `fastboot getvarl all`:
269     ///
270     ///   async fn get_var_all(&mut self, f: F, resp: impl VarInfoSender)
271     ///     -> CommandResult<()> {
272     ///       resp.send("partition-size", &["boot_a"], /* size of boot_a */).await?;
273     ///       resp.send("partition-size", &["boot_b"], /* size of boot_b */).await?;
274     ///       resp.send("partition-size", &["init_boot_a"], /* size of init_boot_a */).await?;
275     ///       resp.send("partition-size", &["init_boot_b"], /* size of init_boot_b */).await?;
276     ///       Ok(())
277     ///   }
278     ///
279     ///   will generates the following outputs for `fastboot getvar all`:
280     ///
281     ///   ...
282     ///   (bootloader) partition-size:boot_a: <size of boot_a>
283     ///   (bootloader) partition-size:boot_b: <size of boot_b>
284     ///   (bootloader) partition-size:init_boot_a: <size of init_boot_a>
285     ///   (bootloader) partition-size:init_boot_b: <size of init_boot_b>
286     ///   ...
287     ///
288     /// TODO(b/322540167): This and `get_var()` contain duplicated logic. Investigate if there can
289     /// be better solutions for doing the combination traversal.
get_var_all(&mut self, responder: impl VarInfoSender) -> CommandResult<()>290     async fn get_var_all(&mut self, responder: impl VarInfoSender) -> CommandResult<()>;
291 
292     /// Backend for getting download buffer
get_download_buffer(&mut self) -> &mut [u8]293     async fn get_download_buffer(&mut self) -> &mut [u8];
294 
295     /// Called when a download is completed.
download_complete( &mut self, download_size: usize, responder: impl InfoSender, ) -> CommandResult<()>296     async fn download_complete(
297         &mut self,
298         download_size: usize,
299         responder: impl InfoSender,
300     ) -> CommandResult<()>;
301 
302     /// Backend for `fastboot flash ...`
303     ///
304     /// # Args
305     ///
306     /// * `part`: Name of the partition.
307     /// * `responder`: An instance of `InfoSender`.
flash(&mut self, part: &str, responder: impl InfoSender) -> CommandResult<()>308     async fn flash(&mut self, part: &str, responder: impl InfoSender) -> CommandResult<()>;
309 
310     /// Backend for `fastboot erase ...`
311     ///
312     /// # Args
313     ///
314     /// * `part`: Name of the partition.
315     /// * `responder`: An instance of `InfoSender`.
erase(&mut self, part: &str, responder: impl InfoSender) -> CommandResult<()>316     async fn erase(&mut self, part: &str, responder: impl InfoSender) -> CommandResult<()>;
317 
318     /// Backend for `fastboot get_staged ...`
319     ///
320     /// # Args
321     ///
322     /// * `responder`: An instance of `UploadBuilder + InfoSender` for initiating and uploading
323     ///   data. For example:
324     ///
325     ///   ```
326     ///   async fn upload(
327     ///       &mut self,
328     ///       responder: impl UploadBuilder + InfoSender,
329     ///   ) -> CommandResult<()> {
330     ///       let data = ..;
331     ///       // Sends a total of 1024 bytes data.
332     ///       responder.send_info("About to upload...").await?;
333     ///       let mut uploader = responder.initiate_upload(1024).await?;
334     ///       // Can upload in multiple batches.
335     ///       uploader.upload(&data[..512]).await?;
336     ///       uploader.upload(&data[512..]).await?;
337     ///       Ok(())
338     ///   }
339     ///   ```
340     ///
341     ///   If implementation fails to upload enough, or attempts to upload more than expected data
342     ///   with `Uploader::upload()`, an error will be returned.
upload(&mut self, responder: impl UploadBuilder + InfoSender) -> CommandResult<()>343     async fn upload(&mut self, responder: impl UploadBuilder + InfoSender) -> CommandResult<()>;
344 
345     /// Backend for `fastboot fetch ...`
346     ///
347     /// # Args
348     ///
349     /// * `part`: The partition name.
350     /// * `offset`: The offset into the partition for upload.
351     /// * `size`: The number of bytes to upload.
352     /// * `responder`: An instance of `UploadBuilder + InfoSender` for initiating and uploading data.
fetch( &mut self, part: &str, offset: u64, size: u64, responder: impl UploadBuilder + InfoSender, ) -> CommandResult<()>353     async fn fetch(
354         &mut self,
355         part: &str,
356         offset: u64,
357         size: u64,
358         responder: impl UploadBuilder + InfoSender,
359     ) -> CommandResult<()>;
360 
361     /// Backend for `fastboot reboot/reboot-bootloader/reboot-fastboot/reboot-recovery`
362     ///
363     /// # Args
364     ///
365     /// * `mode`: An `RebootMode` specifying the reboot mode.
366     /// * `responder`: An instance of `InfoSender + OkaySender`. Implementation should call
367     ///   `responder.send_okay("")` right before reboot to notify the remote host that the
368     ///   operation is successful.
369     ///
370     /// # Returns
371     ///
372     /// * The method is not expected to return if reboot is successful.
373     /// * Returns `Err(e)` on error.
reboot( &mut self, mode: RebootMode, responder: impl InfoSender + OkaySender, ) -> CommandError374     async fn reboot(
375         &mut self,
376         mode: RebootMode,
377         responder: impl InfoSender + OkaySender,
378     ) -> CommandError;
379 
380     /// Method for handling `fastboot continue` clean up.
381     ///
382     /// `run()` and `run_tcp_session()` exit after receiving `fastboot continue.` The method is for
383     /// implementation to perform necessary clean up.
384     ///
385     /// # Args
386     ///
387     /// * `responder`: An instance of `InfoSender`.
388     async fn r#continue(&mut self, responder: impl InfoSender) -> CommandResult<()>;
389 
390     /// Backend for `fastboot set_active`.
set_active(&mut self, slot: &str, responder: impl InfoSender) -> CommandResult<()>391     async fn set_active(&mut self, slot: &str, responder: impl InfoSender) -> CommandResult<()>;
392 
393     /// Backend for `fastboot boot`
394     ///
395     /// # Args
396     ///
397     /// * `responder`: An instance of `InfoSender + OkaySender`. Implementation should call
398     ///   `responder.send_okay("")` right before boot to notify the remote host that the
399     ///   operation is successful.
400     ///
401     /// # Returns
402     ///
403     /// * The method is always return OK to let fastboot continue.
404     /// * Returns `Err(e)` on error.
boot(&mut self, responder: impl InfoSender + OkaySender) -> CommandResult<()>405     async fn boot(&mut self, responder: impl InfoSender + OkaySender) -> CommandResult<()>;
406 
407     /// Backend for `fastboot oem ...`.
408     ///
409     /// # Args
410     ///
411     /// * `cmd`: The OEM command string that comes after "oem ".
412     /// * `responder`: An instance of `InfoSender`.
413     /// * `res`: The responder buffer. Upon success, implementation can use the buffer to
414     ///   construct a valid UTF8 string which will be sent as "OKAY<string>"
415     ///
416     /// # Returns
417     ///
418     /// On success, returns the portion of `res` used by the construction of string message.
oem<'a>( &mut self, cmd: &str, responder: impl InfoSender, res: &'a mut [u8], ) -> CommandResult<&'a [u8]>419     async fn oem<'a>(
420         &mut self,
421         cmd: &str,
422         responder: impl InfoSender,
423         res: &'a mut [u8],
424     ) -> CommandResult<&'a [u8]>;
425 
426     // TODO(b/322540167): Add methods for other commands.
427 }
428 
429 /// An internal convenient macro helper for `fastboot_okay`, `fastboot_fail` and `fastboot_info`.
430 macro_rules! fastboot_msg {
431     ( $arr:expr, $msg_type:expr, $( $x:expr ),* $(,)? ) => {
432         {
433             let mut formatted_bytes = FormattedBytes::new(&mut $arr[..]);
434             write!(formatted_bytes, $msg_type).unwrap();
435             write!(formatted_bytes, $($x,)*).unwrap();
436             let size = formatted_bytes.size();
437             &mut $arr[..size]
438         }
439     };
440 }
441 
442 /// An internal convenient macro that constructs a formatted fastboot OKAY message.
443 macro_rules! fastboot_okay {
444     ( $arr:expr, $( $x:expr ),* $(,)?) => { fastboot_msg!($arr, "OKAY", $($x,)*) };
445 }
446 
447 /// An internal convenient macro that constructs a formatted fastboot FAIL message.
448 macro_rules! fastboot_fail {
449     ( $arr:expr, $( $x:expr ),* $(,)?) => { fastboot_msg!($arr, "FAIL", $($x,)*) };
450 }
451 
452 /// `VarInfoSender` provide an interface for sending variable/args/value combination during the
453 /// processing of `fastboot getvar all`
454 pub trait VarInfoSender {
455     /// Send a combination of variable name, arguments and value.
456     ///
457     /// The method sends a fastboot message "INFO<var>:<args>:<val>" to the host.
458     ///
459     /// # Args
460     ///
461     /// * `name`: Name of the fastboot variable.
462     /// * `args`: An iterator to additional arguments.
463     /// * `val`: Value of the variable.
send_var_info( &mut self, name: &str, args: impl IntoIterator<Item = &'_ str>, val: &str, ) -> Result<()>464     async fn send_var_info(
465         &mut self,
466         name: &str,
467         args: impl IntoIterator<Item = &'_ str>,
468         val: &str,
469     ) -> Result<()>;
470 }
471 
472 /// Provides an API for sending fastboot INFO messages.
473 pub trait InfoSender {
474     /// Sends formatted INFO message.
475     ///
476     /// # Args:
477     ///
478     /// * `cb`: A closure provided by the caller for constructing the formatted messagae.
send_formatted_info<F: FnOnce(&mut dyn Write)>(&mut self, cb: F) -> Result<()>479     async fn send_formatted_info<F: FnOnce(&mut dyn Write)>(&mut self, cb: F) -> Result<()>;
480 
481     /// Sends a Fastboot "INFO<`msg`>" packet.
send_info(&mut self, msg: &str) -> Result<()>482     async fn send_info(&mut self, msg: &str) -> Result<()> {
483         self.send_formatted_info(|w| write!(w, "{}", msg).unwrap()).await
484     }
485 }
486 
487 /// Provides an API for sending fastboot OKAY messages.
488 pub trait OkaySender {
489     /// Sends formatted Okay message.
490     ///
491     /// # Args:
492     ///
493     /// * `cb`: A closure provided by the caller for constructing the formatted messagae.
send_formatted_okay<F: FnOnce(&mut dyn Write)>(self, cb: F) -> Result<()>494     async fn send_formatted_okay<F: FnOnce(&mut dyn Write)>(self, cb: F) -> Result<()>;
495 
496     /// Sends a fastboot OKAY<msg> packet. `Self` is consumed.
send_okay(self, msg: &str) -> Result<()> where Self: Sized,497     async fn send_okay(self, msg: &str) -> Result<()>
498     where
499         Self: Sized,
500     {
501         self.send_formatted_okay(|w| write!(w, "{}", msg).unwrap()).await
502     }
503 }
504 
505 /// `UploadBuilder` provides API for initiating a fastboot upload.
506 pub trait UploadBuilder {
507     /// Starts the upload.
508     ///
509     /// In a real fastboot context, the method should send `DATA0xXXXXXXXX` to the remote host to
510     /// start the download. An `Uploader` implementation should be returned for uploading payload.
initiate_upload(self, data_size: u64) -> Result<impl Uploader>511     async fn initiate_upload(self, data_size: u64) -> Result<impl Uploader>;
512 }
513 
514 /// `UploadBuilder` provides API for uploading payload.
515 pub trait Uploader {
516     /// Uploads data to the Fastboot host.
upload(&mut self, data: &[u8]) -> Result<()>517     async fn upload(&mut self, data: &[u8]) -> Result<()>;
518 }
519 
520 /// `Responder` implements APIs for fastboot backend to send fastboot messages and uploading data.
521 struct Responder<'a, T: Transport> {
522     buffer: [u8; MAX_RESPONSE_SIZE],
523     transport: &'a mut T,
524     transport_error: Result<()>,
525     remaining_upload: u64,
526 }
527 
528 impl<'a, T: Transport> Responder<'a, T> {
new(transport: &'a mut T) -> Self529     fn new(transport: &'a mut T) -> Self {
530         Self {
531             buffer: [0u8; MAX_RESPONSE_SIZE],
532             transport,
533             transport_error: Ok(()),
534             remaining_upload: 0,
535         }
536     }
537 
538     /// A helper for sending a fastboot message in the buffer.
send_buffer(&mut self, size: usize) -> Result<()>539     async fn send_buffer(&mut self, size: usize) -> Result<()> {
540         self.transport_error?;
541         assert!(size < self.buffer.len());
542         self.transport_error = self.transport.send_packet(&self.buffer[..size]).await;
543         Ok(self.transport_error?)
544     }
545 
546     /// Helper for sending a formatted fastboot message.
547     ///
548     /// # Args:
549     ///
550     /// * `cb`: A closure provided by the caller for constructing the formatted messagae.
send_formatted_msg<F: FnOnce(&mut dyn Write)>( &mut self, msg_type: &str, cb: F, ) -> Result<()>551     async fn send_formatted_msg<F: FnOnce(&mut dyn Write)>(
552         &mut self,
553         msg_type: &str,
554         cb: F,
555     ) -> Result<()> {
556         let mut formatted_bytes = FormattedBytes::new(&mut self.buffer);
557         write!(formatted_bytes, "{}", msg_type).unwrap();
558         cb(&mut formatted_bytes);
559         let size = formatted_bytes.size();
560         self.send_buffer(size).await
561     }
562 
563     /// Sends a fastboot DATA message.
send_data_message(&mut self, data_size: u64) -> Result<()>564     async fn send_data_message(&mut self, data_size: u64) -> Result<()> {
565         self.send_formatted_msg("DATA", |v| write!(v, "{:08x}", data_size).unwrap()).await
566     }
567 }
568 
569 impl<'a, T: Transport> VarInfoSender for &mut Responder<'a, T> {
send_var_info( &mut self, name: &str, args: impl IntoIterator<Item = &'_ str>, val: &str, ) -> Result<()>570     async fn send_var_info(
571         &mut self,
572         name: &str,
573         args: impl IntoIterator<Item = &'_ str>,
574         val: &str,
575     ) -> Result<()> {
576         // Sends a "INFO<var>:<':'-separated args>:<val>" packet to the host.
577         Ok(self
578             .send_formatted_msg("INFO", |v| {
579                 write!(v, "{}", name).unwrap();
580                 args.into_iter().for_each(|arg| write!(v, ":{}", arg).unwrap());
581                 write!(v, ": {}", val).unwrap();
582             })
583             .await?)
584     }
585 }
586 
587 /// An internal convenient macro that sends a formatted fastboot OKAY message via a `Responder`
588 macro_rules! reply_okay {
589     ( $resp:expr, $( $x:expr ),* $(,)?) => {
590         {
591             let len = fastboot_okay!($resp.buffer, $($x,)*).len();
592             $resp.send_buffer(len).await
593         }
594     };
595 }
596 
597 /// An internal convenient macro that sends a formatted fastboot FAIL message via a `Responder`
598 macro_rules! reply_fail {
599     ( $resp:expr, $( $x:expr ),* $(,)?) => {
600         {
601             let len = fastboot_fail!($resp.buffer, $($x,)*).len();
602             $resp.send_buffer(len).await
603         }
604     };
605 }
606 
607 impl<T: Transport> InfoSender for &mut Responder<'_, T> {
send_formatted_info<F: FnOnce(&mut dyn Write)>(&mut self, cb: F) -> Result<()>608     async fn send_formatted_info<F: FnOnce(&mut dyn Write)>(&mut self, cb: F) -> Result<()> {
609         Ok(self.send_formatted_msg("INFO", cb).await?)
610     }
611 }
612 
613 impl<T: Transport> OkaySender for &mut Responder<'_, T> {
send_formatted_okay<F: FnOnce(&mut dyn Write)>(self, cb: F) -> Result<()>614     async fn send_formatted_okay<F: FnOnce(&mut dyn Write)>(self, cb: F) -> Result<()> {
615         Ok(self.send_formatted_msg("OKAY", cb).await?)
616     }
617 }
618 
619 impl<'a, T: Transport> UploadBuilder for &mut Responder<'a, T> {
initiate_upload(self, data_size: u64) -> Result<impl Uploader>620     async fn initiate_upload(self, data_size: u64) -> Result<impl Uploader> {
621         self.send_data_message(data_size).await?;
622         self.remaining_upload = data_size;
623         Ok(self)
624     }
625 }
626 
627 impl<'a, T: Transport> Uploader for &mut Responder<'a, T> {
628     /// Uploads data. Returns error if accumulative amount exceeds `data_size` passed to
629     /// `UploadBuilder::start()`.
upload(&mut self, data: &[u8]) -> Result<()>630     async fn upload(&mut self, data: &[u8]) -> Result<()> {
631         self.transport_error?;
632         self.remaining_upload = self
633             .remaining_upload
634             .checked_sub(data.len().try_into().map_err(|_| "")?)
635             .ok_or(Error::Other(Some("Invalid size of upload data")))?;
636         self.transport_error = self.transport.send_packet(data).await;
637         Ok(())
638     }
639 }
640 
641 pub mod test_utils {
642     //! Test utilities to help users of this library write unit tests.
643 
644     use crate::{InfoSender, UploadBuilder, Uploader};
645     use core::fmt::Write;
646     use liberror::Error;
647 
648     /// A test implementation of `UploadBuilder` for unittesting
649     /// `FastbootImplementation::upload()`.
650     ///
651     /// The test uploader simply uploads to a user provided buffer.
652     pub struct TestUploadBuilder<'a>(pub &'a mut [u8]);
653 
654     impl<'a> UploadBuilder for TestUploadBuilder<'a> {
initiate_upload(self, _: u64) -> Result<impl Uploader, Error>655         async fn initiate_upload(self, _: u64) -> Result<impl Uploader, Error> {
656             Ok(TestUploader(0, self.0))
657         }
658     }
659 
660     impl<'a> InfoSender for TestUploadBuilder<'a> {
send_formatted_info<F: FnOnce(&mut dyn Write)>( &mut self, _: F, ) -> Result<(), Error>661         async fn send_formatted_info<F: FnOnce(&mut dyn Write)>(
662             &mut self,
663             _: F,
664         ) -> Result<(), Error> {
665             // Not needed currently.
666             Ok(())
667         }
668     }
669 
670     // (Bytes sent, upload buffer)
671     struct TestUploader<'a>(usize, &'a mut [u8]);
672 
673     impl Uploader for TestUploader<'_> {
upload(&mut self, data: &[u8]) -> Result<(), Error>674         async fn upload(&mut self, data: &[u8]) -> Result<(), Error> {
675             self.1[self.0..][..data.len()].clone_from_slice(data);
676             self.0 = self.0.checked_add(data.len()).unwrap();
677             Ok(())
678         }
679     }
680 }
681 
682 const MAX_DOWNLOAD_SIZE_NAME: &'static str = "max-download-size";
683 
684 /// Converts a null-terminated command line string where arguments are separated by ':' into an
685 /// iterator of individual argument as CStr.
cmd_to_c_string_args(cmd: &mut [u8]) -> impl Iterator<Item = &CStr> + Clone686 fn cmd_to_c_string_args(cmd: &mut [u8]) -> impl Iterator<Item = &CStr> + Clone {
687     let end = cmd.iter().position(|v| *v == 0).unwrap();
688     // Replace ':' with NULL.
689     cmd.iter_mut().filter(|v| **v == b':').for_each(|v| *v = 0);
690     cmd[..end + 1].split_inclusive(|v| *v == 0).map(|v| CStr::from_bytes_until_nul(v).unwrap())
691 }
692 
693 /// Helper for handling "fastboot getvar ..."
get_var( cmd: &mut [u8], transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>694 async fn get_var(
695     cmd: &mut [u8],
696     transport: &mut impl Transport,
697     fb_impl: &mut impl FastbootImplementation,
698 ) -> Result<()> {
699     let mut resp = Responder::new(transport);
700     let mut args = cmd_to_c_string_args(cmd).skip(1);
701     let Some(var) = args.next() else {
702         return reply_fail!(resp, "Missing variable");
703     };
704 
705     match var.to_str()? {
706         "all" => return get_var_all(transport, fb_impl).await,
707         MAX_DOWNLOAD_SIZE_NAME => {
708             return reply_okay!(resp, "{:#x}", fb_impl.get_download_buffer().await.len());
709         }
710         _ => {
711             let mut val = [0u8; MAX_RESPONSE_SIZE];
712             match fb_impl.get_var_as_str(var, args, &mut resp, &mut val[..]).await {
713                 Ok(s) => reply_okay!(resp, "{}", s),
714                 Err(e) => reply_fail!(resp, "{}", e.to_str()),
715             }
716         }
717     }
718 }
719 
720 /// A wrapper of `get_var_all()` that first iterates reserved variables.
get_var_all_with_native( fb_impl: &mut impl FastbootImplementation, mut sender: impl VarInfoSender, ) -> CommandResult<()>721 async fn get_var_all_with_native(
722     fb_impl: &mut impl FastbootImplementation,
723     mut sender: impl VarInfoSender,
724 ) -> CommandResult<()> {
725     // Process the built-in MAX_DOWNLOAD_SIZE_NAME variable.
726     let mut size_str = [0u8; 32];
727     let size_str = snprintf!(size_str, "{:#x}", fb_impl.get_download_buffer().await.len());
728     sender.send_var_info(MAX_DOWNLOAD_SIZE_NAME, [], size_str).await?;
729     fb_impl.get_var_all(sender).await
730 }
731 
732 /// Method for handling "fastboot getvar all"
get_var_all( transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>733 async fn get_var_all(
734     transport: &mut impl Transport,
735     fb_impl: &mut impl FastbootImplementation,
736 ) -> Result<()> {
737     let mut resp = Responder::new(transport);
738     // Don't allow custom INFO messages because variable values are sent as INFO messages.
739     let get_res = get_var_all_with_native(fb_impl, &mut resp).await;
740     match get_res {
741         Ok(()) => reply_okay!(resp, ""),
742         Err(e) => reply_fail!(resp, "{}", e.to_str()),
743     }
744 }
745 
746 /// Helper for handling "fastboot download:...".
download( mut args: Split<'_, char>, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>747 async fn download(
748     mut args: Split<'_, char>,
749     transport: &mut impl Transport,
750     fb_impl: &mut impl FastbootImplementation,
751 ) -> Result<()> {
752     let mut resp = Responder::new(transport);
753     let total_download_size = match (|| -> CommandResult<usize> {
754         usize::try_from(next_arg_u64(&mut args)?.ok_or("Not enough argument")?)
755             .map_err(|_| "Download size overflow".into())
756     })() {
757         Err(e) => return reply_fail!(resp, "{}", e.to_str()),
758         Ok(v) => v,
759     };
760     let download_buffer = &mut fb_impl.get_download_buffer().await;
761     if total_download_size > download_buffer.len() {
762         return reply_fail!(resp, "Download size is too big");
763     } else if total_download_size == 0 {
764         return reply_fail!(resp, "Zero download size");
765     }
766 
767     // Starts the download
768     let download_buffer = &mut download_buffer[..total_download_size];
769     // `total_download_size` is parsed from `next_arg_u64` and thus should fit into u64.
770     resp.send_data_message(u64::try_from(total_download_size).unwrap()).await?;
771     let mut downloaded = 0;
772     while downloaded < total_download_size {
773         let (_, remains) = &mut download_buffer.split_at_mut(downloaded);
774         match resp.transport.receive_packet(remains).await? {
775             0 => yield_now().await,
776             v => match downloaded.checked_add(v) {
777                 Some(v) if v > total_download_size => {
778                     return reply_fail!(resp, "More data received then expected");
779                 }
780                 Some(v) => downloaded = v,
781                 _ => return Err(Error::Other(Some("Invalid read size from transport"))),
782             },
783         };
784     }
785     match fb_impl.download_complete(downloaded, &mut resp).await {
786         Ok(()) => reply_okay!(resp, ""),
787         Err(e) => reply_fail!(resp, "{}", e.to_str()),
788     }
789 }
790 
791 /// Helper for handling "fastboot flash ...".
flash( cmd: &str, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>792 async fn flash(
793     cmd: &str,
794     transport: &mut impl Transport,
795     fb_impl: &mut impl FastbootImplementation,
796 ) -> Result<()> {
797     let mut resp = Responder::new(transport);
798     let flash_res =
799         match cmd.strip_prefix("flash:").ok_or::<CommandError>("Missing partition".into()) {
800             Ok(part) => fb_impl.flash(part, &mut resp).await,
801             Err(e) => Err(e),
802         };
803     match flash_res {
804         Err(e) => reply_fail!(resp, "{}", e.to_str()),
805         _ => reply_okay!(resp, ""),
806     }
807 }
808 
809 /// Helper for handling "fastboot erase ...".
erase( cmd: &str, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>810 async fn erase(
811     cmd: &str,
812     transport: &mut impl Transport,
813     fb_impl: &mut impl FastbootImplementation,
814 ) -> Result<()> {
815     let mut resp = Responder::new(transport);
816     let flash_res =
817         match cmd.strip_prefix("erase:").ok_or::<CommandError>("Missing partition".into()) {
818             Ok(part) => fb_impl.erase(part, &mut resp).await,
819             Err(e) => Err(e),
820         };
821     match flash_res {
822         Err(e) => reply_fail!(resp, "{}", e.to_str()),
823         _ => reply_okay!(resp, ""),
824     }
825 }
826 
827 /// Helper for handling "fastboot get_staged ...".
upload( transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>828 async fn upload(
829     transport: &mut impl Transport,
830     fb_impl: &mut impl FastbootImplementation,
831 ) -> Result<()> {
832     let mut resp = Responder::new(transport);
833     let upload_res = fb_impl.upload(&mut resp).await;
834     match resp.remaining_upload > 0 {
835         true => return Err(Error::InvalidInput),
836         _ => match upload_res {
837             Err(e) => reply_fail!(resp, "{}", e.to_str()),
838             _ => reply_okay!(resp, ""),
839         },
840     }
841 }
842 
843 /// Helper for handling "fastboot fetch ...".
fetch( cmd: &str, args: Split<'_, char>, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>844 async fn fetch(
845     cmd: &str,
846     args: Split<'_, char>,
847     transport: &mut impl Transport,
848     fb_impl: &mut impl FastbootImplementation,
849 ) -> Result<()> {
850     let mut resp = Responder::new(transport);
851     let fetch_res = async {
852         let cmd = cmd.strip_prefix("fetch:").ok_or::<CommandError>("Missing arguments".into())?;
853         if args.clone().count() < 3 {
854             return Err("Not enough argments".into());
855         }
856         // Parses backward. Parses size, offset first and treats the remaining string as
857         // partition name. This allows ":" in partition name.
858         let mut rev = args.clone().rev();
859         let sz = next_arg(&mut rev).ok_or("Missing size")?;
860         let off = next_arg(&mut rev).ok_or("Invalid offset")?;
861         let part = &cmd[..cmd.len() - (off.len() + sz.len() + 2)];
862         fb_impl.fetch(part, hex_to_u64(off)?, hex_to_u64(sz)?, &mut resp).await
863     }
864     .await;
865     match resp.remaining_upload > 0 {
866         true => return Err(Error::InvalidInput),
867         _ => match fetch_res {
868             Err(e) => reply_fail!(resp, "{}", e.to_str()),
869             _ => reply_okay!(resp, ""),
870         },
871     }
872 }
873 
874 // Handles `fastboot reboot*`
reboot( mode: RebootMode, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>875 async fn reboot(
876     mode: RebootMode,
877     transport: &mut impl Transport,
878     fb_impl: &mut impl FastbootImplementation,
879 ) -> Result<()> {
880     let mut resp = Responder::new(transport);
881     let e = fb_impl.reboot(mode, &mut resp).await;
882     reply_fail!(resp, "{}", e.to_str())
883 }
884 
885 // Handles `fastboot boot`
boot( transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>886 async fn boot(
887     transport: &mut impl Transport,
888     fb_impl: &mut impl FastbootImplementation,
889 ) -> Result<()> {
890     let mut resp = Responder::new(transport);
891     let boot_res = async { fb_impl.boot(&mut resp).await }.await;
892     match boot_res {
893         Err(e) => reply_fail!(resp, "{}", e.to_str()),
894         _ => reply_okay!(resp, "boot_command"),
895     }
896 }
897 
898 // Handles `fastboot continue`
899 async fn r#continue(
900     transport: &mut impl Transport,
901     fb_impl: &mut impl FastbootImplementation,
902 ) -> Result<()> {
903     let mut resp = Responder::new(transport);
904     match fb_impl.r#continue(&mut resp).await {
905         Ok(_) => reply_okay!(resp, ""),
906         Err(e) => reply_fail!(resp, "{}", e.to_str()),
907     }
908 }
909 
910 // Handles `fastboot set_active`
set_active( mut args: Split<'_, char>, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>911 async fn set_active(
912     mut args: Split<'_, char>,
913     transport: &mut impl Transport,
914     fb_impl: &mut impl FastbootImplementation,
915 ) -> Result<()> {
916     let mut resp = Responder::new(transport);
917     let res = async {
918         let slot = next_arg(&mut args).ok_or("Missing slot")?;
919         fb_impl.set_active(slot, &mut resp).await
920     };
921     match res.await {
922         Ok(_) => reply_okay!(resp, ""),
923         Err(e) => reply_fail!(resp, "{}", e.to_str()),
924     }
925 }
926 
927 /// Helper for handling "fastboot oem ...".
oem( cmd: &str, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>928 async fn oem(
929     cmd: &str,
930     transport: &mut impl Transport,
931     fb_impl: &mut impl FastbootImplementation,
932 ) -> Result<()> {
933     let mut resp = Responder::new(transport);
934     let mut oem_out = [0u8; MAX_RESPONSE_SIZE - 4];
935     let oem_res = fb_impl.oem(cmd, &mut resp, &mut oem_out[..]).await;
936     match oem_res {
937         Ok(msg) => match from_utf8(msg) {
938             Ok(s) => reply_okay!(resp, "{}", s),
939             Err(e) => reply_fail!(resp, "Invalid return string {}", e),
940         },
941         Err(e) => reply_fail!(resp, "{}", e.to_str()),
942     }
943 }
944 
945 /// Process the next Fastboot command from  the transport.
946 ///
947 /// # Returns
948 ///
949 /// * Returns Ok(is_continue) on success where `is_continue` is true if command is
950 ///   `fastboot continue`.
951 /// * Returns Err() on errors.
process_next_command( transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<bool>952 pub async fn process_next_command(
953     transport: &mut impl Transport,
954     fb_impl: &mut impl FastbootImplementation,
955 ) -> Result<bool> {
956     let mut packet = [0u8; MAX_COMMAND_SIZE + 1];
957     let cmd_size = match transport.receive_packet(&mut packet[..MAX_COMMAND_SIZE]).await? {
958         0 => return Ok(false),
959         v => v,
960     };
961     let Ok(cmd_str) = from_utf8(&packet[..cmd_size]) else {
962         transport.send_packet(fastboot_fail!(packet, "Invalid Command")).await?;
963         return Ok(false);
964     };
965     let mut args = cmd_str.split(':');
966     let Some(cmd) = args.next() else {
967         return transport.send_packet(fastboot_fail!(packet, "No command")).await.map(|_| false);
968     };
969     match cmd {
970         "boot" => {
971             boot(transport, fb_impl).await?;
972             return Ok(true);
973         }
974         "continue" => {
975             r#continue(transport, fb_impl).await?;
976             return Ok(true);
977         }
978         "download" => download(args, transport, fb_impl).await,
979         "erase" => erase(cmd_str, transport, fb_impl).await,
980         "fetch" => fetch(cmd_str, args, transport, fb_impl).await,
981         "flash" => flash(cmd_str, transport, fb_impl).await,
982         "getvar" => get_var(&mut packet[..], transport, fb_impl).await,
983         "reboot" => reboot(RebootMode::Normal, transport, fb_impl).await,
984         "reboot-bootloader" => reboot(RebootMode::Bootloader, transport, fb_impl).await,
985         "reboot-fastboot" => reboot(RebootMode::Fastboot, transport, fb_impl).await,
986         "reboot-recovery" => reboot(RebootMode::Recovery, transport, fb_impl).await,
987         "set_active" => set_active(args, transport, fb_impl).await,
988         "upload" => upload(transport, fb_impl).await,
989         _ if cmd_str.starts_with("oem ") => oem(&cmd_str[4..], transport, fb_impl).await,
990         _ => transport.send_packet(fastboot_fail!(packet, "Command not found")).await,
991     }?;
992     Ok(false)
993 }
994 
995 /// Keeps polling and processing fastboot commands from the transport.
996 ///
997 /// # Returns
998 ///
999 /// * Returns Ok(()) if "fastboot continue" is received.
1000 /// * Returns Err() on errors.
run( transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>1001 pub async fn run(
1002     transport: &mut impl Transport,
1003     fb_impl: &mut impl FastbootImplementation,
1004 ) -> Result<()> {
1005     while !process_next_command(transport, fb_impl).await? {}
1006     Ok(())
1007 }
1008 
1009 /// Runs a fastboot over TCP session.
1010 ///
1011 /// The method performs fastboot over TCP handshake and then call `run(...)`.
1012 ///
1013 /// Returns Ok(()) if "fastboot continue" is received.
run_tcp_session( tcp_stream: &mut impl TcpStream, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>1014 pub async fn run_tcp_session(
1015     tcp_stream: &mut impl TcpStream,
1016     fb_impl: &mut impl FastbootImplementation,
1017 ) -> Result<()> {
1018     run(&mut TcpTransport::new_and_handshake(tcp_stream)?, fb_impl).await
1019 }
1020 
1021 /// A helper to convert a hex string into u64.
hex_to_u64(s: &str) -> CommandResult<u64>1022 pub(crate) fn hex_to_u64(s: &str) -> CommandResult<u64> {
1023     Ok(u64::from_str_radix(s.strip_prefix("0x").unwrap_or(s), 16)?)
1024 }
1025 
1026 /// A helper to check and fetch the next non-empty argument.
1027 ///
1028 /// # Args
1029 ///
1030 /// args: A string iterator.
next_arg<'a, T: Iterator<Item = &'a str>>(args: &mut T) -> Option<&'a str>1031 pub fn next_arg<'a, T: Iterator<Item = &'a str>>(args: &mut T) -> Option<&'a str> {
1032     args.next().filter(|v| *v != "")
1033 }
1034 
1035 /// A helper to check and fetch the next argument as a u64 hex string.
1036 ///
1037 /// # Args
1038 ///
1039 /// args: A string iterator.
1040 ///
1041 ///
1042 /// # Returns
1043 ///
1044 /// * Returns Ok(Some(v)) is next argument is available and a valid u64 hex.
1045 /// * Returns Ok(None) is next argument is not available
1046 /// * Returns Err() if next argument is present but not a valid u64 hex.
next_arg_u64<'a, T: Iterator<Item = &'a str>>(args: &mut T) -> CommandResult<Option<u64>>1047 pub fn next_arg_u64<'a, T: Iterator<Item = &'a str>>(args: &mut T) -> CommandResult<Option<u64>> {
1048     match next_arg(args) {
1049         Some(v) => Ok(Some(hex_to_u64(v)?)),
1050         _ => Ok(None),
1051     }
1052 }
1053 
1054 #[cfg(test)]
1055 mod test {
1056     use super::*;
1057     use core::cmp::min;
1058     use std::collections::{BTreeMap, VecDeque};
1059 
1060     #[derive(Default)]
1061     struct FastbootTest {
1062         // A mapping from (variable name, argument) to variable value.
1063         vars: BTreeMap<(&'static str, &'static [&'static str]), &'static str>,
1064         // The partition arg from Fastboot flash command
1065         flash_partition: String,
1066         // The partition arg from Fastboot erase command
1067         erase_partition: String,
1068         // Upload size, batches of data to upload,
1069         upload_config: (u64, Vec<Vec<u8>>),
1070         // A map from partition name to (upload size override, partition data)
1071         fetch_data: BTreeMap<&'static str, (u64, Vec<u8>)>,
1072         // result string, INFO strings.
1073         oem_output: (String, Vec<String>),
1074         oem_command: String,
1075         download_buffer: Vec<u8>,
1076         downloaded_size: usize,
1077         reboot_mode: Option<RebootMode>,
1078         active_slot: Option<String>,
1079     }
1080 
1081     impl FastbootImplementation for FastbootTest {
get_var( &mut self, var: &CStr, args: impl Iterator<Item = &'_ CStr> + Clone, out: &mut [u8], _: impl InfoSender, ) -> CommandResult<usize>1082         async fn get_var(
1083             &mut self,
1084             var: &CStr,
1085             args: impl Iterator<Item = &'_ CStr> + Clone,
1086             out: &mut [u8],
1087             _: impl InfoSender,
1088         ) -> CommandResult<usize> {
1089             let args = args.map(|v| v.to_str().unwrap()).collect::<Vec<_>>();
1090             match self.vars.get(&(var.to_str()?, &args[..])) {
1091                 Some(v) => {
1092                     out[..v.len()].clone_from_slice(v.as_bytes());
1093                     Ok(v.len())
1094                 }
1095                 _ => Err("Not Found".into()),
1096             }
1097         }
1098 
get_var_all(&mut self, mut responder: impl VarInfoSender) -> CommandResult<()>1099         async fn get_var_all(&mut self, mut responder: impl VarInfoSender) -> CommandResult<()> {
1100             for ((var, config), value) in &self.vars {
1101                 responder.send_var_info(var, config.iter().copied(), value).await?;
1102             }
1103             Ok(())
1104         }
1105 
get_download_buffer(&mut self) -> &mut [u8]1106         async fn get_download_buffer(&mut self) -> &mut [u8] {
1107             self.download_buffer.as_mut_slice()
1108         }
1109 
download_complete( &mut self, download_size: usize, _: impl InfoSender, ) -> CommandResult<()>1110         async fn download_complete(
1111             &mut self,
1112             download_size: usize,
1113             _: impl InfoSender,
1114         ) -> CommandResult<()> {
1115             self.downloaded_size = download_size;
1116             Ok(())
1117         }
1118 
flash(&mut self, part: &str, _: impl InfoSender) -> CommandResult<()>1119         async fn flash(&mut self, part: &str, _: impl InfoSender) -> CommandResult<()> {
1120             self.flash_partition = part.into();
1121             Ok(())
1122         }
1123 
erase(&mut self, part: &str, _: impl InfoSender) -> CommandResult<()>1124         async fn erase(&mut self, part: &str, _: impl InfoSender) -> CommandResult<()> {
1125             self.erase_partition = part.into();
1126             Ok(())
1127         }
1128 
upload(&mut self, responder: impl UploadBuilder) -> CommandResult<()>1129         async fn upload(&mut self, responder: impl UploadBuilder) -> CommandResult<()> {
1130             let (size, batches) = &self.upload_config;
1131             let mut uploader = responder.initiate_upload(*size).await?;
1132             for ele in batches {
1133                 uploader.upload(&ele[..]).await?;
1134             }
1135             Ok(())
1136         }
1137 
fetch( &mut self, part: &str, offset: u64, size: u64, responder: impl UploadBuilder + InfoSender, ) -> CommandResult<()>1138         async fn fetch(
1139             &mut self,
1140             part: &str,
1141             offset: u64,
1142             size: u64,
1143             responder: impl UploadBuilder + InfoSender,
1144         ) -> CommandResult<()> {
1145             let (size_override, data) = self.fetch_data.get(part).ok_or("Not Found")?;
1146             let mut uploader = responder.initiate_upload(*size_override).await?;
1147             Ok(uploader
1148                 .upload(&data[offset.try_into().unwrap()..][..size.try_into().unwrap()])
1149                 .await?)
1150         }
1151 
boot(&mut self, mut responder: impl InfoSender + OkaySender) -> CommandResult<()>1152         async fn boot(&mut self, mut responder: impl InfoSender + OkaySender) -> CommandResult<()> {
1153             Ok(responder.send_info("Boot to boot.img...").await?)
1154         }
1155 
reboot( &mut self, mode: RebootMode, responder: impl InfoSender + OkaySender, ) -> CommandError1156         async fn reboot(
1157             &mut self,
1158             mode: RebootMode,
1159             responder: impl InfoSender + OkaySender,
1160         ) -> CommandError {
1161             responder.send_okay("").await.unwrap();
1162             self.reboot_mode = Some(mode);
1163             "reboot-return".into()
1164         }
1165 
1166         async fn r#continue(&mut self, mut responder: impl InfoSender) -> CommandResult<()> {
1167             Ok(responder.send_info("Continuing to boot...").await?)
1168         }
1169 
set_active(&mut self, slot: &str, _: impl InfoSender) -> CommandResult<()>1170         async fn set_active(&mut self, slot: &str, _: impl InfoSender) -> CommandResult<()> {
1171             self.active_slot = Some(slot.into());
1172             Ok(())
1173         }
1174 
oem<'b>( &mut self, cmd: &str, mut responder: impl InfoSender, res: &'b mut [u8], ) -> CommandResult<&'b [u8]>1175         async fn oem<'b>(
1176             &mut self,
1177             cmd: &str,
1178             mut responder: impl InfoSender,
1179             res: &'b mut [u8],
1180         ) -> CommandResult<&'b [u8]> {
1181             let (res_str, infos) = &mut self.oem_output;
1182             self.oem_command = cmd.into();
1183             for ele in infos {
1184                 responder.send_info(ele.as_str()).await?;
1185             }
1186             Ok(snprintf!(res, "{}", *res_str).as_bytes())
1187         }
1188     }
1189 
1190     struct TestTransport {
1191         in_queue: VecDeque<Vec<u8>>,
1192         out_queue: VecDeque<Vec<u8>>,
1193     }
1194 
1195     impl TestTransport {
new() -> Self1196         fn new() -> Self {
1197             Self { in_queue: VecDeque::new(), out_queue: VecDeque::new() }
1198         }
1199 
add_input(&mut self, packet: &[u8])1200         fn add_input(&mut self, packet: &[u8]) {
1201             self.in_queue.push_back(packet.into());
1202         }
1203     }
1204 
1205     impl Transport for TestTransport {
receive_packet(&mut self, out: &mut [u8]) -> Result<usize>1206         async fn receive_packet(&mut self, out: &mut [u8]) -> Result<usize> {
1207             match self.in_queue.pop_front() {
1208                 Some(v) => {
1209                     let size = min(out.len(), v.len());
1210                     out[..size].clone_from_slice(&v[..size]);
1211                     // Returns the input length so that we can test bogus download size.
1212                     Ok(v.len())
1213                 }
1214                 _ => Err(Error::Other(Some("No more data"))),
1215             }
1216         }
1217 
send_packet(&mut self, packet: &[u8]) -> Result<()>1218         async fn send_packet(&mut self, packet: &[u8]) -> Result<()> {
1219             self.out_queue.push_back(packet.into());
1220             Ok(())
1221         }
1222     }
1223 
1224     #[derive(Default)]
1225     struct TestTcpStream {
1226         in_queue: VecDeque<u8>,
1227         out_queue: VecDeque<u8>,
1228     }
1229 
1230     impl TestTcpStream {
1231         /// Adds bytes to input stream.
add_input(&mut self, data: &[u8])1232         fn add_input(&mut self, data: &[u8]) {
1233             data.iter().for_each(|v| self.in_queue.push_back(*v));
1234         }
1235 
1236         /// Adds a length pre-fixed bytes stream.
add_length_prefixed_input(&mut self, data: &[u8])1237         fn add_length_prefixed_input(&mut self, data: &[u8]) {
1238             self.add_input(&(data.len() as u64).to_be_bytes());
1239             self.add_input(data);
1240         }
1241     }
1242 
1243     impl TcpStream for TestTcpStream {
read_exact(&mut self, out: &mut [u8]) -> Result<()>1244         async fn read_exact(&mut self, out: &mut [u8]) -> Result<()> {
1245             for ele in out {
1246                 *ele = self.in_queue.pop_front().ok_or(Error::OperationProhibited)?;
1247             }
1248             Ok(())
1249         }
1250 
write_exact(&mut self, data: &[u8]) -> Result<()>1251         async fn write_exact(&mut self, data: &[u8]) -> Result<()> {
1252             data.iter().for_each(|v| self.out_queue.push_back(*v));
1253             Ok(())
1254         }
1255     }
1256 
1257     #[test]
test_boot()1258     fn test_boot() {
1259         let mut fastboot_impl: FastbootTest = Default::default();
1260         fastboot_impl.download_buffer = vec![0u8; 1024];
1261         let mut transport = TestTransport::new();
1262         transport.add_input(b"boot");
1263         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1264         assert_eq!(
1265             transport.out_queue,
1266             VecDeque::<Vec<u8>>::from([
1267                 b"INFOBoot to boot.img...".into(),
1268                 b"OKAYboot_command".into()
1269             ])
1270         );
1271     }
1272 
1273     #[test]
test_non_exist_command()1274     fn test_non_exist_command() {
1275         let mut fastboot_impl: FastbootTest = Default::default();
1276         fastboot_impl.download_buffer = vec![0u8; 1024];
1277         let mut transport = TestTransport::new();
1278         transport.add_input(b"non_exist");
1279         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1280         assert_eq!(transport.out_queue, [b"FAILCommand not found"]);
1281     }
1282 
1283     #[test]
test_non_ascii_command_string()1284     fn test_non_ascii_command_string() {
1285         let mut fastboot_impl: FastbootTest = Default::default();
1286         fastboot_impl.download_buffer = vec![0u8; 1024];
1287         let mut transport = TestTransport::new();
1288         transport.add_input(b"\xff\xff\xff");
1289         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1290         assert_eq!(transport.out_queue, [b"FAILInvalid Command"]);
1291     }
1292 
1293     #[test]
test_get_var_max_download_size()1294     fn test_get_var_max_download_size() {
1295         let mut fastboot_impl: FastbootTest = Default::default();
1296         fastboot_impl.download_buffer = vec![0u8; 1024];
1297         let mut transport = TestTransport::new();
1298         transport.add_input(b"getvar:max-download-size");
1299         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1300         assert_eq!(transport.out_queue, [b"OKAY0x400"]);
1301     }
1302 
1303     #[test]
test_get_var()1304     fn test_get_var() {
1305         let mut fastboot_impl: FastbootTest = Default::default();
1306         let vars: [((&str, &[&str]), &str); 4] = [
1307             (("var_0", &[]), "val_0"),
1308             (("var_1", &["a", "b"]), "val_1_a_b"),
1309             (("var_1", &["c", "d"]), "val_1_c_d"),
1310             (("var_2", &["e", "f"]), "val_2_e_f"),
1311         ];
1312         fastboot_impl.vars = BTreeMap::from(vars);
1313 
1314         fastboot_impl.download_buffer = vec![0u8; 1024];
1315         let mut transport = TestTransport::new();
1316         transport.add_input(b"getvar:var_0");
1317         transport.add_input(b"getvar:var_1:a:b");
1318         transport.add_input(b"getvar:var_1:c:d");
1319         transport.add_input(b"getvar:var_1"); // Not Found
1320         transport.add_input(b"getvar:var_2:e:f");
1321         transport.add_input(b"getvar:var_3"); // Not Found
1322         transport.add_input(b"getvar"); // Not Found
1323 
1324         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1325         assert_eq!(
1326             transport.out_queue,
1327             VecDeque::<Vec<u8>>::from([
1328                 b"OKAYval_0".into(),
1329                 b"OKAYval_1_a_b".into(),
1330                 b"OKAYval_1_c_d".into(),
1331                 b"FAILNot Found".into(),
1332                 b"OKAYval_2_e_f".into(),
1333                 b"FAILNot Found".into(),
1334                 b"FAILMissing variable".into(),
1335             ])
1336         );
1337     }
1338 
1339     #[test]
test_get_var_all()1340     fn test_get_var_all() {
1341         let mut fastboot_impl: FastbootTest = Default::default();
1342         let vars: [((&str, &[&str]), &str); 4] = [
1343             (("var_0", &[]), "val_0"),
1344             (("var_1", &["a", "b"]), "val_1_a_b"),
1345             (("var_1", &["c", "d"]), "val_1_c_d"),
1346             (("var_2", &["e", "f"]), "val_2_e_f"),
1347         ];
1348         fastboot_impl.vars = BTreeMap::from(vars);
1349 
1350         fastboot_impl.download_buffer = vec![0u8; 1024];
1351         let mut transport = TestTransport::new();
1352         transport.add_input(b"getvar:all");
1353         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1354         assert_eq!(
1355             transport.out_queue,
1356             VecDeque::<Vec<u8>>::from([
1357                 b"INFOmax-download-size: 0x400".into(),
1358                 b"INFOvar_0: val_0".into(),
1359                 b"INFOvar_1:a:b: val_1_a_b".into(),
1360                 b"INFOvar_1:c:d: val_1_c_d".into(),
1361                 b"INFOvar_2:e:f: val_2_e_f".into(),
1362                 b"OKAY".into(),
1363             ])
1364         );
1365     }
1366 
1367     #[test]
test_download()1368     fn test_download() {
1369         let mut fastboot_impl: FastbootTest = Default::default();
1370         fastboot_impl.download_buffer = vec![0u8; 1024];
1371         let download_content: Vec<u8> =
1372             (0..fastboot_impl.download_buffer.len()).into_iter().map(|v| v as u8).collect();
1373         let mut transport = TestTransport::new();
1374         // Splits download into two batches.
1375         let (first, second) = download_content.as_slice().split_at(download_content.len() / 2);
1376         transport.add_input(format!("download:{:#x}", download_content.len()).as_bytes());
1377         transport.add_input(first);
1378         transport.add_input(second);
1379         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1380         assert_eq!(
1381             transport.out_queue,
1382             VecDeque::<Vec<u8>>::from([b"DATA00000400".into(), b"OKAY".into(),])
1383         );
1384         assert_eq!(fastboot_impl.downloaded_size, download_content.len());
1385         assert_eq!(fastboot_impl.download_buffer, download_content);
1386     }
1387 
1388     #[test]
test_download_not_enough_args()1389     fn test_download_not_enough_args() {
1390         let mut fastboot_impl: FastbootTest = Default::default();
1391         fastboot_impl.download_buffer = vec![0u8; 1024];
1392         let mut transport = TestTransport::new();
1393         transport.add_input(b"download");
1394         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1395         assert_eq!(transport.out_queue, [b"FAILNot enough argument"]);
1396     }
1397 
1398     #[test]
test_download_invalid_hex_string()1399     fn test_download_invalid_hex_string() {
1400         let mut fastboot_impl: FastbootTest = Default::default();
1401         fastboot_impl.download_buffer = vec![0u8; 1024];
1402         let mut transport = TestTransport::new();
1403         transport.add_input(b"download:hhh");
1404         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1405         assert_eq!(transport.out_queue.len(), 1);
1406         assert!(transport.out_queue[0].starts_with(b"FAIL"));
1407     }
1408 
test_download_size(download_buffer_size: usize, download_size: usize, msg: &str)1409     fn test_download_size(download_buffer_size: usize, download_size: usize, msg: &str) {
1410         let mut fastboot_impl: FastbootTest = Default::default();
1411         fastboot_impl.download_buffer = vec![0u8; download_buffer_size];
1412         let mut transport = TestTransport::new();
1413         transport.add_input(format!("download:{:#x}", download_size).as_bytes());
1414         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1415         assert_eq!(transport.out_queue, VecDeque::<Vec<u8>>::from([msg.as_bytes().into()]));
1416     }
1417 
1418     #[test]
test_download_download_size_too_big()1419     fn test_download_download_size_too_big() {
1420         test_download_size(1024, 1025, "FAILDownload size is too big");
1421     }
1422 
1423     #[test]
test_download_zero_download_size()1424     fn test_download_zero_download_size() {
1425         test_download_size(1024, 0, "FAILZero download size");
1426     }
1427 
1428     #[test]
test_download_more_than_expected()1429     fn test_download_more_than_expected() {
1430         let mut fastboot_impl: FastbootTest = Default::default();
1431         fastboot_impl.download_buffer = vec![0u8; 1024];
1432         let download_content: Vec<u8> = vec![0u8; fastboot_impl.download_buffer.len()];
1433         let mut transport = TestTransport::new();
1434         transport.add_input(format!("download:{:#x}", download_content.len() - 1).as_bytes());
1435         transport.add_input(&download_content[..]);
1436         // State should be reset to command state.
1437         transport.add_input(b"getvar:max-download-size");
1438         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1439         assert_eq!(
1440             transport.out_queue,
1441             VecDeque::<Vec<u8>>::from([
1442                 b"DATA000003ff".into(),
1443                 b"FAILMore data received then expected".into(),
1444                 b"OKAY0x400".into(),
1445             ])
1446         );
1447     }
1448 
1449     #[test]
test_oem_cmd()1450     fn test_oem_cmd() {
1451         let mut fastboot_impl: FastbootTest = Default::default();
1452         fastboot_impl.download_buffer = vec![0u8; 2048];
1453         let mut transport = TestTransport::new();
1454         transport.add_input(b"oem oem-command");
1455         fastboot_impl.oem_output =
1456             ("oem-return".into(), vec!["oem-info-1".into(), "oem-info-2".into()]);
1457         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1458         assert_eq!(fastboot_impl.oem_command, "oem-command");
1459         assert_eq!(
1460             transport.out_queue,
1461             VecDeque::<Vec<u8>>::from([
1462                 b"INFOoem-info-1".into(),
1463                 b"INFOoem-info-2".into(),
1464                 b"OKAYoem-return".into(),
1465             ])
1466         );
1467     }
1468 
1469     #[test]
test_flash()1470     fn test_flash() {
1471         let mut fastboot_impl: FastbootTest = Default::default();
1472         fastboot_impl.download_buffer = vec![0u8; 2048];
1473         let mut transport = TestTransport::new();
1474         transport.add_input(b"flash:boot_a:0::");
1475         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1476         assert_eq!(fastboot_impl.flash_partition, "boot_a:0::");
1477         assert_eq!(transport.out_queue, VecDeque::<Vec<u8>>::from([b"OKAY".into()]));
1478     }
1479 
1480     #[test]
test_flash_missing_partition()1481     fn test_flash_missing_partition() {
1482         let mut fastboot_impl: FastbootTest = Default::default();
1483         let mut transport = TestTransport::new();
1484         transport.add_input(b"flash");
1485         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1486         assert_eq!(transport.out_queue, [b"FAILMissing partition"]);
1487     }
1488 
1489     #[test]
test_erase()1490     fn test_erase() {
1491         let mut fastboot_impl: FastbootTest = Default::default();
1492         fastboot_impl.download_buffer = vec![0u8; 2048];
1493         let mut transport = TestTransport::new();
1494         transport.add_input(b"erase:boot_a:0::");
1495         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1496         assert_eq!(fastboot_impl.erase_partition, "boot_a:0::");
1497         assert_eq!(transport.out_queue, VecDeque::<Vec<u8>>::from([b"OKAY".into()]));
1498     }
1499 
1500     #[test]
test_erase_missing_partition()1501     fn test_erase_missing_partition() {
1502         let mut fastboot_impl: FastbootTest = Default::default();
1503         let mut transport = TestTransport::new();
1504         transport.add_input(b"erase");
1505         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1506         assert_eq!(transport.out_queue, [b"FAILMissing partition"]);
1507     }
1508 
1509     #[test]
test_upload()1510     fn test_upload() {
1511         let mut fastboot_impl: FastbootTest = Default::default();
1512         let upload_content: Vec<u8> = (0..1024).into_iter().map(|v| v as u8).collect();
1513         let mut transport = TestTransport::new();
1514         transport.add_input(b"upload");
1515         fastboot_impl.upload_config = (
1516             upload_content.len().try_into().unwrap(),
1517             vec![
1518                 upload_content[..upload_content.len() / 2].to_vec(),
1519                 upload_content[upload_content.len() / 2..].to_vec(),
1520             ],
1521         );
1522         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1523         assert_eq!(
1524             transport.out_queue,
1525             VecDeque::<Vec<u8>>::from([
1526                 b"DATA00000400".into(),
1527                 upload_content[..upload_content.len() / 2].to_vec(),
1528                 upload_content[upload_content.len() / 2..].to_vec(),
1529                 b"OKAY".into(),
1530             ])
1531         );
1532     }
1533 
1534     #[test]
test_upload_not_enough_data()1535     fn test_upload_not_enough_data() {
1536         let mut fastboot_impl: FastbootTest = Default::default();
1537         fastboot_impl.download_buffer = vec![0u8; 2048];
1538         let mut transport = TestTransport::new();
1539         transport.add_input(b"upload");
1540         fastboot_impl.upload_config = (0x400, vec![vec![0u8; 0x400 - 1]]);
1541         assert!(block_on(run(&mut transport, &mut fastboot_impl)).is_err());
1542     }
1543 
1544     #[test]
test_upload_more_data()1545     fn test_upload_more_data() {
1546         let mut fastboot_impl: FastbootTest = Default::default();
1547         fastboot_impl.download_buffer = vec![0u8; 2048];
1548         let mut transport = TestTransport::new();
1549         transport.add_input(b"upload");
1550         fastboot_impl.upload_config = (0x400, vec![vec![0u8; 0x400 + 1]]);
1551         assert!(block_on(run(&mut transport, &mut fastboot_impl)).is_err());
1552     }
1553 
1554     #[test]
test_fetch()1555     fn test_fetch() {
1556         let mut fastboot_impl: FastbootTest = Default::default();
1557         fastboot_impl.download_buffer = vec![0u8; 2048];
1558         let mut transport = TestTransport::new();
1559         transport.add_input(b"fetch:boot_a:0:::200:400");
1560         fastboot_impl
1561             .fetch_data
1562             .insert("boot_a:0::", (0x400, vec![vec![0u8; 0x200], vec![1u8; 0x400]].concat()));
1563         block_on(process_next_command(&mut transport, &mut fastboot_impl)).unwrap();
1564         assert_eq!(
1565             transport.out_queue,
1566             VecDeque::<Vec<u8>>::from([
1567                 b"DATA00000400".into(),
1568                 [1u8; 0x400].to_vec(),
1569                 b"OKAY".into(),
1570             ])
1571         );
1572     }
1573 
1574     #[test]
test_fetch_not_enough_data()1575     fn test_fetch_not_enough_data() {
1576         let mut fastboot_impl: FastbootTest = Default::default();
1577         fastboot_impl.download_buffer = vec![0u8; 2048];
1578         let mut transport = TestTransport::new();
1579         transport.add_input(b"fetch:boot_a:0:::200:400");
1580         fastboot_impl
1581             .fetch_data
1582             .insert("boot_a:0::", (0x400 - 1, vec![vec![0u8; 0x200], vec![1u8; 0x400]].concat()));
1583         assert!(block_on(process_next_command(&mut transport, &mut fastboot_impl)).is_err());
1584     }
1585 
1586     #[test]
test_fetch_more_data()1587     fn test_fetch_more_data() {
1588         let mut fastboot_impl: FastbootTest = Default::default();
1589         fastboot_impl.download_buffer = vec![0u8; 2048];
1590         let mut transport = TestTransport::new();
1591         transport.add_input(b"fetch:boot_a:0:::200:400");
1592         fastboot_impl
1593             .fetch_data
1594             .insert("boot_a:0::", (0x400 + 1, vec![vec![0u8; 0x200], vec![1u8; 0x400]].concat()));
1595         assert!(block_on(process_next_command(&mut transport, &mut fastboot_impl)).is_err());
1596     }
1597 
1598     #[test]
test_fetch_invalid_args()1599     fn test_fetch_invalid_args() {
1600         let mut fastboot_impl: FastbootTest = Default::default();
1601         fastboot_impl.download_buffer = vec![0u8; 2048];
1602         let mut transport = TestTransport::new();
1603         transport.add_input(b"fetch");
1604         transport.add_input(b"fetch:");
1605         transport.add_input(b"fetch:boot_a");
1606         transport.add_input(b"fetch:boot_a:200");
1607         transport.add_input(b"fetch:boot_a::400");
1608         transport.add_input(b"fetch:boot_a::");
1609         transport.add_input(b"fetch:boot_a:xxx:400");
1610         transport.add_input(b"fetch:boot_a:200:xxx");
1611         let _ = block_on(run(&mut transport, &mut fastboot_impl));
1612         assert!(transport.out_queue.iter().all(|v| v.starts_with(b"FAIL")));
1613     }
1614 
1615     #[test]
test_fastboot_tcp()1616     fn test_fastboot_tcp() {
1617         let mut fastboot_impl: FastbootTest = Default::default();
1618         fastboot_impl.download_buffer = vec![0u8; 1024];
1619         let download_content: Vec<u8> =
1620             (0..fastboot_impl.download_buffer.len()).into_iter().map(|v| v as u8).collect();
1621         let mut tcp_stream: TestTcpStream = Default::default();
1622         tcp_stream.add_input(TCP_HANDSHAKE_MESSAGE);
1623         // Add two commands and verify both are executed.
1624         tcp_stream.add_length_prefixed_input(b"getvar:max-download-size");
1625         tcp_stream.add_length_prefixed_input(
1626             format!("download:{:#x}", download_content.len()).as_bytes(),
1627         );
1628         tcp_stream.add_length_prefixed_input(&download_content[..]);
1629         let _ = block_on(run_tcp_session(&mut tcp_stream, &mut fastboot_impl));
1630         let expected: &[&[u8]] = &[
1631             b"FB01",
1632             b"\x00\x00\x00\x00\x00\x00\x00\x09OKAY0x400",
1633             b"\x00\x00\x00\x00\x00\x00\x00\x0cDATA00000400",
1634             b"\x00\x00\x00\x00\x00\x00\x00\x04OKAY",
1635         ];
1636         assert_eq!(tcp_stream.out_queue, VecDeque::from(expected.concat()));
1637         assert_eq!(fastboot_impl.download_buffer, download_content);
1638     }
1639 
1640     #[test]
test_fastboot_tcp_invalid_handshake()1641     fn test_fastboot_tcp_invalid_handshake() {
1642         let mut fastboot_impl: FastbootTest = Default::default();
1643         let mut tcp_stream: TestTcpStream = Default::default();
1644         tcp_stream.add_input(b"ABCD");
1645         assert_eq!(
1646             block_on(run_tcp_session(&mut tcp_stream, &mut fastboot_impl)).unwrap_err(),
1647             Error::InvalidHandshake
1648         );
1649     }
1650 
1651     #[test]
test_fastboot_tcp_packet_size_exceeds_maximum()1652     fn test_fastboot_tcp_packet_size_exceeds_maximum() {
1653         let mut fastboot_impl: FastbootTest = Default::default();
1654         let mut tcp_stream: TestTcpStream = Default::default();
1655         tcp_stream.add_input(TCP_HANDSHAKE_MESSAGE);
1656         tcp_stream.add_input(&(MAX_COMMAND_SIZE + 1).to_be_bytes());
1657         assert_eq!(
1658             block_on(run_tcp_session(&mut tcp_stream, &mut fastboot_impl)).unwrap_err(),
1659             Error::InvalidInput
1660         );
1661     }
1662 
1663     #[test]
test_reboot()1664     fn test_reboot() {
1665         let mut fastboot_impl: FastbootTest = Default::default();
1666         let mut transport = TestTransport::new();
1667         transport.add_input(b"reboot");
1668         block_on(process_next_command(&mut transport, &mut fastboot_impl)).unwrap();
1669         assert_eq!(fastboot_impl.reboot_mode, Some(RebootMode::Normal));
1670         assert_eq!(transport.out_queue[0], b"OKAY");
1671         // Failure is expected here because test reboot implementation always returns, which
1672         // automatically generates a fastboot failure packet.
1673         assert!(transport.out_queue[1].starts_with(b"FAIL"));
1674     }
1675 
1676     #[test]
test_reboot_bootloader()1677     fn test_reboot_bootloader() {
1678         let mut fastboot_impl: FastbootTest = Default::default();
1679         let mut transport = TestTransport::new();
1680         transport.add_input(b"reboot-bootloader");
1681         block_on(process_next_command(&mut transport, &mut fastboot_impl)).unwrap();
1682         assert_eq!(fastboot_impl.reboot_mode, Some(RebootMode::Bootloader));
1683         assert_eq!(transport.out_queue[0], b"OKAY");
1684         // Failure is expected here because test reboot implementation always returns, which
1685         // automatically generates a fastboot failure packet.
1686         assert!(transport.out_queue[1].starts_with(b"FAIL"));
1687     }
1688 
1689     #[test]
test_reboot_fastboot()1690     fn test_reboot_fastboot() {
1691         let mut fastboot_impl: FastbootTest = Default::default();
1692         let mut transport = TestTransport::new();
1693         transport.add_input(b"reboot-fastboot");
1694         block_on(process_next_command(&mut transport, &mut fastboot_impl)).unwrap();
1695         assert_eq!(fastboot_impl.reboot_mode, Some(RebootMode::Fastboot));
1696         assert_eq!(transport.out_queue[0], b"OKAY");
1697         // Failure is expected here because test reboot implementation always returns, which
1698         // automatically generates a fastboot failure packet.
1699         assert!(transport.out_queue[1].starts_with(b"FAIL"));
1700     }
1701 
1702     #[test]
test_reboot_recovery()1703     fn test_reboot_recovery() {
1704         let mut fastboot_impl: FastbootTest = Default::default();
1705         let mut transport = TestTransport::new();
1706         transport.add_input(b"reboot-recovery");
1707         block_on(process_next_command(&mut transport, &mut fastboot_impl)).unwrap();
1708         assert_eq!(fastboot_impl.reboot_mode, Some(RebootMode::Recovery));
1709         assert_eq!(transport.out_queue[0], b"OKAY");
1710         // Failure is expected here because test reboot implementation always returns, which
1711         // automatically generates a fastboot failure packet.
1712         assert!(transport.out_queue[1].starts_with(b"FAIL"));
1713     }
1714 
1715     #[test]
test_continue()1716     fn test_continue() {
1717         let mut fastboot_impl: FastbootTest = Default::default();
1718         fastboot_impl.download_buffer = vec![0u8; 1024];
1719         let mut transport = TestTransport::new();
1720         transport.add_input(b"getvar:max-download-size");
1721         transport.add_input(b"continue");
1722         transport.add_input(b"getvar:max-download-size");
1723         block_on(run(&mut transport, &mut fastboot_impl)).unwrap();
1724         assert_eq!(
1725             transport.out_queue,
1726             VecDeque::<Vec<u8>>::from([
1727                 b"OKAY0x400".into(),
1728                 b"INFOContinuing to boot...".into(),
1729                 b"OKAY".into()
1730             ])
1731         );
1732     }
1733 
1734     #[test]
test_continue_run_tcp()1735     fn test_continue_run_tcp() {
1736         let mut fastboot_impl: FastbootTest = Default::default();
1737         let mut tcp_stream: TestTcpStream = Default::default();
1738         tcp_stream.add_input(TCP_HANDSHAKE_MESSAGE);
1739         tcp_stream.add_length_prefixed_input(b"continue");
1740         block_on(run_tcp_session(&mut tcp_stream, &mut fastboot_impl)).unwrap();
1741     }
1742 
1743     #[test]
test_set_active()1744     fn test_set_active() {
1745         let mut fastboot_impl: FastbootTest = Default::default();
1746         fastboot_impl.download_buffer = vec![0u8; 1024];
1747         let mut transport = TestTransport::new();
1748         transport.add_input(b"set_active:a");
1749         block_on(process_next_command(&mut transport, &mut fastboot_impl)).unwrap();
1750         assert_eq!(transport.out_queue, VecDeque::<Vec<u8>>::from([b"OKAY".into()]));
1751         assert_eq!(fastboot_impl.active_slot, Some("a".into()));
1752     }
1753 
1754     #[test]
test_set_active_missing_slot()1755     fn test_set_active_missing_slot() {
1756         let mut fastboot_impl: FastbootTest = Default::default();
1757         fastboot_impl.download_buffer = vec![0u8; 1024];
1758         let mut transport = TestTransport::new();
1759         transport.add_input(b"set_active");
1760         block_on(process_next_command(&mut transport, &mut fastboot_impl)).unwrap();
1761         assert_eq!(transport.out_queue, VecDeque::<Vec<u8>>::from([b"FAILMissing slot".into()]));
1762     }
1763 }
1764