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 //! ) -> Result<usize, CommandError> {
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(&mut download_buffer[..]);
64 //! let result = fastboot.run(&mut transport, &mut fastboot_impl, &[]);
65 //! ```
66
67 #![cfg_attr(not(test), no_std)]
68
69 use core::fmt::{Debug, Display, Error, Formatter, Write};
70 use core::str::{from_utf8, Split};
71
72 pub const MAX_COMMAND_SIZE: usize = 4096;
73 pub const MAX_RESPONSE_SIZE: usize = 256;
74 const OKAY: &'static str = "OKAY";
75
76 /// Transport errors.
77 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
78 pub enum TransportError {
79 InvalidHanshake,
80 InvalidState,
81 PacketSizeOverflow,
82 PacketSizeExceedMaximum,
83 NotEnoughUpload,
84 Others(&'static str),
85 }
86
87 impl Display for TransportError {
fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>88 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
89 write!(f, "{:?}", self)
90 }
91 }
92
93 /// Implementation for Fastboot transport interfaces.
94 pub trait Transport {
95 /// Fetches the next fastboot packet into `out`.
96 ///
97 /// Returns the actual size of the packet on success.
98 ///
99 /// TODO(b/322540167): In the future, we may want to support using `[MaybeUninit<u8>]` as the
100 /// download buffer to avoid expensive initialization at the beginning. This would require an
101 /// interface where the implementation provides the buffer for us to copy instead of us.
receive_packet(&mut self, out: &mut [u8]) -> Result<usize, TransportError>102 fn receive_packet(&mut self, out: &mut [u8]) -> Result<usize, TransportError>;
103
104 /// Sends a fastboot packet.
105 ///
106 /// The method assumes `packet` is sent or at least copied to queue after it returns, where
107 /// the buffer can go out of scope without affecting anything.
send_packet(&mut self, packet: &[u8]) -> Result<(), TransportError>108 fn send_packet(&mut self, packet: &[u8]) -> Result<(), TransportError>;
109 }
110
111 /// For now, we hardcode the expected version, until we need to distinguish between multiple
112 /// versions.
113 const TCP_HANDSHAKE_MESSAGE: &[u8] = b"FB01";
114
115 /// A trait representing a TCP stream reader/writer. Fastboot over TCP has additional handshake
116 /// process and uses a length-prefixed wire message format. It is recommended that caller
117 /// implements this trait instead of `Transport`, and uses the API `Fastboot::run_tcp_session()`
118 /// to perform fastboot over TCP. It internally handles handshake and wire message parsing.
119 pub trait TcpStream {
120 /// Reads to `out` for exactly `out.len()` number bytes from the TCP connection.
read_exact(&mut self, out: &mut [u8]) -> Result<(), TransportError>121 fn read_exact(&mut self, out: &mut [u8]) -> Result<(), TransportError>;
122
123 /// Sends exactly `data.len()` number bytes from `data` to the TCP connection.
write_exact(&mut self, data: &[u8]) -> Result<(), TransportError>124 fn write_exact(&mut self, data: &[u8]) -> Result<(), TransportError>;
125 }
126
127 /// `TcpTransport` implements `Transport` with a `TcpStream` object.
128 pub struct TcpTransport<'a>(&'a mut dyn TcpStream);
129
130 impl<'a> TcpTransport<'a> {
131 /// Creates an instance from a newly connected TcpStream and performs handshake.
new_and_handshake(tcp_stream: &'a mut dyn TcpStream) -> Result<Self, TransportError>132 pub fn new_and_handshake(tcp_stream: &'a mut dyn TcpStream) -> Result<Self, TransportError> {
133 let mut handshake = [0u8; 4];
134 tcp_stream.write_exact(TCP_HANDSHAKE_MESSAGE)?;
135 tcp_stream.read_exact(&mut handshake[..])?;
136 match handshake == *TCP_HANDSHAKE_MESSAGE {
137 true => Ok(Self(tcp_stream)),
138 _ => Err(TransportError::InvalidHanshake),
139 }
140 }
141 }
142
143 impl Transport for TcpTransport<'_> {
receive_packet(&mut self, out: &mut [u8]) -> Result<usize, TransportError>144 fn receive_packet(&mut self, out: &mut [u8]) -> Result<usize, TransportError> {
145 let mut length_prefix = [0u8; 8];
146 self.0.read_exact(&mut length_prefix[..])?;
147 let packet_size: usize = u64::from_be_bytes(length_prefix)
148 .try_into()
149 .map_err(|_| TransportError::PacketSizeOverflow)?;
150 match out.len() < packet_size {
151 true => Err(TransportError::PacketSizeExceedMaximum),
152 _ => {
153 self.0.read_exact(&mut out[..packet_size])?;
154 Ok(packet_size)
155 }
156 }
157 }
158
send_packet(&mut self, packet: &[u8]) -> Result<(), TransportError>159 fn send_packet(&mut self, packet: &[u8]) -> Result<(), TransportError> {
160 self.0.write_exact(
161 &mut u64::try_from(packet.len())
162 .map_err(|_| TransportError::PacketSizeOverflow)?
163 .to_be_bytes()[..],
164 )?;
165 self.0.write_exact(packet)
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 from_utf8(&self.0 .0[..self.0 .1]).unwrap_or("")
183 }
184 }
185
186 impl Debug for CommandError {
fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>187 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
188 write!(f, "{}", self.to_str())
189 }
190 }
191
192 impl<T: Display> From<T> for CommandError {
from(val: T) -> Self193 fn from(val: T) -> Self {
194 let mut res = CommandError(FormattedBytes([0u8; COMMAND_ERROR_LENGTH], 0));
195 write!(res.0, "{}", val).unwrap();
196 res
197 }
198 }
199
200 /// Implementation for Fastboot command backends.
201 pub trait FastbootImplementation {
202 /// Backend for `fastboot getvar ...`
203 ///
204 /// Gets the value of a variable specified by name and configuration represented by list of
205 /// additional arguments in `args`.
206 ///
207 /// Variable `max-download-size`, `version` are reserved by the library.
208 ///
209 /// # Args
210 ///
211 /// * `var`: Name of the variable.
212 /// * `args`: Additional arguments.
213 /// * `out`: Output buffer for storing the variable value.
214 /// * `utils`: A mutable reference to an instance of `FastbootUtils`.
215 ///
216 /// TODO(b/322540167): Figure out other reserved variables.
get_var( &mut self, var: &str, args: Split<char>, out: &mut [u8], utils: &mut FastbootUtils, ) -> Result<usize, CommandError>217 fn get_var(
218 &mut self,
219 var: &str,
220 args: Split<char>,
221 out: &mut [u8],
222 utils: &mut FastbootUtils,
223 ) -> Result<usize, CommandError>;
224
225 /// A helper API for getting the value of a fastboot variable and decoding it into string.
get_var_as_str<'s>( &mut self, var: &str, args: Split<char>, out: &'s mut [u8], utils: &mut FastbootUtils, ) -> Result<&'s str, CommandError>226 fn get_var_as_str<'s>(
227 &mut self,
228 var: &str,
229 args: Split<char>,
230 out: &'s mut [u8],
231 utils: &mut FastbootUtils,
232 ) -> Result<&'s str, CommandError> {
233 let size = self.get_var(var, args, out, utils)?;
234 Ok(from_utf8(out.get(..size).ok_or("Invalid variable size")?)
235 .map_err(|_| "Value is not string")?)
236 }
237
238 /// Backend for `fastboot getvar all`.
239 ///
240 /// Iterates all combinations of fastboot variable, configurations and values that need to be
241 /// included in the response to `fastboot getvar all`.
242 ///
243 /// # Args
244 ///
245 /// * `f`: A closure that takes 3 arguments: 1. variable name, 2. an array of string
246 /// arguments and 3. the corresponding variable value. Implementation should call this for
247 /// all combinations that need to be returned for `fastboot getvar all`. If `f` returns
248 /// error, the implementation should return it immediately. For example the following
249 /// implementation:
250 ///
251 /// fn get_var_all(&mut self, f: F, utils: &mut FastbootUtils) -> Result<(), CommandError> {
252 /// f("partition-size", &["boot_a"], /* size string of boot_a */)?;
253 /// f("partition-size", &["boot_b"], /* size string of boot_b */)?;
254 /// f("partition-size", &["init_boot_a"], /* size string of init_boot_a */)?;
255 /// f("partition-size", &["init_boot_b"], /* size string of init_boot_b */)?;
256 /// Ok(())
257 /// }
258 ///
259 /// will generates the following outputs for `fastboot getvar all`:
260 ///
261 /// ...
262 /// (bootloader) partition-size:boot_a: <size of boot_a>
263 /// (bootloader) partition-size:boot_b: <size of boot_b>
264 /// (bootloader) partition-size:init_boot_a: <size of init_boot_a>
265 /// (bootloader) partition-size:init_boot_b: <size of init_boot_b>
266 /// ...
267 ///
268 /// * `utils`: A mutable reference to an instance of `FastbootUtils`.
269 ///
270 /// TODO(b/322540167): This and `get_var()` contain duplicated logic. Investigate if there can
271 /// be better solutions for doing the combination traversal.
get_var_all( &mut self, f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>, utils: &mut FastbootUtils, ) -> Result<(), CommandError>272 fn get_var_all(
273 &mut self,
274 f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>,
275 utils: &mut FastbootUtils,
276 ) -> Result<(), CommandError>;
277
278 /// Backend for `fastboot flash ...`
279 ///
280 /// # Args
281 ///
282 /// * `part`: Name of the partition.
283 /// * `utils`: A mutable reference to an instance of `FastbootUtils`.
flash(&mut self, part: &str, utils: &mut FastbootUtils) -> Result<(), CommandError>284 fn flash(&mut self, part: &str, utils: &mut FastbootUtils) -> Result<(), CommandError>;
285
286 /// Backend for `fastboot get_staged ...`
287 ///
288 /// # Args
289 ///
290 /// * `upload_builder`: An instance of `UploadBuilder` for initiating and uploading data. For
291 /// example:
292 ///
293 /// ```
294 /// fn upload(
295 /// &mut self,
296 /// upload_builder: UploadBuilder,
297 /// utils: &mut FastbootUtils,
298 /// ) -> Result<(), CommandError> {
299 /// // Sends a total of 1024 bytes data.
300 /// let mut uploader = upload_builder.start(1024)?;
301 /// // Can upload in multiple batches.
302 /// uploader.upload(&utils.download_buffer[..512])?;
303 /// uploader.upload(&utils.download_buffer[512..])?;
304 /// Ok(())
305 /// }
306 /// ```
307 ///
308 /// If implementation fails to upload enough, or attempts to upload more than expected data
309 /// with `Uploader::upload()`, an error will be returned.
310 ///
311 /// * `utils`: A mutable reference to an instance of `FastbootUtils`.
upload( &mut self, upload_builder: UploadBuilder, utils: &mut FastbootUtils, ) -> Result<(), CommandError>312 fn upload(
313 &mut self,
314 upload_builder: UploadBuilder,
315 utils: &mut FastbootUtils,
316 ) -> Result<(), CommandError>;
317
318 /// Backend for `fastboot fetch ...`
319 ///
320 /// # Args
321 ///
322 /// * `part`: The partition name.
323 /// * `offset`: The offset into the partition for upload.
324 /// * `size`: The number of bytes to upload.
325 /// * `upload_builder`: An instance of `UploadBuilder` for initiating and uploading data.
326 /// * `utils`: A mutable reference to an instance of `FastbootUtils`.
fetch( &mut self, part: &str, offset: u64, size: u64, upload_builder: UploadBuilder, utils: &mut FastbootUtils, ) -> Result<(), CommandError>327 fn fetch(
328 &mut self,
329 part: &str,
330 offset: u64,
331 size: u64,
332 upload_builder: UploadBuilder,
333 utils: &mut FastbootUtils,
334 ) -> Result<(), CommandError>;
335
336 /// Backend for `fastboot oem ...`.
337 ///
338 /// # Args
339 ///
340 /// * `cmd`: The OEM command string that comes after "oem ".
341 /// * `utils`: A mutable reference to an instance of `FastbootUtils`.
342 /// * `res`: The response buffer. Upon success, implementation can use the buffer to
343 /// construct a valid UTF8 string which will be sent as "OKAY<string>"
344 ///
345 /// # Returns
346 ///
347 /// On success, returns the portion of `res` used by the construction of string message.
oem<'a>( &mut self, cmd: &str, utils: &mut FastbootUtils, res: &'a mut [u8], ) -> Result<&'a [u8], CommandError>348 fn oem<'a>(
349 &mut self,
350 cmd: &str,
351 utils: &mut FastbootUtils,
352 res: &'a mut [u8],
353 ) -> Result<&'a [u8], CommandError>;
354
355 // TODO(b/322540167): Add methods for other commands.
356 }
357
358 /// An internal convenient macro helper for `fastboot_okay`, `fastboot_fail` and `fastboot_info`.
359 macro_rules! fastboot_msg {
360 ( $arr:expr, $msg_type:expr, $( $x:expr ),* $(,)? ) => {
361 {
362 let mut formatted_bytes = FormattedBytes::new(&mut $arr[..]);
363 write!(formatted_bytes, $msg_type).unwrap();
364 write!(formatted_bytes, $($x,)*).unwrap();
365 let size = formatted_bytes.size();
366 &mut $arr[..size]
367 }
368 };
369 }
370
371 /// An internal convenient macro that constructs a formatted fastboot OKAY message.
372 macro_rules! fastboot_okay {
373 ( $arr:expr, $( $x:expr ),* ) => { fastboot_msg!($arr, "OKAY", $($x,)*) };
374 }
375
376 /// An internal convenient macro that constructs a formatted fastboot FAIL message.
377 macro_rules! fastboot_fail {
378 ( $arr:expr, $( $x:expr ),* ) => { fastboot_msg!($arr, "FAIL", $($x,)*) };
379 }
380
381 /// An internal convenient macro that constructs a formatted fastboot INFO message.
382 macro_rules! fastboot_info {
383 ( $arr:expr, $( $x:expr ),* ) => { fastboot_msg!($arr, "INFO", $($x,)*) };
384 }
385
386 /// `FastbootInfoSender` defines a method for sending Fastboot INFO messages.
387 ///
388 /// The trait is for user to implement their mock `FastbootUtils` for testing implementation
389 /// of the `FastbootImplementation` trait.
390 pub trait FastbootInfoSend {
391 /// Sends a Fastboot "INFO<`msg`>" packet
send(&mut self, msg: &str) -> Result<(), CommandError>392 fn send(&mut self, msg: &str) -> Result<(), CommandError>;
393 }
394
395 /// `FastbootUtils` contains download data/buffer and a `FastbootInfoSend` trait object for sending
396 /// Fastboot INFO messages. It can be used in the implementation of `FastbootImplementation`.
397 pub struct FastbootUtils<'a> {
398 // TODO(b/328784766): Consider using arrayvec crate or similar instead of passing download
399 // buffer and size separately.
400 // The total download buffer.
401 download_buffer: &'a mut [u8],
402 // Current downloaded data size.
403 download_data_size: &'a mut usize,
404 /// When available, a trait object `FastbootInfoSend` for sending Fastboot INFO messages.
405 fb_info: Option<&'a mut dyn FastbootInfoSend>,
406 }
407
408 impl<'a> FastbootUtils<'a> {
409 /// Creates a new instance.
new( download_buffer: &'a mut [u8], download_data_size: &'a mut usize, fb_info: Option<&'a mut dyn FastbootInfoSend>, ) -> Self410 pub fn new(
411 download_buffer: &'a mut [u8],
412 download_data_size: &'a mut usize,
413 fb_info: Option<&'a mut dyn FastbootInfoSend>,
414 ) -> Self {
415 Self { download_buffer, download_data_size, fb_info }
416 }
417
418 /// Returns the current downloaded data.
download_data(&mut self) -> &mut [u8]419 pub fn download_data(&mut self) -> &mut [u8] {
420 &mut self.download_buffer[..*self.download_data_size]
421 }
422
423 /// Returns the entire download buffer and the size of the download data. The method assumes
424 /// that callers will modify the download buffer and therefore will no longer consider the
425 /// download data valid, i.e. future calls of FastbootUtils::download_data() will only return
426 /// an empty slice.
take_download_buffer(&mut self) -> (&mut [u8], usize)427 pub fn take_download_buffer(&mut self) -> (&mut [u8], usize) {
428 let download_data_size = *self.download_data_size;
429 *self.download_data_size = 0;
430 (self.download_buffer, download_data_size)
431 }
432
433 /// Sends a Fastboot INFO message.
434 ///
435 /// Returns Ok(true) if successful, Ok(false) if INFO messages are not supported in the context
436 /// of the current command, error otherwise.
info_send(&mut self, msg: &str) -> Result<bool, CommandError>437 pub fn info_send(&mut self, msg: &str) -> Result<bool, CommandError> {
438 match self.fb_info {
439 Some(ref mut send) => send.send(msg).map(|_| true),
440 _ => Ok(false),
441 }
442 }
443 }
444
445 /// `FastbootInfoSender` is an internal type that implements `FastbootInfoSend` with a `Transport`
446 /// trait object.
447 struct FastbootInfoSender<'a> {
448 transport: &'a mut dyn Transport,
449 transport_error: Result<(), TransportError>,
450 }
451
452 impl<'a> FastbootInfoSender<'a> {
453 /// Creates an new instance
new(transport: &'a mut dyn Transport) -> Self454 fn new(transport: &'a mut dyn Transport) -> Self {
455 Self { transport: transport, transport_error: Ok(()) }
456 }
457
458 /// Returns the `Self:;transport_error`.
transport_error(&self) -> Result<(), TransportError>459 fn transport_error(&self) -> Result<(), TransportError> {
460 self.transport_error
461 }
462 }
463
464 impl FastbootInfoSend for FastbootInfoSender<'_> {
send(&mut self, msg: &str) -> Result<(), CommandError>465 fn send(&mut self, msg: &str) -> Result<(), CommandError> {
466 self.transport_error?;
467 let mut res = [0u8; MAX_RESPONSE_SIZE];
468 self.transport_error = self.transport.send_packet(fastboot_info!(res, "{}", msg));
469 Ok(self.transport_error?)
470 }
471 }
472
473 /// `UploadBuilder` can be consumed to create an `Uploader` for sending data to the host during
474 /// handling of command `fastboot get_staged`.
475 pub struct UploadBuilder<'a> {
476 remaining: &'a mut u64,
477 // `send` sends a bytes array as fastboot packet.
478 send: &'a mut dyn FnMut(&[u8]) -> Result<(), CommandError>,
479 }
480
481 impl<'a> UploadBuilder<'a> {
482 /// Consumes the builder to create an `Uploader` to start uploading data.
start(self, data_size: u64) -> Result<Uploader<'a>, CommandError>483 pub fn start(self, data_size: u64) -> Result<Uploader<'a>, CommandError> {
484 let mut res = [0u8; 16];
485 (self.send)(snprintf!(res, "DATA{:08x}", data_size).as_bytes())?;
486 *self.remaining = data_size;
487 Ok(Uploader { remaining: self.remaining, send: self.send })
488 }
489 }
490
491 /// `UploadBuilder` provides APIs for sending data from the device in response to
492 /// `fastboot get_staged`
493 pub struct Uploader<'a> {
494 remaining: &'a mut u64,
495 send: &'a mut dyn FnMut(&[u8]) -> Result<(), CommandError>,
496 }
497
498 impl<'a> Uploader<'a> {
499 /// Uploads data. Returns error if accumulative amount exceeds `data_size` passed to
500 /// `UploadBuilder::start()`.
upload(&mut self, data: &[u8]) -> Result<(), CommandError>501 pub fn upload(&mut self, data: &[u8]) -> Result<(), CommandError> {
502 *self.remaining = self
503 .remaining
504 .checked_sub(data.len().try_into().map_err(|_| "")?)
505 .ok_or::<CommandError>("".into())?;
506 (self.send)(data)
507 }
508 }
509
510 /// A helper function that creates an `UploadBuilder` from a `Transport` and runs a closure with
511 /// it. The helper internally checks that the closure uploads enough data it specifies.
with_upload_builder<F, R>( transport: &mut impl Transport, mut f: F, ) -> Result<Result<R, CommandError>, TransportError> where F: FnMut(UploadBuilder) -> Result<R, CommandError>,512 fn with_upload_builder<F, R>(
513 transport: &mut impl Transport,
514 mut f: F,
515 ) -> Result<Result<R, CommandError>, TransportError>
516 where
517 F: FnMut(UploadBuilder) -> Result<R, CommandError>,
518 {
519 let mut transport_error = Ok(());
520 let mut remaining = 0u64;
521 let mut send = |data: &[u8]| -> Result<(), CommandError> {
522 transport_error?;
523 transport_error = transport.send_packet(data);
524 Ok(transport_error?)
525 };
526 let upload_builder = UploadBuilder { remaining: &mut remaining, send: &mut send };
527 let res = f(upload_builder);
528 transport_error?;
529 // Failing to upload enough data should be considered a transport error. Because the remote
530 // host will hang as long as the connection is still active.
531 match remaining > 0 {
532 true => Err(TransportError::NotEnoughUpload),
533 _ => Ok(res),
534 }
535 }
536
537 pub mod test_utils {
538 use crate::{CommandError, UploadBuilder};
539
540 /// Runs a closure with a mock uploader for user implementation to test
541 /// `FastbootImplementation::upload()`.
542 ///
543 /// The mock uploader simply uploads to a user provided buffer.
544 ///
545 /// Returns the total uploaded size and remaining size.
with_mock_upload_builder<F>(buffer: &mut [u8], mut f: F) -> (usize, usize) where F: FnMut(UploadBuilder),546 pub fn with_mock_upload_builder<F>(buffer: &mut [u8], mut f: F) -> (usize, usize)
547 where
548 F: FnMut(UploadBuilder),
549 {
550 let mut remaining = 0u64;
551 let mut sent = 0;
552 let mut send = |data: &[u8]| -> Result<(), CommandError> {
553 // Skips the first 12 bytes "DATAXXXXXXXX" fastboot message.
554 match sent == 0 {
555 true => {
556 assert_eq!(data.len(), 12);
557 assert!(data.starts_with(b"DATA"));
558 sent += data.len()
559 }
560 _ => {
561 buffer[sent - 12..][..data.len()].clone_from_slice(data);
562 sent += data.len();
563 }
564 };
565 Ok(())
566 };
567 f(UploadBuilder { remaining: &mut remaining, send: &mut send });
568 (core::cmp::max(sent, 12) - 12, remaining.try_into().unwrap())
569 }
570 }
571
572 const MAX_DOWNLOAD_SIZE_NAME: &'static str = "max-download-size";
573
574 /// State of the fastboot protocol.
575 enum ProtocolState {
576 Command,
577 Download,
578 }
579
580 /// `Fastboot` provides methods for receiving/processing/replying fastboot commands from a
581 /// transport.
582 pub struct Fastboot<'a> {
583 state: ProtocolState,
584 download_buffer: &'a mut [u8],
585 downloaded_size: usize,
586 total_download_size: usize,
587 }
588
589 impl<'a> Fastboot<'a> {
590 /// Creates an instance with a given download buffer.
new(download_buffer: &'a mut [u8]) -> Self591 pub fn new(download_buffer: &'a mut [u8]) -> Self {
592 Self {
593 state: ProtocolState::Command,
594 download_buffer: download_buffer,
595 downloaded_size: 0,
596 total_download_size: 0,
597 }
598 }
599
600 /// Processes the next fastboot packet from a transport.
601 ///
602 /// # Args
603 ///
604 /// * `transport`: An implementation of `Transport`
605 /// * `fb_impl`: An implementation of `FastbootImplementation`.
606 ///
607 /// # Returns
608 ///
609 /// Returns error if any calls to `transport` methods return error.
610 /// Returns Ok(()) if transport doesn't have any next packet.
process_next_packet( &mut self, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<(), TransportError>611 pub fn process_next_packet(
612 &mut self,
613 transport: &mut impl Transport,
614 fb_impl: &mut impl FastbootImplementation,
615 ) -> Result<(), TransportError> {
616 match self.state {
617 ProtocolState::Command => {
618 let mut packet = [0u8; MAX_COMMAND_SIZE];
619 let cmd_size = transport.receive_packet(&mut packet[..])?;
620 if cmd_size == 0 {
621 return Ok(());
622 }
623
624 let mut res = [0u8; MAX_RESPONSE_SIZE];
625 let cmd_str = match from_utf8(&packet[..cmd_size]) {
626 Ok(s) => s,
627 _ => {
628 return transport.send_packet(fastboot_fail!(res, "Invalid Command"));
629 }
630 };
631 let mut args = cmd_str.split(':');
632 let Some(cmd) = args.next() else {
633 return transport.send_packet(fastboot_fail!(res, "No command"));
634 };
635 match cmd {
636 "getvar" => self.get_var(args, transport, fb_impl)?,
637 "download" => self.download(args, transport, fb_impl)?,
638 "flash" => self.flash(cmd_str, transport, fb_impl)?,
639 "upload" => self.upload(transport, fb_impl)?,
640 "fetch" => self.fetch(&cmd_str, args, transport, fb_impl)?,
641 _ if cmd_str.starts_with("oem ") => {
642 self.oem(&cmd_str[4..], transport, fb_impl)?;
643 }
644 _ => {
645 return transport.send_packet(fastboot_fail!(res, "Command not found"));
646 }
647 }
648 }
649 ProtocolState::Download => {
650 let (_, remains) = &mut self.download_buffer[..self.total_download_size]
651 .split_at_mut(self.downloaded_size);
652 match transport.receive_packet(remains) {
653 Ok(size) if size > remains.len() => {
654 let mut res = [0u8; MAX_RESPONSE_SIZE];
655 transport.send_packet(
656 snprintf!(res, "FAILMore data received then expected").as_bytes(),
657 )?;
658 self.total_download_size = 0;
659 self.downloaded_size = 0;
660 self.state = ProtocolState::Command;
661 }
662 Ok(size) => {
663 self.downloaded_size = self.downloaded_size.checked_add(size).unwrap();
664 if self.downloaded_size == self.total_download_size {
665 self.state = ProtocolState::Command;
666 transport.send_packet(OKAY.as_bytes())?;
667 }
668 }
669 Err(e) => {
670 self.total_download_size = 0;
671 self.downloaded_size = 0;
672 return Err(e);
673 }
674 }
675 }
676 };
677 Ok(())
678 }
679
680 /// Fetches and processes the next fastboot command from the transport.
681 ///
682 /// Returns Ok(()) if transport doesn't have any next packet.
process_next_command( &mut self, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<(), TransportError>683 pub fn process_next_command(
684 &mut self,
685 transport: &mut impl Transport,
686 fb_impl: &mut impl FastbootImplementation,
687 ) -> Result<(), TransportError> {
688 if !matches!(self.state, ProtocolState::Command) {
689 return Err(TransportError::InvalidState);
690 }
691 self.process_next_packet(transport, fb_impl)?;
692 // Keep processing until it is back to the command state.
693 while !matches!(self.state, ProtocolState::Command) {
694 self.process_next_packet(transport, fb_impl)?;
695 }
696 Ok(())
697 }
698
699 /// Keeps polling and processing fastboot commands from the transport.
run( &mut self, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<(), TransportError>700 pub fn run(
701 &mut self,
702 transport: &mut impl Transport,
703 fb_impl: &mut impl FastbootImplementation,
704 ) -> Result<(), TransportError> {
705 loop {
706 self.process_next_command(transport, fb_impl)?;
707 }
708 }
709
710 /// Runs a fastboot over TCP session.
711 ///
712 /// The method performs fastboot over TCP handshake and then call `Self::run(...)`.
run_tcp_session( &mut self, tcp_stream: &mut dyn TcpStream, fb_impl: &mut impl FastbootImplementation, ) -> Result<(), TransportError>713 pub fn run_tcp_session(
714 &mut self,
715 tcp_stream: &mut dyn TcpStream,
716 fb_impl: &mut impl FastbootImplementation,
717 ) -> Result<(), TransportError> {
718 self.run(&mut TcpTransport::new_and_handshake(tcp_stream)?, fb_impl)
719 }
720
721 /// Method for handling "fastboot getvar ..."
get_var( &mut self, mut args: Split<char>, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<(), TransportError>722 fn get_var(
723 &mut self,
724 mut args: Split<char>,
725 transport: &mut impl Transport,
726 fb_impl: &mut impl FastbootImplementation,
727 ) -> Result<(), TransportError> {
728 let mut res = [0u8; MAX_RESPONSE_SIZE];
729 let Some(var) = args.next() else {
730 return transport.send_packet(fastboot_fail!(res, "Missing variable"));
731 };
732
733 if var == "all" {
734 return self.get_var_all(transport, fb_impl);
735 } else if var == MAX_DOWNLOAD_SIZE_NAME {
736 return transport.send_packet(fastboot_okay!(res, "{:#x}", self.download_buffer.len()));
737 }
738
739 let mut val = [0u8; MAX_RESPONSE_SIZE];
740 match self.get_var_str(var, args, &mut val[..], transport, fb_impl) {
741 Ok(s) => transport.send_packet(fastboot_okay!(res, "{}", s)),
742 Err(e) => transport.send_packet(fastboot_fail!(res, "{}", e.to_str())),
743 }
744 }
745
746 /// A helper for getting the string version of a fastboot variable value.
get_var_str<'s>( &mut self, var: &str, args: Split<char>, out: &'s mut [u8], transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<&'s str, CommandError>747 fn get_var_str<'s>(
748 &mut self,
749 var: &str,
750 args: Split<char>,
751 out: &'s mut [u8],
752 transport: &mut impl Transport,
753 fb_impl: &mut impl FastbootImplementation,
754 ) -> Result<&'s str, CommandError> {
755 let mut info_sender = FastbootInfoSender::new(transport);
756 let mut utils = self.utils(Some(&mut info_sender));
757 fb_impl.get_var_as_str(var, args, out, &mut utils)
758 }
759
760 /// A wrapper of `get_var_all()` that first iterates reserved variables.
get_var_all_with_native( &mut self, fb_impl: &mut impl FastbootImplementation, f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>, ) -> Result<(), CommandError>761 fn get_var_all_with_native(
762 &mut self,
763 fb_impl: &mut impl FastbootImplementation,
764 f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>,
765 ) -> Result<(), CommandError> {
766 // Process the built-in MAX_DOWNLOAD_SIZE_NAME variable.
767 let mut size_str = [0u8; 32];
768 f(MAX_DOWNLOAD_SIZE_NAME, &[], snprintf!(size_str, "{:#x}", self.download_buffer.len()))?;
769 // Don't allow other custom INFO messages because variable values are sent as INFO
770 // messages.
771 fb_impl.get_var_all(f, &mut self.utils(None))
772 }
773
774 /// Method for handling "fastboot getvar all"
get_var_all( &mut self, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<(), TransportError>775 fn get_var_all(
776 &mut self,
777 transport: &mut impl Transport,
778 fb_impl: &mut impl FastbootImplementation,
779 ) -> Result<(), TransportError> {
780 let mut res = [0u8; MAX_RESPONSE_SIZE];
781 let mut transport_error = Ok(());
782 let get_res = self.get_var_all_with_native(fb_impl, &mut |name, args, val| {
783 let mut formatted_bytes = FormattedBytes::new(&mut res);
784 write!(formatted_bytes, "INFO{}", name).unwrap();
785 args.iter().for_each(|arg| write!(formatted_bytes, ":{}", arg).unwrap());
786 write!(formatted_bytes, ": {}", val).unwrap();
787 let size = formatted_bytes.size();
788 transport_error = transport.send_packet(&res[..size]);
789 Ok(transport_error?)
790 });
791 transport_error?;
792 match get_res {
793 Ok(()) => transport.send_packet(fastboot_okay!(res, "")),
794 Err(e) => transport.send_packet(fastboot_fail!(res, "{}", e.to_str())),
795 }
796 }
797
798 /// Method for handling "fastboot download:...".
download( &mut self, mut args: Split<char>, transport: &mut impl Transport, _: &mut impl FastbootImplementation, ) -> Result<(), TransportError>799 fn download(
800 &mut self,
801 mut args: Split<char>,
802 transport: &mut impl Transport,
803 _: &mut impl FastbootImplementation,
804 ) -> Result<(), TransportError> {
805 let mut res = [0u8; MAX_RESPONSE_SIZE];
806 let total_download_size = match (|| -> Result<usize, CommandError> {
807 usize::try_from(next_arg_u64(&mut args, Err("Not enough argument".into()))?)
808 .map_err(|_| "Download size overflow".into())
809 })() {
810 Err(e) => return transport.send_packet(fastboot_fail!(res, "{}", e.to_str())),
811 Ok(v) => v,
812 };
813 if total_download_size > self.download_buffer.len() {
814 return transport.send_packet(fastboot_fail!(res, "Download size is too big"));
815 } else if total_download_size == 0 {
816 return transport.send_packet(fastboot_fail!(res, "Zero download size"));
817 }
818
819 transport.send_packet(snprintf!(res, "DATA{:#x}", total_download_size).as_bytes())?;
820 self.total_download_size = total_download_size;
821 self.downloaded_size = 0;
822 self.state = ProtocolState::Download;
823 Ok(())
824 }
825
826 /// Method for handling "fastboot flash ...".
flash( &mut self, cmd: &str, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<(), TransportError>827 fn flash(
828 &mut self,
829 cmd: &str,
830 transport: &mut impl Transport,
831 fb_impl: &mut impl FastbootImplementation,
832 ) -> Result<(), TransportError> {
833 let mut res = [0u8; MAX_RESPONSE_SIZE];
834 match (|| -> Result<(), CommandError> {
835 let part =
836 cmd.strip_prefix("flash:").ok_or::<CommandError>("Missing partition".into())?;
837 fb_impl.flash(part, &mut self.utils(Some(&mut FastbootInfoSender::new(transport))))
838 })() {
839 Err(e) => transport.send_packet(fastboot_fail!(res, "{}", e.to_str())),
840 _ => transport.send_packet(fastboot_okay!(res, "")),
841 }
842 }
843
844 /// Method for handling "fastboot get_staged ...".
upload( &mut self, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<(), TransportError>845 fn upload(
846 &mut self,
847 transport: &mut impl Transport,
848 fb_impl: &mut impl FastbootImplementation,
849 ) -> Result<(), TransportError> {
850 let mut res = [0u8; MAX_RESPONSE_SIZE];
851 match with_upload_builder(transport, |upload_builder| {
852 // No INFO message should be sent during upload.
853 let mut utils = self.utils(None);
854 fb_impl.upload(upload_builder, &mut utils)
855 })? {
856 Err(e) => transport.send_packet(fastboot_fail!(res, "{}", e.to_str())),
857 _ => transport.send_packet(fastboot_okay!(res, "")),
858 }
859 }
860
861 /// Method for handling "fastboot fetch ...".
fetch( &mut self, cmd: &str, args: Split<char>, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<(), TransportError>862 pub fn fetch(
863 &mut self,
864 cmd: &str,
865 args: Split<char>,
866 transport: &mut impl Transport,
867 fb_impl: &mut impl FastbootImplementation,
868 ) -> Result<(), TransportError> {
869 let mut res = [0u8; MAX_RESPONSE_SIZE];
870 match with_upload_builder(transport, |upload_builder| -> Result<(), CommandError> {
871 let cmd =
872 cmd.strip_prefix("fetch:").ok_or::<CommandError>("Missing arguments".into())?;
873 if args.clone().count() < 3 {
874 return Err("Not enough argments".into());
875 }
876 // Parses backward. Parses size, offset first and treats the remaining string as
877 // partition name. This allows ":" in partition name.
878 let mut rev = args.clone().rev();
879 let sz = next_arg(&mut rev, Err("Invalid argument".into()))?;
880 let off = next_arg(&mut rev, Err("Invalid argument".into()))?;
881 let part = &cmd[..cmd.len() - (off.len() + sz.len() + 2)];
882 // No INFO message should be sent during upload.
883 let mut utils = self.utils(None);
884 fb_impl.fetch(part, hex_to_u64(off)?, hex_to_u64(sz)?, upload_builder, &mut utils)
885 })? {
886 Err(e) => transport.send_packet(fastboot_fail!(res, "{}", e.to_str())),
887 _ => transport.send_packet(fastboot_okay!(res, "")),
888 }
889 }
890
891 /// Method for handling "fastboot oem ...".
oem( &mut self, cmd: &str, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<(), TransportError>892 fn oem(
893 &mut self,
894 cmd: &str,
895 transport: &mut impl Transport,
896 fb_impl: &mut impl FastbootImplementation,
897 ) -> Result<(), TransportError> {
898 let mut info_sender = FastbootInfoSender::new(transport);
899 let mut utils = self.utils(Some(&mut info_sender));
900 let mut oem_out = [0u8; MAX_RESPONSE_SIZE - 4];
901 let oem_res = fb_impl.oem(cmd, &mut utils, &mut oem_out[..]);
902 info_sender.transport_error()?;
903 let mut res = [0u8; MAX_RESPONSE_SIZE];
904 match oem_res {
905 Ok(msg) => match from_utf8(msg) {
906 Ok(s) => transport.send_packet(fastboot_okay!(res, "{}", s)),
907 Err(e) => transport.send_packet(fastboot_fail!(res, "Invalid return string {}", e)),
908 },
909 Err(e) => transport.send_packet(fastboot_fail!(res, "{}", e.to_str())),
910 }
911 }
912
913 /// Helper method to create an instance of `FastbootUtils`.
utils<'b>(&'b mut self, info: Option<&'b mut dyn FastbootInfoSend>) -> FastbootUtils<'b>914 fn utils<'b>(&'b mut self, info: Option<&'b mut dyn FastbootInfoSend>) -> FastbootUtils<'b> {
915 FastbootUtils::new(self.download_buffer, &mut self.total_download_size, info.map(|v| v))
916 }
917 }
918
919 /// A helper data structure for writing formatted string to fixed size bytes array.
920 #[derive(Debug)]
921 pub struct FormattedBytes<T: AsMut<[u8]>>(T, usize);
922
923 impl<T: AsMut<[u8]>> FormattedBytes<T> {
924 /// Create an instance.
new(buf: T) -> Self925 pub fn new(buf: T) -> Self {
926 Self(buf, 0)
927 }
928
929 /// Get the size of content.
size(&self) -> usize930 pub fn size(&self) -> usize {
931 self.1
932 }
933
append(&mut self, bytes: &[u8]) -> &mut [u8]934 pub fn append(&mut self, bytes: &[u8]) -> &mut [u8] {
935 let buf = &mut self.0.as_mut()[self.1..];
936 // Only write as much as the size of the bytes buffer. Additional write is silently
937 // ignored.
938 let to_write = core::cmp::min(buf.len(), bytes.len());
939 buf[..to_write].clone_from_slice(&bytes[..to_write]);
940 self.1 += to_write;
941 &mut self.0.as_mut()[..self.1]
942 }
943 }
944
945 impl<T: AsMut<[u8]>> core::fmt::Write for FormattedBytes<T> {
write_str(&mut self, s: &str) -> core::fmt::Result946 fn write_str(&mut self, s: &str) -> core::fmt::Result {
947 self.append(s.as_bytes());
948 Ok(())
949 }
950 }
951
952 /// A convenient macro that behaves similar to snprintf in C.
953 #[macro_export]
954 macro_rules! snprintf {
955 ( $arr:expr, $( $x:expr ),* ) => {
956 {
957 let mut formatted_bytes = FormattedBytes::new(&mut $arr[..]);
958 write!(formatted_bytes, $($x,)*).unwrap();
959 let size = formatted_bytes.size();
960 from_utf8(&$arr[..size]).unwrap()
961 }
962 };
963 }
964
965 /// A helper to convert a hex string into u64.
hex_to_u64(s: &str) -> Result<u64, CommandError>966 pub(crate) fn hex_to_u64(s: &str) -> Result<u64, CommandError> {
967 Ok(u64::from_str_radix(s.strip_prefix("0x").unwrap_or(s), 16)?)
968 }
969
970 /// A helper to check and fetch the next argument or fall back to the default if not available.
971 ///
972 /// # Args
973 ///
974 /// args: A string iterator.
975 /// default: This will be returned as it is if args doesn't have the next element(). Providing a
976 /// Ok(str) is equivalent to providing a default value. Providing an Err() is equivalent to
977 /// requiring that the next argument is mandatory.
next_arg<'a, T: Iterator<Item = &'a str>>( args: &mut T, default: Result<&'a str, CommandError>, ) -> Result<&'a str, CommandError>978 pub fn next_arg<'a, T: Iterator<Item = &'a str>>(
979 args: &mut T,
980 default: Result<&'a str, CommandError>,
981 ) -> Result<&'a str, CommandError> {
982 args.next().filter(|v| *v != "").ok_or("").or(default.map_err(|e| e.into()))
983 }
984
985 /// A helper to check and fetch the next argument as a u64 hex string.
986 ///
987 /// # Args
988 ///
989 /// args: A string iterator.
990 /// default: This will be returned as it is if args doesn't have the next element(). Providing a
991 /// Ok(u64) is equivalent to providing a default value. Providing an Err() is equivalent to
992 /// requiring that the next argument is mandatory.
993 ///
994 /// Returns error if the next argument is not a valid hex string.
next_arg_u64<'a, T: Iterator<Item = &'a str>>( args: &mut T, default: Result<u64, CommandError>, ) -> Result<u64, CommandError>995 pub fn next_arg_u64<'a, T: Iterator<Item = &'a str>>(
996 args: &mut T,
997 default: Result<u64, CommandError>,
998 ) -> Result<u64, CommandError> {
999 match next_arg(args, Err("".into())) {
1000 Ok(v) => hex_to_u64(v),
1001 _ => default.map_err(|e| e.into()),
1002 }
1003 }
1004
1005 #[cfg(test)]
1006 mod test {
1007 use super::*;
1008 use std::collections::{BTreeMap, VecDeque};
1009
1010 #[derive(Default)]
1011 struct FastbootTest<'a> {
1012 // A mapping from (variable name, argument) to variable value.
1013 vars: BTreeMap<(&'static str, &'static [&'static str]), &'static str>,
1014 flash_cb: Option<&'a mut dyn FnMut(&str, &mut FastbootUtils) -> Result<(), CommandError>>,
1015 upload_cb: Option<
1016 &'a mut dyn FnMut(UploadBuilder, &mut FastbootUtils) -> Result<(), CommandError>,
1017 >,
1018 fetch_cb: Option<
1019 &'a mut dyn FnMut(
1020 &str,
1021 u64,
1022 u64,
1023 UploadBuilder,
1024 &mut FastbootUtils,
1025 ) -> Result<(), CommandError>,
1026 >,
1027 oem_cb: Option<
1028 &'a mut dyn FnMut(&str, &mut FastbootUtils, &mut [u8]) -> Result<usize, CommandError>,
1029 >,
1030 }
1031
1032 impl FastbootImplementation for FastbootTest<'_> {
get_var( &mut self, var: &str, args: Split<char>, out: &mut [u8], _: &mut FastbootUtils, ) -> Result<usize, CommandError>1033 fn get_var(
1034 &mut self,
1035 var: &str,
1036 args: Split<char>,
1037 out: &mut [u8],
1038 _: &mut FastbootUtils,
1039 ) -> Result<usize, CommandError> {
1040 let args = args.collect::<Vec<_>>();
1041 match self.vars.get(&(var, &args[..])) {
1042 Some(v) => {
1043 out[..v.len()].clone_from_slice(v.as_bytes());
1044 Ok(v.len())
1045 }
1046 _ => Err("Not Found".into()),
1047 }
1048 }
1049
get_var_all( &mut self, f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>, _: &mut FastbootUtils, ) -> Result<(), CommandError>1050 fn get_var_all(
1051 &mut self,
1052 f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>,
1053 _: &mut FastbootUtils,
1054 ) -> Result<(), CommandError> {
1055 for ((var, config), value) in &self.vars {
1056 f(var, config, value)?;
1057 }
1058 Ok(())
1059 }
1060
flash(&mut self, part: &str, utils: &mut FastbootUtils) -> Result<(), CommandError>1061 fn flash(&mut self, part: &str, utils: &mut FastbootUtils) -> Result<(), CommandError> {
1062 (self.flash_cb.as_mut().unwrap())(part, utils)
1063 }
1064
upload( &mut self, upload_builder: UploadBuilder, utils: &mut FastbootUtils, ) -> Result<(), CommandError>1065 fn upload(
1066 &mut self,
1067 upload_builder: UploadBuilder,
1068 utils: &mut FastbootUtils,
1069 ) -> Result<(), CommandError> {
1070 (self.upload_cb.as_mut().unwrap())(upload_builder, utils)
1071 }
1072
fetch( &mut self, part: &str, offset: u64, size: u64, upload_builder: UploadBuilder, utils: &mut FastbootUtils, ) -> Result<(), CommandError>1073 fn fetch(
1074 &mut self,
1075 part: &str,
1076 offset: u64,
1077 size: u64,
1078 upload_builder: UploadBuilder,
1079 utils: &mut FastbootUtils,
1080 ) -> Result<(), CommandError> {
1081 (self.fetch_cb.as_mut().unwrap())(part, offset, size, upload_builder, utils)
1082 }
1083
oem<'b>( &mut self, cmd: &str, utils: &mut FastbootUtils, res: &'b mut [u8], ) -> Result<&'b [u8], CommandError>1084 fn oem<'b>(
1085 &mut self,
1086 cmd: &str,
1087 utils: &mut FastbootUtils,
1088 res: &'b mut [u8],
1089 ) -> Result<&'b [u8], CommandError> {
1090 let sz = (self.oem_cb.as_mut().unwrap())(cmd, utils, res)?;
1091 Ok(&res[..sz])
1092 }
1093 }
1094
1095 struct TestTransport {
1096 in_queue: VecDeque<Vec<u8>>,
1097 out_queue: VecDeque<Vec<u8>>,
1098 }
1099
1100 impl TestTransport {
new() -> Self1101 fn new() -> Self {
1102 Self { in_queue: VecDeque::new(), out_queue: VecDeque::new() }
1103 }
1104
add_input(&mut self, packet: &[u8])1105 fn add_input(&mut self, packet: &[u8]) {
1106 self.in_queue.push_back(packet.into());
1107 }
1108 }
1109
1110 impl Transport for TestTransport {
receive_packet(&mut self, out: &mut [u8]) -> Result<usize, TransportError>1111 fn receive_packet(&mut self, out: &mut [u8]) -> Result<usize, TransportError> {
1112 match self.in_queue.pop_front() {
1113 Some(v) => {
1114 let size = core::cmp::min(out.len(), v.len());
1115 out[..size].clone_from_slice(&v[..size]);
1116 // Returns the input length so that we can test bogus download size.
1117 Ok(v.len())
1118 }
1119 _ => Err(TransportError::Others("No more data")),
1120 }
1121 }
1122
send_packet(&mut self, packet: &[u8]) -> Result<(), TransportError>1123 fn send_packet(&mut self, packet: &[u8]) -> Result<(), TransportError> {
1124 self.out_queue.push_back(packet.into());
1125 Ok(())
1126 }
1127 }
1128
1129 #[derive(Default)]
1130 struct TestTcpStream {
1131 in_queue: VecDeque<u8>,
1132 out_queue: VecDeque<u8>,
1133 }
1134
1135 impl TestTcpStream {
1136 /// Adds bytes to input stream.
add_input(&mut self, data: &[u8])1137 fn add_input(&mut self, data: &[u8]) {
1138 data.iter().for_each(|v| self.in_queue.push_back(*v));
1139 }
1140
1141 /// Adds a length pre-fixed bytes stream.
add_length_prefixed_input(&mut self, data: &[u8])1142 fn add_length_prefixed_input(&mut self, data: &[u8]) {
1143 self.add_input(&(data.len() as u64).to_be_bytes());
1144 self.add_input(data);
1145 }
1146 }
1147
1148 impl TcpStream for TestTcpStream {
read_exact(&mut self, out: &mut [u8]) -> Result<(), TransportError>1149 fn read_exact(&mut self, out: &mut [u8]) -> Result<(), TransportError> {
1150 for ele in out {
1151 *ele = self.in_queue.pop_front().ok_or(TransportError::Others("No more data"))?;
1152 }
1153 Ok(())
1154 }
1155
write_exact(&mut self, data: &[u8]) -> Result<(), TransportError>1156 fn write_exact(&mut self, data: &[u8]) -> Result<(), TransportError> {
1157 data.iter().for_each(|v| self.out_queue.push_back(*v));
1158 Ok(())
1159 }
1160 }
1161
1162 #[test]
test_non_exist_command()1163 fn test_non_exist_command() {
1164 let mut fastboot_impl: FastbootTest = Default::default();
1165 let mut download_buffer = vec![0u8; 1024];
1166 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1167 let mut transport = TestTransport::new();
1168 transport.add_input(b"non_exist");
1169 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1170 assert_eq!(transport.out_queue, [b"FAILCommand not found"]);
1171 }
1172
1173 #[test]
test_non_ascii_command_string()1174 fn test_non_ascii_command_string() {
1175 let mut fastboot_impl: FastbootTest = Default::default();
1176 let mut download_buffer = vec![0u8; 1024];
1177 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1178 let mut transport = TestTransport::new();
1179 transport.add_input(b"\xff\xff\xff");
1180 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1181 assert_eq!(transport.out_queue, [b"FAILInvalid Command"]);
1182 }
1183
1184 #[test]
test_get_var_max_download_size()1185 fn test_get_var_max_download_size() {
1186 let mut fastboot_impl: FastbootTest = Default::default();
1187 let mut download_buffer = vec![0u8; 1024];
1188 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1189 let mut transport = TestTransport::new();
1190 transport.add_input(b"getvar:max-download-size");
1191 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1192 assert_eq!(transport.out_queue, [b"OKAY0x400"]);
1193 }
1194
1195 #[test]
test_get_var()1196 fn test_get_var() {
1197 let mut fastboot_impl: FastbootTest = Default::default();
1198 let vars: [((&str, &[&str]), &str); 4] = [
1199 (("var_0", &[]), "val_0"),
1200 (("var_1", &["a", "b"]), "val_1_a_b"),
1201 (("var_1", &["c", "d"]), "val_1_c_d"),
1202 (("var_2", &["e", "f"]), "val_2_e_f"),
1203 ];
1204 fastboot_impl.vars = BTreeMap::from(vars);
1205
1206 let mut download_buffer = vec![0u8; 1024];
1207 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1208 let mut transport = TestTransport::new();
1209 transport.add_input(b"getvar:var_0");
1210 transport.add_input(b"getvar:var_1:a:b");
1211 transport.add_input(b"getvar:var_1:c:d");
1212 transport.add_input(b"getvar:var_1"); // Not Found
1213 transport.add_input(b"getvar:var_2:e:f");
1214 transport.add_input(b"getvar:var_3"); // Not Found
1215 transport.add_input(b"getvar"); // Not Found
1216
1217 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1218 assert_eq!(
1219 transport.out_queue,
1220 VecDeque::<Vec<u8>>::from([
1221 b"OKAYval_0".into(),
1222 b"OKAYval_1_a_b".into(),
1223 b"OKAYval_1_c_d".into(),
1224 b"FAILNot Found".into(),
1225 b"OKAYval_2_e_f".into(),
1226 b"FAILNot Found".into(),
1227 b"FAILMissing variable".into(),
1228 ])
1229 );
1230 }
1231
1232 #[test]
test_get_var_all()1233 fn test_get_var_all() {
1234 let mut fastboot_impl: FastbootTest = Default::default();
1235 let vars: [((&str, &[&str]), &str); 4] = [
1236 (("var_0", &[]), "val_0"),
1237 (("var_1", &["a", "b"]), "val_1_a_b"),
1238 (("var_1", &["c", "d"]), "val_1_c_d"),
1239 (("var_2", &["e", "f"]), "val_2_e_f"),
1240 ];
1241 fastboot_impl.vars = BTreeMap::from(vars);
1242
1243 let mut download_buffer = vec![0u8; 1024];
1244 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1245 let mut transport = TestTransport::new();
1246 transport.add_input(b"getvar:all");
1247 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1248 assert_eq!(
1249 transport.out_queue,
1250 VecDeque::<Vec<u8>>::from([
1251 b"INFOmax-download-size: 0x400".into(),
1252 b"INFOvar_0: val_0".into(),
1253 b"INFOvar_1:a:b: val_1_a_b".into(),
1254 b"INFOvar_1:c:d: val_1_c_d".into(),
1255 b"INFOvar_2:e:f: val_2_e_f".into(),
1256 b"OKAY".into(),
1257 ])
1258 );
1259 }
1260
1261 #[test]
test_download()1262 fn test_download() {
1263 let mut fastboot_impl: FastbootTest = Default::default();
1264 let mut download_buffer = vec![0u8; 1024];
1265 let download_content: Vec<u8> =
1266 (0..download_buffer.len()).into_iter().map(|v| v as u8).collect();
1267 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1268 let mut transport = TestTransport::new();
1269 // Splits download into two batches.
1270 let (first, second) = download_content.as_slice().split_at(download_content.len() / 2);
1271 transport.add_input(format!("download:{:#x}", download_content.len()).as_bytes());
1272 transport.add_input(first);
1273 transport.add_input(second);
1274 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1275 assert_eq!(
1276 transport.out_queue,
1277 VecDeque::<Vec<u8>>::from([b"DATA0x400".into(), b"OKAY".into(),])
1278 );
1279 assert_eq!(download_buffer, download_content);
1280 }
1281
1282 #[test]
test_download_not_enough_args()1283 fn test_download_not_enough_args() {
1284 let mut fastboot_impl: FastbootTest = Default::default();
1285 let mut download_buffer = vec![0u8; 1024];
1286 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1287 let mut transport = TestTransport::new();
1288 transport.add_input(b"download");
1289 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1290 assert_eq!(transport.out_queue, [b"FAILNot enough argument"]);
1291 }
1292
1293 #[test]
test_download_invalid_hex_string()1294 fn test_download_invalid_hex_string() {
1295 let mut fastboot_impl: FastbootTest = Default::default();
1296 let mut download_buffer = vec![0u8; 1024];
1297 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1298 let mut transport = TestTransport::new();
1299 transport.add_input(b"download:hhh");
1300 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1301 assert_eq!(transport.out_queue.len(), 1);
1302 assert!(transport.out_queue[0].starts_with(b"FAIL"));
1303 }
1304
test_download_size(download_buffer_size: usize, download_size: usize, msg: &str)1305 fn test_download_size(download_buffer_size: usize, download_size: usize, msg: &str) {
1306 let mut fastboot_impl: FastbootTest = Default::default();
1307 let mut download_buffer = vec![0u8; download_buffer_size];
1308 let mut transport = TestTransport::new();
1309 transport.add_input(format!("download:{:#x}", download_size).as_bytes());
1310 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1311 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1312 assert_eq!(transport.out_queue, VecDeque::<Vec<u8>>::from([msg.as_bytes().into()]));
1313 }
1314
1315 #[test]
test_download_download_size_too_big()1316 fn test_download_download_size_too_big() {
1317 test_download_size(1024, 1025, "FAILDownload size is too big");
1318 }
1319
1320 #[test]
test_download_zero_download_size()1321 fn test_download_zero_download_size() {
1322 test_download_size(1024, 0, "FAILZero download size");
1323 }
1324
1325 #[test]
test_download_more_than_expected()1326 fn test_download_more_than_expected() {
1327 let mut fastboot_impl: FastbootTest = Default::default();
1328 let mut download_buffer = vec![0u8; 1024];
1329 let download_content: Vec<u8> = vec![0u8; download_buffer.len()];
1330 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1331 let mut transport = TestTransport::new();
1332 transport.add_input(format!("download:{:#x}", download_content.len() - 1).as_bytes());
1333 transport.add_input(&download_content[..]);
1334 // State should be reset to command state.
1335 transport.add_input(b"getvar:max-download-size");
1336 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1337 assert_eq!(
1338 transport.out_queue,
1339 VecDeque::<Vec<u8>>::from([
1340 b"DATA0x3ff".into(),
1341 b"FAILMore data received then expected".into(),
1342 b"OKAY0x400".into(),
1343 ])
1344 );
1345 }
1346
1347 #[test]
test_oem_cmd()1348 fn test_oem_cmd() {
1349 let mut fastboot_impl: FastbootTest = Default::default();
1350 const DOWNLOAD_BUFFER_LEN: usize = 2048;
1351 let mut download_buffer = vec![0u8; DOWNLOAD_BUFFER_LEN];
1352 let download_content: Vec<u8> = (0..1024).into_iter().map(|v| v as u8).collect();
1353 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1354 let mut transport = TestTransport::new();
1355 transport.add_input(format!("download:{:#x}", download_content.len()).as_bytes());
1356 transport.add_input(&download_content[..]);
1357 transport.add_input(b"oem oem-command");
1358
1359 let mut oem_cb = |cmd: &str, utils: &mut FastbootUtils, res: &mut [u8]| {
1360 assert_eq!(cmd, "oem-command");
1361 assert_eq!(utils.download_buffer.len(), DOWNLOAD_BUFFER_LEN);
1362 assert_eq!(utils.download_data().to_vec(), download_content);
1363 assert!(utils.info_send("oem-info-1").unwrap());
1364 assert!(utils.info_send("oem-info-2").unwrap());
1365 Ok(snprintf!(res, "oem-return").len())
1366 };
1367 fastboot_impl.oem_cb = Some(&mut oem_cb);
1368 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1369 assert_eq!(
1370 transport.out_queue,
1371 VecDeque::<Vec<u8>>::from([
1372 b"DATA0x400".into(),
1373 b"OKAY".into(),
1374 b"INFOoem-info-1".into(),
1375 b"INFOoem-info-2".into(),
1376 b"OKAYoem-return".into(),
1377 ])
1378 );
1379 }
1380
1381 #[test]
test_flash()1382 fn test_flash() {
1383 let mut fastboot_impl: FastbootTest = Default::default();
1384 const DOWNLOAD_BUFFER_LEN: usize = 2048;
1385 let mut download_buffer = vec![0u8; DOWNLOAD_BUFFER_LEN];
1386 let download_content: Vec<u8> = (0..1024).into_iter().map(|v| v as u8).collect();
1387 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1388 let mut transport = TestTransport::new();
1389 transport.add_input(format!("download:{:#x}", download_content.len()).as_bytes());
1390 transport.add_input(&download_content[..]);
1391 transport.add_input(b"flash:boot_a:0::");
1392
1393 let mut flash_cb = |part: &str, utils: &mut FastbootUtils| {
1394 assert_eq!(part, "boot_a:0::");
1395 assert_eq!(utils.download_data().to_vec(), download_content);
1396 Ok(())
1397 };
1398 fastboot_impl.flash_cb = Some(&mut flash_cb);
1399 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1400 assert_eq!(
1401 transport.out_queue,
1402 VecDeque::<Vec<u8>>::from([b"DATA0x400".into(), b"OKAY".into(), b"OKAY".into(),])
1403 );
1404 }
1405
1406 #[test]
test_flash_missing_partition()1407 fn test_flash_missing_partition() {
1408 let mut fastboot_impl: FastbootTest = Default::default();
1409 let mut fastboot = Fastboot::new(&mut []);
1410 let mut transport = TestTransport::new();
1411 transport.add_input(b"flash");
1412 let mut flash_cb = |_: &str, _: &mut FastbootUtils| Ok(());
1413 fastboot_impl.flash_cb = Some(&mut flash_cb);
1414 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1415 assert_eq!(transport.out_queue, [b"FAILMissing partition"]);
1416 }
1417
1418 #[test]
test_upload()1419 fn test_upload() {
1420 let mut fastboot_impl: FastbootTest = Default::default();
1421 const DOWNLOAD_BUFFER_LEN: usize = 2048;
1422 let mut download_buffer = vec![0u8; DOWNLOAD_BUFFER_LEN];
1423 let download_content: Vec<u8> = (0..1024).into_iter().map(|v| v as u8).collect();
1424 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1425 let mut transport = TestTransport::new();
1426 transport.add_input(format!("download:{:#x}", download_content.len()).as_bytes());
1427 transport.add_input(&download_content[..]);
1428 transport.add_input(b"upload");
1429
1430 let mut upload_cb = |upload_builder: UploadBuilder, utils: &mut FastbootUtils| {
1431 assert_eq!(utils.download_buffer.len(), DOWNLOAD_BUFFER_LEN);
1432 assert_eq!(utils.download_data().to_vec(), download_content);
1433 let (download_buffer, download_len) = utils.take_download_buffer();
1434 let to_send = &mut download_buffer[..download_len];
1435 let mut uploader = upload_builder.start(u64::try_from(to_send.len()).unwrap()).unwrap();
1436 uploader.upload(&to_send[..download_len / 2]).unwrap();
1437 uploader.upload(&to_send[download_len / 2..]).unwrap();
1438 assert!(!utils.info_send("").unwrap());
1439 assert_eq!(utils.download_data().len(), 0);
1440 Ok(())
1441 };
1442 fastboot_impl.upload_cb = Some(&mut upload_cb);
1443 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1444 assert_eq!(
1445 transport.out_queue,
1446 VecDeque::<Vec<u8>>::from([
1447 b"DATA0x400".into(),
1448 b"OKAY".into(),
1449 b"DATA00000400".into(),
1450 download_content[..download_content.len() / 2].to_vec(),
1451 download_content[download_content.len() / 2..].to_vec(),
1452 b"OKAY".into(),
1453 ])
1454 );
1455 }
1456
1457 #[test]
test_upload_not_enough_data()1458 fn test_upload_not_enough_data() {
1459 let mut fastboot_impl: FastbootTest = Default::default();
1460 let mut download_buffer = vec![0u8; 2048];
1461 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1462 let mut transport = TestTransport::new();
1463 transport.add_input(b"upload");
1464
1465 let mut upload_cb = |upload_builder: UploadBuilder, _: &mut FastbootUtils| {
1466 let mut uploader = upload_builder.start(0x400).unwrap();
1467 uploader.upload(&[0u8; 0x400 - 1]).unwrap();
1468 Ok(())
1469 };
1470 fastboot_impl.upload_cb = Some(&mut upload_cb);
1471 assert!(fastboot.run(&mut transport, &mut fastboot_impl).is_err());
1472 }
1473
1474 #[test]
test_upload_more_data()1475 fn test_upload_more_data() {
1476 let mut fastboot_impl: FastbootTest = Default::default();
1477 let mut download_buffer = vec![0u8; 2048];
1478 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1479 let mut transport = TestTransport::new();
1480 transport.add_input(b"upload");
1481
1482 let mut upload_cb = |upload_builder: UploadBuilder, _: &mut FastbootUtils| {
1483 let mut uploader = upload_builder.start(0x400).unwrap();
1484 uploader.upload(&[0u8; 0x400 + 1])?;
1485 Ok(())
1486 };
1487 fastboot_impl.upload_cb = Some(&mut upload_cb);
1488 assert!(fastboot.run(&mut transport, &mut fastboot_impl).is_err());
1489 }
1490
1491 #[test]
test_fetch()1492 fn test_fetch() {
1493 let mut fastboot_impl: FastbootTest = Default::default();
1494 let mut download_buffer = vec![0u8; 2048];
1495 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1496 let mut transport = TestTransport::new();
1497 transport.add_input(b"fetch:boot_a:0:::200:400");
1498
1499 let mut fetch_cb = |part: &str,
1500 offset: u64,
1501 size: u64,
1502 upload_builder: UploadBuilder,
1503 _: &mut FastbootUtils| {
1504 assert_eq!(part, "boot_a:0::");
1505 assert_eq!(offset, 0x200);
1506 assert_eq!(size, 0x400);
1507 let mut uploader = upload_builder.start(size)?;
1508 uploader.upload(&vec![0u8; size.try_into().unwrap()][..])?;
1509 Ok(())
1510 };
1511 fastboot_impl.fetch_cb = Some(&mut fetch_cb);
1512 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1513 assert_eq!(
1514 transport.out_queue,
1515 VecDeque::<Vec<u8>>::from([
1516 b"DATA00000400".into(),
1517 [0u8; 0x400].to_vec(),
1518 b"OKAY".into(),
1519 ])
1520 );
1521 }
1522
1523 #[test]
test_fetch_invalid_args()1524 fn test_fetch_invalid_args() {
1525 let mut fastboot_impl: FastbootTest = Default::default();
1526 let mut download_buffer = vec![0u8; 2048];
1527 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1528 let mut transport = TestTransport::new();
1529 transport.add_input(b"fetch");
1530 transport.add_input(b"fetch:");
1531 transport.add_input(b"fetch:boot_a");
1532 transport.add_input(b"fetch:boot_a:200");
1533 transport.add_input(b"fetch:boot_a::400");
1534 transport.add_input(b"fetch:boot_a::");
1535 transport.add_input(b"fetch:boot_a:xxx:400");
1536 transport.add_input(b"fetch:boot_a:200:xxx");
1537 let mut fetch_cb =
1538 |_: &str, _: u64, _: u64, _: UploadBuilder, _: &mut FastbootUtils| Ok(());
1539 fastboot_impl.fetch_cb = Some(&mut fetch_cb);
1540 let _ = fastboot.run(&mut transport, &mut fastboot_impl);
1541 assert!(transport.out_queue.iter().all(|v| v.starts_with(b"FAIL")));
1542 }
1543
1544 #[test]
test_fastboot_tcp()1545 fn test_fastboot_tcp() {
1546 let mut fastboot_impl: FastbootTest = Default::default();
1547 let mut download_buffer = vec![0u8; 1024];
1548 let download_content: Vec<u8> =
1549 (0..download_buffer.len()).into_iter().map(|v| v as u8).collect();
1550 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1551 let mut tcp_stream: TestTcpStream = Default::default();
1552 tcp_stream.add_input(TCP_HANDSHAKE_MESSAGE);
1553 // Add two commands and verify both are executed.
1554 tcp_stream.add_length_prefixed_input(b"getvar:max-download-size");
1555 tcp_stream.add_length_prefixed_input(
1556 format!("download:{:#x}", download_content.len()).as_bytes(),
1557 );
1558 tcp_stream.add_length_prefixed_input(&download_content[..]);
1559 let _ = fastboot.run_tcp_session(&mut tcp_stream, &mut fastboot_impl);
1560 let expected: &[&[u8]] = &[
1561 b"FB01",
1562 b"\x00\x00\x00\x00\x00\x00\x00\x09OKAY0x400",
1563 b"\x00\x00\x00\x00\x00\x00\x00\x09DATA0x400",
1564 b"\x00\x00\x00\x00\x00\x00\x00\x04OKAY",
1565 ];
1566 assert_eq!(tcp_stream.out_queue, VecDeque::from(expected.concat()));
1567 assert_eq!(download_buffer, download_content);
1568 }
1569
1570 #[test]
test_fastboot_tcp_invalid_handshake()1571 fn test_fastboot_tcp_invalid_handshake() {
1572 let mut fastboot_impl: FastbootTest = Default::default();
1573 let mut download_buffer = vec![0u8; 1024];
1574 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1575 let mut tcp_stream: TestTcpStream = Default::default();
1576 tcp_stream.add_input(b"ABCD");
1577 assert_eq!(
1578 fastboot.run_tcp_session(&mut tcp_stream, &mut fastboot_impl).unwrap_err(),
1579 TransportError::InvalidHanshake
1580 );
1581 }
1582
1583 #[test]
test_fastboot_tcp_packet_size_exceeds_maximum()1584 fn test_fastboot_tcp_packet_size_exceeds_maximum() {
1585 let mut fastboot_impl: FastbootTest = Default::default();
1586 let mut download_buffer = vec![0u8; 1024];
1587 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
1588 let mut tcp_stream: TestTcpStream = Default::default();
1589 tcp_stream.add_input(TCP_HANDSHAKE_MESSAGE);
1590 tcp_stream.add_input(&(MAX_COMMAND_SIZE + 1).to_be_bytes());
1591 assert_eq!(
1592 fastboot.run_tcp_session(&mut tcp_stream, &mut fastboot_impl).unwrap_err(),
1593 TransportError::PacketSizeExceedMaximum
1594 );
1595 }
1596 }
1597