Loading fastboot/Android.mk +3 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ LOCAL_SRC_FILES := \ fs.cpp\ protocol.cpp \ socket.cpp \ tcp.cpp \ util.cpp \ LOCAL_MODULE := fastboot Loading Loading @@ -111,6 +112,8 @@ LOCAL_SRC_FILES := \ socket.cpp \ socket_mock.cpp \ socket_test.cpp \ tcp.cpp \ tcp_test.cpp \ LOCAL_STATIC_LIBRARIES := libbase libcutils Loading fastboot/fastboot.cpp +54 −14 Original line number Diff line number Diff line Loading @@ -42,22 +42,22 @@ #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <functional> #include <utility> #include <vector> #include <android-base/parseint.h> #include <android-base/parsenetaddress.h> #include <android-base/strings.h> #include <sparse/sparse.h> #include <ziparchive/zip_archive.h> #include <android-base/strings.h> #include <android-base/parseint.h> #include "bootimg_utils.h" #include "diagnose_usb.h" #include "fastboot.h" #include "fs.h" #include "tcp.h" #include "transport.h" #include "usb.h" Loading @@ -69,9 +69,9 @@ char cur_product[FB_RESPONSE_SZ + 1]; static const char *serial = 0; static const char *product = 0; static const char *cmdline = 0; static const char* serial = nullptr; static const char* product = nullptr; static const char* cmdline = nullptr; static unsigned short vendor_id = 0; static int long_listing = 0; static int64_t sparse_limit = -1; Loading Loading @@ -227,17 +227,51 @@ static int list_devices_callback(usb_ifc_info* info) { return -1; } // Opens a new Transport connected to a device. If |serial| is non-null it will be used to identify // a specific device, otherwise the first USB device found will be used. // // If |serial| is non-null but invalid, this prints an error message to stderr and returns nullptr. // Otherwise it blocks until the target is available. // // The returned Transport is a singleton, so multiple calls to this function will return the same // object, and the caller should not attempt to delete the returned Transport. static Transport* open_device() { static Transport* transport = nullptr; int announce = 1; bool announce = true; if (transport != nullptr) { return transport; } std::string host; int port = tcp::kDefaultPort; if (serial != nullptr && android::base::StartsWith(serial, "tcp:")) { std::string error; const char* address = serial + strlen("tcp:"); if (transport) return transport; if (!android::base::ParseNetAddress(address, &host, &port, nullptr, &error)) { fprintf(stderr, "error: Invalid network address '%s': %s\n", address, error.c_str()); return nullptr; } } for (;;) { while (true) { if (!host.empty()) { std::string error; transport = tcp::Connect(host, port, &error).release(); if (transport == nullptr && announce) { fprintf(stderr, "error: %s\n", error.c_str()); } } else { transport = usb_open(match_fastboot); if (transport) return transport; } if (transport != nullptr) { return transport; } if (announce) { announce = 0; announce = false; fprintf(stderr, "< waiting for %s >\n", serial ? serial : "any device"); } usleep(1000); Loading Loading @@ -299,8 +333,10 @@ static void usage() { " if supported by partition type).\n" " -u Do not erase partition before\n" " formatting.\n" " -s <specific device> Specify device serial number\n" " or path to device port.\n" " -s <specific device> Specify a device. For USB, provide either\n" " a serial number or path to device port.\n" " For TCP, provide an address in the form\n" " tcp:<hostname>[:port].\n" " -p <product> Specify product name.\n" " -c <cmdline> Override kernel commandline.\n" " -i <vendor id> Specify a custom USB vendor id.\n" Loading Loading @@ -1263,6 +1299,10 @@ int main(int argc, char **argv) } Transport* transport = open_device(); if (transport == nullptr) { return 1; } if (slot_override != "") slot_override = verify_slot(transport, slot_override.c_str()); if (next_active != "") Loading fastboot/fastboot_protocol.txt +54 −7 Original line number Diff line number Diff line Loading @@ -3,20 +3,26 @@ FastBoot Version 0.4 ---------------------- The fastboot protocol is a mechanism for communicating with bootloaders over USB. It is designed to be very straightforward to implement, to allow it to be used across a wide range of devices and from hosts running over USB or ethernet. It is designed to be very straightforward to implement, to allow it to be used across a wide range of devices and from hosts running Linux, Windows, or OSX. Basic Requirements ------------------ * USB * Two bulk endpoints (in, out) are required * Max packet size must be 64 bytes for full-speed, 512 bytes for high-speed and 1024 bytes for Super Speed USB. * The protocol is entirely host-driven and synchronous (unlike the multi-channel, bi-directional, asynchronous ADB protocol) * TCP * Device must be reachable via IP. * Device will act as the TCP server, fastboot will be the client. * Fastboot data is wrapped in a simple protocol; see below for details. Transport and Framing --------------------- Loading Loading @@ -171,3 +177,44 @@ specification. OEM-specific names should not start with lowercase characters. TCP Protocol v1 --------------- The TCP protocol is designed to be a simple way to use the fastboot protocol over ethernet if USB is not available. The device will open a TCP server on port 5554 and wait for a fastboot client to connect. -- Handshake -- Upon connecting, both sides will send a 4-byte handshake message to ensure they are speaking the same protocol. This consists of the ASCII characters "FB" followed by a 2-digit base-10 ASCII version number. For example, the version 1 handshake message will be [FB01]. If either side detects a malformed handshake, it should disconnect. The protocol version to use must be the minimum of the versions sent by each side; if either side cannot speak this protocol version, it should disconnect. -- Fastboot Data -- Once the handshake is complete, fastboot data will be sent as follows: [data_size][data] Where data_size is an unsigned 8-byte big-endian binary value, and data is the fastboot packet. The 8-byte length is intended to provide future-proofing even though currently fastboot packets have a 4-byte maximum length. -- Example -- In this example the fastboot host queries the device for two variables, "version" and "none". Host <connect to the device on port 5555> Host FB01 Device FB01 Host [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0E]getvar:version Device [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x07]OKAY0.4 Host [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0B]getvar:none Device [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x04]OKAY Host <disconnect> fastboot/tcp.cpp 0 → 100644 +196 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "tcp.h" #include <android-base/stringprintf.h> namespace tcp { static constexpr int kProtocolVersion = 1; static constexpr size_t kHandshakeLength = 4; static constexpr int kHandshakeTimeoutMs = 2000; // Extract the big-endian 8-byte message length into a 64-bit number. static uint64_t ExtractMessageLength(const void* buffer) { uint64_t ret = 0; for (int i = 0; i < 8; ++i) { ret |= uint64_t{reinterpret_cast<const uint8_t*>(buffer)[i]} << (56 - i * 8); } return ret; } // Encode the 64-bit number into a big-endian 8-byte message length. static void EncodeMessageLength(uint64_t length, void* buffer) { for (int i = 0; i < 8; ++i) { reinterpret_cast<uint8_t*>(buffer)[i] = length >> (56 - i * 8); } } class TcpTransport : public Transport { public: // Factory function so we can return nullptr if initialization fails. static std::unique_ptr<TcpTransport> NewTransport(std::unique_ptr<Socket> socket, std::string* error); ~TcpTransport() override = default; ssize_t Read(void* data, size_t length) override; ssize_t Write(const void* data, size_t length) override; int Close() override; private: TcpTransport(std::unique_ptr<Socket> sock) : socket_(std::move(sock)) {} // Connects to the device and performs the initial handshake. Returns false and fills |error| // on failure. bool InitializeProtocol(std::string* error); std::unique_ptr<Socket> socket_; uint64_t message_bytes_left_ = 0; DISALLOW_COPY_AND_ASSIGN(TcpTransport); }; std::unique_ptr<TcpTransport> TcpTransport::NewTransport(std::unique_ptr<Socket> socket, std::string* error) { std::unique_ptr<TcpTransport> transport(new TcpTransport(std::move(socket))); if (!transport->InitializeProtocol(error)) { return nullptr; } return transport; } // These error strings are checked in tcp_test.cpp and should be kept in sync. bool TcpTransport::InitializeProtocol(std::string* error) { std::string handshake_message(android::base::StringPrintf("FB%02d", kProtocolVersion)); if (!socket_->Send(handshake_message.c_str(), kHandshakeLength)) { *error = android::base::StringPrintf("Failed to send initialization message (%s)", Socket::GetErrorMessage().c_str()); return false; } char buffer[kHandshakeLength]; if (socket_->ReceiveAll(buffer, kHandshakeLength, kHandshakeTimeoutMs) != kHandshakeLength) { *error = android::base::StringPrintf( "No initialization message received (%s). Target may not support TCP fastboot", Socket::GetErrorMessage().c_str()); return false; } if (memcmp(buffer, "FB", 2) != 0) { *error = "Unrecognized initialization message. Target may not support TCP fastboot"; return false; } if (memcmp(buffer + 2, "01", 2) != 0) { *error = android::base::StringPrintf("Unknown TCP protocol version %s (host version %02d)", std::string(buffer + 2, 2).c_str(), kProtocolVersion); return false; } error->clear(); return true; } ssize_t TcpTransport::Read(void* data, size_t length) { if (socket_ == nullptr) { return -1; } // Unless we're mid-message, read the next 8-byte message length. if (message_bytes_left_ == 0) { char buffer[8]; if (socket_->ReceiveAll(buffer, 8, 0) != 8) { Close(); return -1; } message_bytes_left_ = ExtractMessageLength(buffer); } // Now read the message (up to |length| bytes). if (length > message_bytes_left_) { length = message_bytes_left_; } ssize_t bytes_read = socket_->ReceiveAll(data, length, 0); if (bytes_read == -1) { Close(); } else { message_bytes_left_ -= bytes_read; } return bytes_read; } ssize_t TcpTransport::Write(const void* data, size_t length) { if (socket_ == nullptr) { return -1; } // Use multi-buffer writes for better performance. char header[8]; EncodeMessageLength(length, header); if (!socket_->Send(std::vector<cutils_socket_buffer_t>{{header, 8}, {data, length}})) { Close(); return -1; } return length; } int TcpTransport::Close() { if (socket_ == nullptr) { return 0; } int result = socket_->Close(); socket_.reset(); return result; } std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) { return internal::Connect(Socket::NewClient(Socket::Protocol::kTcp, hostname, port, error), error); } namespace internal { std::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error) { if (sock == nullptr) { // If Socket creation failed |error| is already set. return nullptr; } return TcpTransport::NewTransport(std::move(sock), error); } } // namespace internal } // namespace tcp fastboot/tcp.h 0 → 100644 +59 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef TCP_H_ #define TCP_H_ #include <memory> #include <string> #include <android-base/macros.h> #include "socket.h" #include "transport.h" namespace tcp { constexpr int kDefaultPort = 5554; // Returns a newly allocated Transport object connected to |hostname|:|port|. On failure, |error| is // filled and nullptr is returned. std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error); // Internal namespace for test use only. namespace internal { // Creates a TCP Transport object but using a given Socket instead of connecting to a hostname. // Used for unit tests to create a Transport object that uses a SocketMock. std::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error); } // namespace internal } // namespace tcp #endif // TCP_H_ Loading
fastboot/Android.mk +3 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ LOCAL_SRC_FILES := \ fs.cpp\ protocol.cpp \ socket.cpp \ tcp.cpp \ util.cpp \ LOCAL_MODULE := fastboot Loading Loading @@ -111,6 +112,8 @@ LOCAL_SRC_FILES := \ socket.cpp \ socket_mock.cpp \ socket_test.cpp \ tcp.cpp \ tcp_test.cpp \ LOCAL_STATIC_LIBRARIES := libbase libcutils Loading
fastboot/fastboot.cpp +54 −14 Original line number Diff line number Diff line Loading @@ -42,22 +42,22 @@ #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <functional> #include <utility> #include <vector> #include <android-base/parseint.h> #include <android-base/parsenetaddress.h> #include <android-base/strings.h> #include <sparse/sparse.h> #include <ziparchive/zip_archive.h> #include <android-base/strings.h> #include <android-base/parseint.h> #include "bootimg_utils.h" #include "diagnose_usb.h" #include "fastboot.h" #include "fs.h" #include "tcp.h" #include "transport.h" #include "usb.h" Loading @@ -69,9 +69,9 @@ char cur_product[FB_RESPONSE_SZ + 1]; static const char *serial = 0; static const char *product = 0; static const char *cmdline = 0; static const char* serial = nullptr; static const char* product = nullptr; static const char* cmdline = nullptr; static unsigned short vendor_id = 0; static int long_listing = 0; static int64_t sparse_limit = -1; Loading Loading @@ -227,17 +227,51 @@ static int list_devices_callback(usb_ifc_info* info) { return -1; } // Opens a new Transport connected to a device. If |serial| is non-null it will be used to identify // a specific device, otherwise the first USB device found will be used. // // If |serial| is non-null but invalid, this prints an error message to stderr and returns nullptr. // Otherwise it blocks until the target is available. // // The returned Transport is a singleton, so multiple calls to this function will return the same // object, and the caller should not attempt to delete the returned Transport. static Transport* open_device() { static Transport* transport = nullptr; int announce = 1; bool announce = true; if (transport != nullptr) { return transport; } std::string host; int port = tcp::kDefaultPort; if (serial != nullptr && android::base::StartsWith(serial, "tcp:")) { std::string error; const char* address = serial + strlen("tcp:"); if (transport) return transport; if (!android::base::ParseNetAddress(address, &host, &port, nullptr, &error)) { fprintf(stderr, "error: Invalid network address '%s': %s\n", address, error.c_str()); return nullptr; } } for (;;) { while (true) { if (!host.empty()) { std::string error; transport = tcp::Connect(host, port, &error).release(); if (transport == nullptr && announce) { fprintf(stderr, "error: %s\n", error.c_str()); } } else { transport = usb_open(match_fastboot); if (transport) return transport; } if (transport != nullptr) { return transport; } if (announce) { announce = 0; announce = false; fprintf(stderr, "< waiting for %s >\n", serial ? serial : "any device"); } usleep(1000); Loading Loading @@ -299,8 +333,10 @@ static void usage() { " if supported by partition type).\n" " -u Do not erase partition before\n" " formatting.\n" " -s <specific device> Specify device serial number\n" " or path to device port.\n" " -s <specific device> Specify a device. For USB, provide either\n" " a serial number or path to device port.\n" " For TCP, provide an address in the form\n" " tcp:<hostname>[:port].\n" " -p <product> Specify product name.\n" " -c <cmdline> Override kernel commandline.\n" " -i <vendor id> Specify a custom USB vendor id.\n" Loading Loading @@ -1263,6 +1299,10 @@ int main(int argc, char **argv) } Transport* transport = open_device(); if (transport == nullptr) { return 1; } if (slot_override != "") slot_override = verify_slot(transport, slot_override.c_str()); if (next_active != "") Loading
fastboot/fastboot_protocol.txt +54 −7 Original line number Diff line number Diff line Loading @@ -3,20 +3,26 @@ FastBoot Version 0.4 ---------------------- The fastboot protocol is a mechanism for communicating with bootloaders over USB. It is designed to be very straightforward to implement, to allow it to be used across a wide range of devices and from hosts running over USB or ethernet. It is designed to be very straightforward to implement, to allow it to be used across a wide range of devices and from hosts running Linux, Windows, or OSX. Basic Requirements ------------------ * USB * Two bulk endpoints (in, out) are required * Max packet size must be 64 bytes for full-speed, 512 bytes for high-speed and 1024 bytes for Super Speed USB. * The protocol is entirely host-driven and synchronous (unlike the multi-channel, bi-directional, asynchronous ADB protocol) * TCP * Device must be reachable via IP. * Device will act as the TCP server, fastboot will be the client. * Fastboot data is wrapped in a simple protocol; see below for details. Transport and Framing --------------------- Loading Loading @@ -171,3 +177,44 @@ specification. OEM-specific names should not start with lowercase characters. TCP Protocol v1 --------------- The TCP protocol is designed to be a simple way to use the fastboot protocol over ethernet if USB is not available. The device will open a TCP server on port 5554 and wait for a fastboot client to connect. -- Handshake -- Upon connecting, both sides will send a 4-byte handshake message to ensure they are speaking the same protocol. This consists of the ASCII characters "FB" followed by a 2-digit base-10 ASCII version number. For example, the version 1 handshake message will be [FB01]. If either side detects a malformed handshake, it should disconnect. The protocol version to use must be the minimum of the versions sent by each side; if either side cannot speak this protocol version, it should disconnect. -- Fastboot Data -- Once the handshake is complete, fastboot data will be sent as follows: [data_size][data] Where data_size is an unsigned 8-byte big-endian binary value, and data is the fastboot packet. The 8-byte length is intended to provide future-proofing even though currently fastboot packets have a 4-byte maximum length. -- Example -- In this example the fastboot host queries the device for two variables, "version" and "none". Host <connect to the device on port 5555> Host FB01 Device FB01 Host [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0E]getvar:version Device [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x07]OKAY0.4 Host [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0B]getvar:none Device [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x04]OKAY Host <disconnect>
fastboot/tcp.cpp 0 → 100644 +196 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "tcp.h" #include <android-base/stringprintf.h> namespace tcp { static constexpr int kProtocolVersion = 1; static constexpr size_t kHandshakeLength = 4; static constexpr int kHandshakeTimeoutMs = 2000; // Extract the big-endian 8-byte message length into a 64-bit number. static uint64_t ExtractMessageLength(const void* buffer) { uint64_t ret = 0; for (int i = 0; i < 8; ++i) { ret |= uint64_t{reinterpret_cast<const uint8_t*>(buffer)[i]} << (56 - i * 8); } return ret; } // Encode the 64-bit number into a big-endian 8-byte message length. static void EncodeMessageLength(uint64_t length, void* buffer) { for (int i = 0; i < 8; ++i) { reinterpret_cast<uint8_t*>(buffer)[i] = length >> (56 - i * 8); } } class TcpTransport : public Transport { public: // Factory function so we can return nullptr if initialization fails. static std::unique_ptr<TcpTransport> NewTransport(std::unique_ptr<Socket> socket, std::string* error); ~TcpTransport() override = default; ssize_t Read(void* data, size_t length) override; ssize_t Write(const void* data, size_t length) override; int Close() override; private: TcpTransport(std::unique_ptr<Socket> sock) : socket_(std::move(sock)) {} // Connects to the device and performs the initial handshake. Returns false and fills |error| // on failure. bool InitializeProtocol(std::string* error); std::unique_ptr<Socket> socket_; uint64_t message_bytes_left_ = 0; DISALLOW_COPY_AND_ASSIGN(TcpTransport); }; std::unique_ptr<TcpTransport> TcpTransport::NewTransport(std::unique_ptr<Socket> socket, std::string* error) { std::unique_ptr<TcpTransport> transport(new TcpTransport(std::move(socket))); if (!transport->InitializeProtocol(error)) { return nullptr; } return transport; } // These error strings are checked in tcp_test.cpp and should be kept in sync. bool TcpTransport::InitializeProtocol(std::string* error) { std::string handshake_message(android::base::StringPrintf("FB%02d", kProtocolVersion)); if (!socket_->Send(handshake_message.c_str(), kHandshakeLength)) { *error = android::base::StringPrintf("Failed to send initialization message (%s)", Socket::GetErrorMessage().c_str()); return false; } char buffer[kHandshakeLength]; if (socket_->ReceiveAll(buffer, kHandshakeLength, kHandshakeTimeoutMs) != kHandshakeLength) { *error = android::base::StringPrintf( "No initialization message received (%s). Target may not support TCP fastboot", Socket::GetErrorMessage().c_str()); return false; } if (memcmp(buffer, "FB", 2) != 0) { *error = "Unrecognized initialization message. Target may not support TCP fastboot"; return false; } if (memcmp(buffer + 2, "01", 2) != 0) { *error = android::base::StringPrintf("Unknown TCP protocol version %s (host version %02d)", std::string(buffer + 2, 2).c_str(), kProtocolVersion); return false; } error->clear(); return true; } ssize_t TcpTransport::Read(void* data, size_t length) { if (socket_ == nullptr) { return -1; } // Unless we're mid-message, read the next 8-byte message length. if (message_bytes_left_ == 0) { char buffer[8]; if (socket_->ReceiveAll(buffer, 8, 0) != 8) { Close(); return -1; } message_bytes_left_ = ExtractMessageLength(buffer); } // Now read the message (up to |length| bytes). if (length > message_bytes_left_) { length = message_bytes_left_; } ssize_t bytes_read = socket_->ReceiveAll(data, length, 0); if (bytes_read == -1) { Close(); } else { message_bytes_left_ -= bytes_read; } return bytes_read; } ssize_t TcpTransport::Write(const void* data, size_t length) { if (socket_ == nullptr) { return -1; } // Use multi-buffer writes for better performance. char header[8]; EncodeMessageLength(length, header); if (!socket_->Send(std::vector<cutils_socket_buffer_t>{{header, 8}, {data, length}})) { Close(); return -1; } return length; } int TcpTransport::Close() { if (socket_ == nullptr) { return 0; } int result = socket_->Close(); socket_.reset(); return result; } std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) { return internal::Connect(Socket::NewClient(Socket::Protocol::kTcp, hostname, port, error), error); } namespace internal { std::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error) { if (sock == nullptr) { // If Socket creation failed |error| is already set. return nullptr; } return TcpTransport::NewTransport(std::move(sock), error); } } // namespace internal } // namespace tcp
fastboot/tcp.h 0 → 100644 +59 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef TCP_H_ #define TCP_H_ #include <memory> #include <string> #include <android-base/macros.h> #include "socket.h" #include "transport.h" namespace tcp { constexpr int kDefaultPort = 5554; // Returns a newly allocated Transport object connected to |hostname|:|port|. On failure, |error| is // filled and nullptr is returned. std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error); // Internal namespace for test use only. namespace internal { // Creates a TCP Transport object but using a given Socket instead of connecting to a hostname. // Used for unit tests to create a Transport object that uses a SocketMock. std::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error); } // namespace internal } // namespace tcp #endif // TCP_H_