1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *  * Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in
12  *    the documentation and/or other materials provided with the
13  *    distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include "fastboot_driver.h"
30 
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <algorithm>
37 #include <chrono>
38 #include <fstream>
39 #include <memory>
40 #include <regex>
41 #include <vector>
42 
43 #include <android-base/file.h>
44 #include <android-base/mapped_file.h>
45 #include <android-base/stringprintf.h>
46 #include <android-base/strings.h>
47 #include <android-base/unique_fd.h>
48 
49 #include "constants.h"
50 #include "transport.h"
51 
52 using android::base::StringPrintf;
53 
54 namespace fastboot {
55 
56 /*************************** PUBLIC *******************************/
FastBootDriver(Transport * transport,DriverCallbacks driver_callbacks,bool no_checks)57 FastBootDriver::FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks,
58                                bool no_checks)
59     : transport_(transport),
60       prolog_(std::move(driver_callbacks.prolog)),
61       epilog_(std::move(driver_callbacks.epilog)),
62       info_(std::move(driver_callbacks.info)),
63       disable_checks_(no_checks) {}
64 
~FastBootDriver()65 FastBootDriver::~FastBootDriver() {
66 }
67 
Boot(std::string * response,std::vector<std::string> * info)68 RetCode FastBootDriver::Boot(std::string* response, std::vector<std::string>* info) {
69     return RawCommand(FB_CMD_BOOT, "Booting", response, info);
70 }
71 
Continue(std::string * response,std::vector<std::string> * info)72 RetCode FastBootDriver::Continue(std::string* response, std::vector<std::string>* info) {
73     return RawCommand(FB_CMD_CONTINUE, "Resuming boot", response, info);
74 }
75 
CreatePartition(const std::string & partition,const std::string & size)76 RetCode FastBootDriver::CreatePartition(const std::string& partition, const std::string& size) {
77     return RawCommand(FB_CMD_CREATE_PARTITION ":" + partition + ":" + size,
78                       "Creating '" + partition + "'");
79 }
80 
DeletePartition(const std::string & partition)81 RetCode FastBootDriver::DeletePartition(const std::string& partition) {
82     return RawCommand(FB_CMD_DELETE_PARTITION ":" + partition, "Deleting '" + partition + "'");
83 }
84 
Erase(const std::string & partition,std::string * response,std::vector<std::string> * info)85 RetCode FastBootDriver::Erase(const std::string& partition, std::string* response,
86                               std::vector<std::string>* info) {
87     return RawCommand(FB_CMD_ERASE ":" + partition, "Erasing '" + partition + "'", response, info);
88 }
89 
Flash(const std::string & partition,std::string * response,std::vector<std::string> * info)90 RetCode FastBootDriver::Flash(const std::string& partition, std::string* response,
91                               std::vector<std::string>* info) {
92     return RawCommand(FB_CMD_FLASH ":" + partition, "Writing '" + partition + "'", response, info);
93 }
94 
GetVar(const std::string & key,std::string * val,std::vector<std::string> * info)95 RetCode FastBootDriver::GetVar(const std::string& key, std::string* val,
96                                std::vector<std::string>* info) {
97     return RawCommand(FB_CMD_GETVAR ":" + key, val, info);
98 }
99 
GetVarAll(std::vector<std::string> * response)100 RetCode FastBootDriver::GetVarAll(std::vector<std::string>* response) {
101     std::string tmp;
102     return GetVar("all", &tmp, response);
103 }
104 
Reboot(std::string * response,std::vector<std::string> * info)105 RetCode FastBootDriver::Reboot(std::string* response, std::vector<std::string>* info) {
106     return RawCommand(FB_CMD_REBOOT, "Rebooting", response, info);
107 }
108 
RebootTo(std::string target,std::string * response,std::vector<std::string> * info)109 RetCode FastBootDriver::RebootTo(std::string target, std::string* response,
110                                  std::vector<std::string>* info) {
111     return RawCommand("reboot-" + target, "Rebooting into " + target, response, info);
112 }
113 
ResizePartition(const std::string & partition,const std::string & size)114 RetCode FastBootDriver::ResizePartition(const std::string& partition, const std::string& size) {
115     return RawCommand(FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size,
116                       "Resizing '" + partition + "'");
117 }
118 
SetActive(const std::string & slot,std::string * response,std::vector<std::string> * info)119 RetCode FastBootDriver::SetActive(const std::string& slot, std::string* response,
120                                   std::vector<std::string>* info) {
121     return RawCommand(FB_CMD_SET_ACTIVE ":" + slot, "Setting current slot to '" + slot + "'",
122                       response, info);
123 }
124 
SnapshotUpdateCommand(const std::string & command,std::string * response,std::vector<std::string> * info)125 RetCode FastBootDriver::SnapshotUpdateCommand(const std::string& command, std::string* response,
126                                               std::vector<std::string>* info) {
127     prolog_(StringPrintf("Snapshot %s", command.c_str()));
128     std::string raw = FB_CMD_SNAPSHOT_UPDATE ":" + command;
129     auto result = RawCommand(raw, response, info);
130     epilog_(result);
131     return result;
132 }
133 
FlashPartition(const std::string & partition,const std::vector<char> & data)134 RetCode FastBootDriver::FlashPartition(const std::string& partition,
135                                        const std::vector<char>& data) {
136     RetCode ret;
137     if ((ret = Download(partition, data))) {
138         return ret;
139     }
140     return Flash(partition);
141 }
142 
FlashPartition(const std::string & partition,int fd,uint32_t size)143 RetCode FastBootDriver::FlashPartition(const std::string& partition, int fd, uint32_t size) {
144     RetCode ret;
145     if ((ret = Download(partition, fd, size))) {
146         return ret;
147     }
148     return Flash(partition);
149 }
150 
FlashPartition(const std::string & partition,sparse_file * s,uint32_t size,size_t current,size_t total)151 RetCode FastBootDriver::FlashPartition(const std::string& partition, sparse_file* s, uint32_t size,
152                                        size_t current, size_t total) {
153     RetCode ret;
154     if ((ret = Download(partition, s, size, current, total, false))) {
155         return ret;
156     }
157     return Flash(partition);
158 }
159 
Partitions(std::vector<std::tuple<std::string,uint64_t>> * partitions)160 RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint64_t>>* partitions) {
161     std::vector<std::string> all;
162     RetCode ret;
163     if ((ret = GetVarAll(&all))) {
164         return ret;
165     }
166 
167     std::regex reg("partition-size[[:s:]]*:[[:s:]]*([[:w:]]+)[[:s:]]*:[[:s:]]*0x([[:xdigit:]]+)");
168     std::smatch sm;
169 
170     for (auto& s : all) {
171         if (std::regex_match(s, sm, reg)) {
172             std::string m1(sm[1]);
173             std::string m2(sm[2]);
174             uint64_t tmp = strtoll(m2.c_str(), 0, 16);
175             partitions->push_back(std::make_tuple(m1, tmp));
176         }
177     }
178     return SUCCESS;
179 }
180 
Download(const std::string & name,int fd,size_t size,std::string * response,std::vector<std::string> * info)181 RetCode FastBootDriver::Download(const std::string& name, int fd, size_t size,
182                                  std::string* response, std::vector<std::string>* info) {
183     prolog_(StringPrintf("Sending '%s' (%zu KB)", name.c_str(), size / 1024));
184     auto result = Download(fd, size, response, info);
185     epilog_(result);
186     return result;
187 }
188 
Download(int fd,size_t size,std::string * response,std::vector<std::string> * info)189 RetCode FastBootDriver::Download(int fd, size_t size, std::string* response,
190                                  std::vector<std::string>* info) {
191     RetCode ret;
192 
193     if ((size <= 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
194         error_ = "File is too large to download";
195         return BAD_ARG;
196     }
197 
198     uint32_t u32size = static_cast<uint32_t>(size);
199     if ((ret = DownloadCommand(u32size, response, info))) {
200         return ret;
201     }
202 
203     // Write the buffer
204     if ((ret = SendBuffer(fd, size))) {
205         return ret;
206     }
207 
208     // Wait for response
209     return HandleResponse(response, info);
210 }
211 
Download(const std::string & name,const std::vector<char> & buf,std::string * response,std::vector<std::string> * info)212 RetCode FastBootDriver::Download(const std::string& name, const std::vector<char>& buf,
213                                  std::string* response, std::vector<std::string>* info) {
214     prolog_(StringPrintf("Sending '%s' (%zu KB)", name.c_str(), buf.size() / 1024));
215     auto result = Download(buf, response, info);
216     epilog_(result);
217     return result;
218 }
219 
Download(const std::vector<char> & buf,std::string * response,std::vector<std::string> * info)220 RetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,
221                                  std::vector<std::string>* info) {
222     RetCode ret;
223     error_ = "";
224     if ((buf.size() == 0 || buf.size() > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
225         error_ = "Buffer is too large or 0 bytes";
226         return BAD_ARG;
227     }
228 
229     if ((ret = DownloadCommand(buf.size(), response, info))) {
230         return ret;
231     }
232 
233     // Write the buffer
234     if ((ret = SendBuffer(buf))) {
235         return ret;
236     }
237 
238     // Wait for response
239     return HandleResponse(response, info);
240 }
241 
Download(const std::string & partition,struct sparse_file * s,uint32_t size,size_t current,size_t total,bool use_crc,std::string * response,std::vector<std::string> * info)242 RetCode FastBootDriver::Download(const std::string& partition, struct sparse_file* s, uint32_t size,
243                                  size_t current, size_t total, bool use_crc, std::string* response,
244                                  std::vector<std::string>* info) {
245     prolog_(StringPrintf("Sending sparse '%s' %zu/%zu (%u KB)", partition.c_str(), current, total,
246                          size / 1024));
247     auto result = Download(s, use_crc, response, info);
248     epilog_(result);
249     return result;
250 }
251 
Download(sparse_file * s,bool use_crc,std::string * response,std::vector<std::string> * info)252 RetCode FastBootDriver::Download(sparse_file* s, bool use_crc, std::string* response,
253                                  std::vector<std::string>* info) {
254     error_ = "";
255     int64_t size = sparse_file_len(s, true, use_crc);
256     if (size <= 0 || size > MAX_DOWNLOAD_SIZE) {
257         error_ = "Sparse file is too large or invalid";
258         return BAD_ARG;
259     }
260 
261     RetCode ret;
262     uint32_t u32size = static_cast<uint32_t>(size);
263     if ((ret = DownloadCommand(u32size, response, info))) {
264         return ret;
265     }
266 
267     struct SparseCBPrivate {
268         FastBootDriver* self;
269         std::vector<char> tpbuf;
270     } cb_priv;
271     cb_priv.self = this;
272 
273     auto cb = [](void* priv, const void* buf, size_t len) -> int {
274         SparseCBPrivate* data = static_cast<SparseCBPrivate*>(priv);
275         const char* cbuf = static_cast<const char*>(buf);
276         return data->self->SparseWriteCallback(data->tpbuf, cbuf, len);
277     };
278 
279     if (sparse_file_callback(s, true, use_crc, cb, &cb_priv) < 0) {
280         error_ = "Error reading sparse file";
281         return IO_ERROR;
282     }
283 
284     // Now flush
285     if (cb_priv.tpbuf.size() && (ret = SendBuffer(cb_priv.tpbuf))) {
286         return ret;
287     }
288 
289     return HandleResponse(response, info);
290 }
291 
Upload(const std::string & outfile,std::string * response,std::vector<std::string> * info)292 RetCode FastBootDriver::Upload(const std::string& outfile, std::string* response,
293                                std::vector<std::string>* info) {
294     prolog_("Uploading '" + outfile + "'");
295     auto result = UploadInner(outfile, response, info);
296     epilog_(result);
297     return result;
298 }
299 
UploadInner(const std::string & outfile,std::string * response,std::vector<std::string> * info)300 RetCode FastBootDriver::UploadInner(const std::string& outfile, std::string* response,
301                                     std::vector<std::string>* info) {
302     RetCode ret;
303     int dsize = 0;
304     if ((ret = RawCommand(FB_CMD_UPLOAD, response, info, &dsize))) {
305         error_ = "Upload request failed: " + error_;
306         return ret;
307     }
308 
309     if (!dsize) {
310         error_ = "Upload request failed, device reports 0 bytes available";
311         return BAD_DEV_RESP;
312     }
313 
314     std::vector<char> data;
315     data.resize(dsize);
316 
317     if ((ret = ReadBuffer(data))) {
318         return ret;
319     }
320 
321     std::ofstream ofs;
322     ofs.open(outfile, std::ofstream::out | std::ofstream::binary);
323     if (ofs.fail()) {
324         error_ = android::base::StringPrintf("Failed to open '%s'", outfile.c_str());
325         return IO_ERROR;
326     }
327     ofs.write(data.data(), data.size());
328     if (ofs.fail() || ofs.bad()) {
329         error_ = android::base::StringPrintf("Writing to '%s' failed", outfile.c_str());
330         return IO_ERROR;
331     }
332     ofs.close();
333 
334     return HandleResponse(response, info);
335 }
336 
337 // Helpers
SetInfoCallback(std::function<void (const std::string &)> info)338 void FastBootDriver::SetInfoCallback(std::function<void(const std::string&)> info) {
339     info_ = info;
340 }
341 
RCString(RetCode rc)342 const std::string FastBootDriver::RCString(RetCode rc) {
343     switch (rc) {
344         case SUCCESS:
345             return std::string("Success");
346 
347         case BAD_ARG:
348             return std::string("Invalid Argument");
349 
350         case IO_ERROR:
351             return std::string("I/O Error");
352 
353         case BAD_DEV_RESP:
354             return std::string("Invalid Device Response");
355 
356         case DEVICE_FAIL:
357             return std::string("Device Error");
358 
359         case TIMEOUT:
360             return std::string("Timeout");
361 
362         default:
363             return std::string("Unknown Error");
364     }
365 }
366 
Error()367 std::string FastBootDriver::Error() {
368     return error_;
369 }
370 
WaitForDisconnect()371 RetCode FastBootDriver::WaitForDisconnect() {
372     return transport_->WaitForDisconnect() ? IO_ERROR : SUCCESS;
373 }
374 
375 /****************************** PROTECTED *************************************/
RawCommand(const std::string & cmd,const std::string & message,std::string * response,std::vector<std::string> * info,int * dsize)376 RetCode FastBootDriver::RawCommand(const std::string& cmd, const std::string& message,
377                                    std::string* response, std::vector<std::string>* info,
378                                    int* dsize) {
379     prolog_(message);
380     auto result = RawCommand(cmd, response, info, dsize);
381     epilog_(result);
382     return result;
383 }
384 
RawCommand(const std::string & cmd,std::string * response,std::vector<std::string> * info,int * dsize)385 RetCode FastBootDriver::RawCommand(const std::string& cmd, std::string* response,
386                                    std::vector<std::string>* info, int* dsize) {
387     error_ = "";  // Clear any pending error
388     if (cmd.size() > FB_COMMAND_SZ && !disable_checks_) {
389         error_ = "Command length to RawCommand() is too long";
390         return BAD_ARG;
391     }
392 
393     if (transport_->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
394         error_ = ErrnoStr("Write to device failed");
395         return IO_ERROR;
396     }
397 
398     // Read the response
399     return HandleResponse(response, info, dsize);
400 }
401 
DownloadCommand(uint32_t size,std::string * response,std::vector<std::string> * info)402 RetCode FastBootDriver::DownloadCommand(uint32_t size, std::string* response,
403                                         std::vector<std::string>* info) {
404     std::string cmd(android::base::StringPrintf("%s:%08" PRIx32, FB_CMD_DOWNLOAD, size));
405     RetCode ret;
406     if ((ret = RawCommand(cmd, response, info))) {
407         return ret;
408     }
409     return SUCCESS;
410 }
411 
HandleResponse(std::string * response,std::vector<std::string> * info,int * dsize)412 RetCode FastBootDriver::HandleResponse(std::string* response, std::vector<std::string>* info,
413                                        int* dsize) {
414     char status[FB_RESPONSE_SZ + 1];
415     auto start = std::chrono::steady_clock::now();
416 
417     auto set_response = [response](std::string s) {
418         if (response) *response = std::move(s);
419     };
420     auto add_info = [info](std::string s) {
421         if (info) info->push_back(std::move(s));
422     };
423 
424     // erase response
425     set_response("");
426     while ((std::chrono::steady_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
427         int r = transport_->Read(status, FB_RESPONSE_SZ);
428         if (r < 0) {
429             error_ = ErrnoStr("Status read failed");
430             return IO_ERROR;
431         }
432 
433         status[r] = '\0';  // Need the null terminator
434         std::string input(status);
435         if (android::base::StartsWith(input, "INFO")) {
436             std::string tmp = input.substr(strlen("INFO"));
437             info_(tmp);
438             add_info(std::move(tmp));
439             // We may receive one or more INFO packets during long operations,
440             // e.g. flash/erase if they are back by slow media like NAND/NOR
441             // flash. In that case, reset the timer since it's not a real
442             // timeout.
443             start = std::chrono::steady_clock::now();
444         } else if (android::base::StartsWith(input, "OKAY")) {
445             set_response(input.substr(strlen("OKAY")));
446             return SUCCESS;
447         } else if (android::base::StartsWith(input, "FAIL")) {
448             error_ = android::base::StringPrintf("remote: '%s'", status + strlen("FAIL"));
449             set_response(input.substr(strlen("FAIL")));
450             return DEVICE_FAIL;
451         } else if (android::base::StartsWith(input, "DATA")) {
452             std::string tmp = input.substr(strlen("DATA"));
453             uint32_t num = strtol(tmp.c_str(), 0, 16);
454             if (num > MAX_DOWNLOAD_SIZE) {
455                 error_ = android::base::StringPrintf("Data size too large (%d)", num);
456                 return BAD_DEV_RESP;
457             }
458             if (dsize) *dsize = num;
459             set_response(std::move(tmp));
460             return SUCCESS;
461         } else {
462             error_ = android::base::StringPrintf("Device sent unknown status code: %s", status);
463             return BAD_DEV_RESP;
464         }
465 
466     }  // End of while loop
467 
468     return TIMEOUT;
469 }
470 
ErrnoStr(const std::string & msg)471 std::string FastBootDriver::ErrnoStr(const std::string& msg) {
472     return android::base::StringPrintf("%s (%s)", msg.c_str(), strerror(errno));
473 }
474 
475 /******************************* PRIVATE **************************************/
SendBuffer(int fd,size_t size)476 RetCode FastBootDriver::SendBuffer(int fd, size_t size) {
477     static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
478     off64_t offset = 0;
479     uint32_t remaining = size;
480     RetCode ret;
481 
482     while (remaining) {
483         // Memory map the file
484         size_t len = std::min(remaining, MAX_MAP_SIZE);
485         auto mapping{android::base::MappedFile::FromFd(fd, offset, len, PROT_READ)};
486         if (!mapping) {
487             error_ = "Creating filemap failed";
488             return IO_ERROR;
489         }
490 
491         if ((ret = SendBuffer(mapping->data(), mapping->size()))) {
492             return ret;
493         }
494 
495         remaining -= len;
496         offset += len;
497     }
498 
499     return SUCCESS;
500 }
501 
SendBuffer(const std::vector<char> & buf)502 RetCode FastBootDriver::SendBuffer(const std::vector<char>& buf) {
503     // Write the buffer
504     return SendBuffer(buf.data(), buf.size());
505 }
506 
SendBuffer(const void * buf,size_t size)507 RetCode FastBootDriver::SendBuffer(const void* buf, size_t size) {
508     // ioctl on 0-length buffer causes freezing
509     if (!size) {
510         return BAD_ARG;
511     }
512     // Write the buffer
513     ssize_t tmp = transport_->Write(buf, size);
514 
515     if (tmp < 0) {
516         error_ = ErrnoStr("Write to device failed in SendBuffer()");
517         return IO_ERROR;
518     } else if (static_cast<size_t>(tmp) != size) {
519         error_ = android::base::StringPrintf("Failed to write all %zu bytes", size);
520 
521         return IO_ERROR;
522     }
523 
524     return SUCCESS;
525 }
526 
ReadBuffer(std::vector<char> & buf)527 RetCode FastBootDriver::ReadBuffer(std::vector<char>& buf) {
528     // Read the buffer
529     return ReadBuffer(buf.data(), buf.size());
530 }
531 
ReadBuffer(void * buf,size_t size)532 RetCode FastBootDriver::ReadBuffer(void* buf, size_t size) {
533     // Read the buffer
534     ssize_t tmp = transport_->Read(buf, size);
535 
536     if (tmp < 0) {
537         error_ = ErrnoStr("Read from device failed in ReadBuffer()");
538         return IO_ERROR;
539     } else if (static_cast<size_t>(tmp) != size) {
540         error_ = android::base::StringPrintf("Failed to read all %zu bytes", size);
541         return IO_ERROR;
542     }
543 
544     return SUCCESS;
545 }
546 
SparseWriteCallback(std::vector<char> & tpbuf,const char * data,size_t len)547 int FastBootDriver::SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len) {
548     size_t total = 0;
549     size_t to_write = std::min(TRANSPORT_CHUNK_SIZE - tpbuf.size(), len);
550 
551     // Handle the residual
552     tpbuf.insert(tpbuf.end(), data, data + to_write);
553     if (tpbuf.size() < TRANSPORT_CHUNK_SIZE) {  // Nothing enough to send rn
554         return 0;
555     }
556 
557     if (SendBuffer(tpbuf)) {
558         error_ = ErrnoStr("Send failed in SparseWriteCallback()");
559         return -1;
560     }
561     tpbuf.clear();
562     total += to_write;
563 
564     // Now we need to send a multiple of chunk size
565     size_t nchunks = (len - total) / TRANSPORT_CHUNK_SIZE;
566     size_t nbytes = TRANSPORT_CHUNK_SIZE * nchunks;
567     if (nbytes && SendBuffer(data + total, nbytes)) {  // Don't send a ZLP
568         error_ = ErrnoStr("Send failed in SparseWriteCallback()");
569         return -1;
570     }
571     total += nbytes;
572 
573     if (len - total > 0) {  // We have residual data to save for next time
574         tpbuf.assign(data + total, data + len);
575     }
576 
577     return 0;
578 }
579 
set_transport(Transport * transport)580 Transport* FastBootDriver::set_transport(Transport* transport) {
581     std::swap(transport_, transport);
582     return transport;
583 }
584 
585 }  // End namespace fastboot
586