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