Merge "Add fastbootd."
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 2b4a954..51cc62a 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -1,3 +1,17 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 cc_library_host_static {
     name: "libfastboot2",
 
@@ -54,3 +68,54 @@
     export_include_dirs: ["."],
 
 }
+
+cc_defaults {
+    name: "fastboot_defaults",
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wvla",
+    ],
+    rtti: true,
+
+    clang_cflags: [
+        "-Wthread-safety",
+    ],
+}
+
+cc_binary {
+    name: "fastbootd",
+    defaults: ["fastboot_defaults"],
+
+    recovery: true,
+
+    srcs: [
+        "device/commands.cpp",
+        "device/fastboot_device.cpp",
+        "device/main.cpp",
+        "device/usb_client.cpp",
+    ],
+
+    shared_libs: [
+        "libasyncio",
+        "libext4_utils",
+        "libsparse",
+        "liblog",
+        "libbootloader_message",
+        "libhidltransport",
+        "libhidlbase",
+        "libhwbinder",
+        "libbase",
+        "libutils",
+        "libcutils",
+        "libfs_mgr",
+    ],
+
+    static_libs: [
+        "libadbd",
+    ],
+
+    cpp_std: "c++17",
+}
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 5e7e955..7caca3d 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -27,6 +27,8 @@
 #define FB_CMD_REBOOT "reboot"
 #define FB_CMD_SHUTDOWN "shutdown"
 #define FB_CMD_REBOOT_BOOTLOADER "reboot-bootloader"
+#define FB_CMD_REBOOT_RECOVERY "reboot-recovery"
+#define FB_CMD_REBOOT_FASTBOOT "reboot-fastboot"
 #define FB_CMD_POWERDOWN "powerdown"
 
 #define RESPONSE_OKAY "OKAY"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
new file mode 100644
index 0000000..a3cbf96
--- /dev/null
+++ b/fastboot/device/commands.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "commands.h"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/android_reboot.h>
+
+#include "fastboot_device.h"
+
+bool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteStatus(FastbootResult::FAIL, "size argument unspecified");
+    }
+    // arg[0] is the command name, arg[1] contains size of data to be downloaded
+    unsigned int size;
+    if (!android::base::ParseUint("0x" + args[1], &size, UINT_MAX)) {
+        return device->WriteStatus(FastbootResult::FAIL, "Invalid size");
+    }
+    device->get_download_data().resize(size);
+    if (!device->WriteStatus(FastbootResult::DATA, android::base::StringPrintf("%08x", size))) {
+        return false;
+    }
+
+    if (device->HandleData(true, &device->get_download_data())) {
+        return device->WriteStatus(FastbootResult::OKAY, "");
+    }
+
+    PLOG(ERROR) << "Couldn't download data";
+    return device->WriteStatus(FastbootResult::FAIL, "Couldn't download data");
+}
+
+bool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    return device->WriteStatus(FastbootResult::OKAY, "");
+}
+
+bool ShutDownHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Shutting down");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,fastboot");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+bool RebootHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Rebooting");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,from_fastboot");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+bool RebootBootloaderHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Rebooting bootloader");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+bool RebootFastbootHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Rebooting fastboot");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+static bool EnterRecovery() {
+    const char msg_switch_to_recovery = 'r';
+
+    android::base::unique_fd sock(socket(AF_UNIX, SOCK_STREAM, 0));
+    if (sock < 0) {
+        PLOG(ERROR) << "Couldn't create sock";
+        return false;
+    }
+
+    struct sockaddr_un addr = {.sun_family = AF_UNIX};
+    strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
+    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+        PLOG(ERROR) << "Couldn't connect to recovery";
+        return false;
+    }
+    // Switch to recovery will not update the boot reason since it does not
+    // require a reboot.
+    auto ret = write(sock, &msg_switch_to_recovery, sizeof(msg_switch_to_recovery));
+    if (ret != sizeof(msg_switch_to_recovery)) {
+        PLOG(ERROR) << "Couldn't write message to switch to recovery";
+        return false;
+    }
+
+    return true;
+}
+
+bool RebootRecoveryHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto status = true;
+    if (EnterRecovery()) {
+        status = device->WriteStatus(FastbootResult::OKAY, "Rebooting to recovery");
+    } else {
+        status = device->WriteStatus(FastbootResult::FAIL, "Unable to reboot to recovery");
+    }
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return status;
+}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
new file mode 100644
index 0000000..cd984c8
--- /dev/null
+++ b/fastboot/device/commands.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <functional>
+#include <string>
+#include <vector>
+
+class FastbootDevice;
+
+enum class FastbootResult {
+    OKAY,
+    FAIL,
+    INFO,
+    DATA,
+};
+
+// Execute a command with the given arguments (possibly empty).
+using CommandHandler = std::function<bool(FastbootDevice*, const std::vector<std::string>&)>;
+
+bool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool ShutDownHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootBootloaderHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootFastbootHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootRecoveryHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
new file mode 100644
index 0000000..4c3d23f
--- /dev/null
+++ b/fastboot/device/fastboot_device.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fastboot_device.h"
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include <algorithm>
+
+#include "constants.h"
+#include "usb_client.h"
+
+FastbootDevice::FastbootDevice()
+    : kCommandMap({
+              {FB_CMD_SET_ACTIVE, SetActiveHandler},
+              {FB_CMD_DOWNLOAD, DownloadHandler},
+              {FB_CMD_SHUTDOWN, ShutDownHandler},
+              {FB_CMD_REBOOT, RebootHandler},
+              {FB_CMD_REBOOT_BOOTLOADER, RebootBootloaderHandler},
+              {FB_CMD_REBOOT_FASTBOOT, RebootFastbootHandler},
+              {FB_CMD_REBOOT_RECOVERY, RebootRecoveryHandler},
+      }),
+      transport_(std::make_unique<ClientUsbTransport>()) {}
+
+FastbootDevice::~FastbootDevice() {
+    CloseDevice();
+}
+
+void FastbootDevice::CloseDevice() {
+    transport_->Close();
+}
+
+bool FastbootDevice::WriteStatus(FastbootResult result, const std::string& message) {
+    constexpr size_t kResponseReasonSize = 4;
+    constexpr size_t kNumResponseTypes = 4;  // "FAIL", "OKAY", "INFO", "DATA"
+    char buf[FB_RESPONSE_SZ];
+    constexpr size_t kMaxMessageSize = sizeof(buf) - kResponseReasonSize;
+    size_t msg_len = std::min(kMaxMessageSize, message.size());
+
+    constexpr const char* kResultStrings[kNumResponseTypes] = {RESPONSE_OKAY, RESPONSE_FAIL,
+                                                               RESPONSE_INFO, RESPONSE_DATA};
+
+    if (static_cast<size_t>(result) >= kNumResponseTypes) {
+        return false;
+    }
+
+    memcpy(buf, kResultStrings[static_cast<size_t>(result)], kResponseReasonSize);
+    memcpy(buf + kResponseReasonSize, message.c_str(), msg_len);
+
+    size_t response_len = kResponseReasonSize + msg_len;
+    auto write_ret = this->get_transport()->Write(buf, response_len);
+    if (write_ret != static_cast<ssize_t>(response_len)) {
+        PLOG(ERROR) << "Failed to write " << message;
+        return false;
+    }
+
+    return true;
+}
+
+bool FastbootDevice::HandleData(bool read, std::vector<char>* data) {
+    auto read_write_data_size = read ? this->get_transport()->Read(data->data(), data->size())
+                                     : this->get_transport()->Write(data->data(), data->size());
+    if (read_write_data_size == -1 || static_cast<size_t>(read_write_data_size) != data->size()) {
+        return false;
+    }
+    return true;
+}
+
+void FastbootDevice::ExecuteCommands() {
+    char command[FB_RESPONSE_SZ + 1];
+    for (;;) {
+        auto bytes_read = transport_->Read(command, FB_RESPONSE_SZ);
+        if (bytes_read == -1) {
+            PLOG(ERROR) << "Couldn't read command";
+            return;
+        }
+        command[bytes_read] = '\0';
+
+        LOG(INFO) << "Fastboot command: " << command;
+        auto args = android::base::Split(command, ":");
+        auto found_command = kCommandMap.find(args[0]);
+        if (found_command == kCommandMap.end()) {
+            WriteStatus(FastbootResult::FAIL, "Unrecognized command");
+            continue;
+        }
+        if (!found_command->second(this, args)) {
+            return;
+        }
+    }
+}
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
new file mode 100644
index 0000000..fec42a7
--- /dev/null
+++ b/fastboot/device/fastboot_device.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "commands.h"
+#include "transport.h"
+
+class FastbootDevice;
+
+class FastbootDevice {
+  public:
+    FastbootDevice();
+    ~FastbootDevice();
+
+    void CloseDevice();
+    void ExecuteCommands();
+    bool WriteStatus(FastbootResult result, const std::string& message);
+    bool HandleData(bool read, std::vector<char>* data);
+
+    std::vector<char>& get_download_data() { return download_data_; }
+    void set_upload_data(const std::vector<char>& data) { upload_data_ = data; }
+    void set_upload_data(std::vector<char>&& data) { upload_data_ = std::move(data); }
+    Transport* get_transport() { return transport_.get(); }
+
+  private:
+    const std::unordered_map<std::string, CommandHandler> kCommandMap;
+
+    std::unique_ptr<Transport> transport_;
+
+    std::vector<char> download_data_;
+    std::vector<char> upload_data_;
+};
diff --git a/fastboot/device/main.cpp b/fastboot/device/main.cpp
new file mode 100644
index 0000000..df9c900
--- /dev/null
+++ b/fastboot/device/main.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+
+#include "fastboot_device.h"
+
+int main(int /*argc*/, char* argv[]) {
+    android::base::InitLogging(argv, &android::base::KernelLogger);
+
+    while (true) {
+        FastbootDevice device;
+        device.ExecuteCommands();
+    }
+}
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
new file mode 100644
index 0000000..215f99a
--- /dev/null
+++ b/fastboot/device/usb_client.cpp
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "usb_client.h"
+
+#include <endian.h>
+#include <fcntl.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+constexpr int kMaxPacketSizeFs = 64;
+constexpr int kMaxPacketSizeHs = 512;
+constexpr int kMaxPacketsizeSs = 1024;
+
+constexpr size_t kFbFfsNumBufs = 16;
+constexpr size_t kFbFfsBufSize = 32768;
+
+constexpr const char* kUsbFfsFastbootEp0 = "/dev/usb-ffs/fastboot/ep0";
+constexpr const char* kUsbFfsFastbootOut = "/dev/usb-ffs/fastboot/ep1";
+constexpr const char* kUsbFfsFastbootIn = "/dev/usb-ffs/fastboot/ep2";
+
+struct FuncDesc {
+    struct usb_interface_descriptor intf;
+    struct usb_endpoint_descriptor_no_audio source;
+    struct usb_endpoint_descriptor_no_audio sink;
+} __attribute__((packed));
+
+struct SsFuncDesc {
+    struct usb_interface_descriptor intf;
+    struct usb_endpoint_descriptor_no_audio source;
+    struct usb_ss_ep_comp_descriptor source_comp;
+    struct usb_endpoint_descriptor_no_audio sink;
+    struct usb_ss_ep_comp_descriptor sink_comp;
+} __attribute__((packed));
+
+struct DescV2 {
+    struct usb_functionfs_descs_head_v2 header;
+    // The rest of the structure depends on the flags in the header.
+    __le32 fs_count;
+    __le32 hs_count;
+    __le32 ss_count;
+    struct FuncDesc fs_descs, hs_descs;
+    struct SsFuncDesc ss_descs;
+} __attribute__((packed));
+
+struct usb_interface_descriptor fastboot_interface = {
+        .bLength = USB_DT_INTERFACE_SIZE,
+        .bDescriptorType = USB_DT_INTERFACE,
+        .bInterfaceNumber = 0,
+        .bNumEndpoints = 2,
+        .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+        .bInterfaceSubClass = 66,
+        .bInterfaceProtocol = 3,
+        .iInterface = 1, /* first string from the provided table */
+};
+
+static struct FuncDesc fs_descriptors = {
+        .intf = fastboot_interface,
+        .source =
+                {
+                        .bLength = sizeof(fs_descriptors.source),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_OUT,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeFs,
+                },
+        .sink =
+                {
+                        .bLength = sizeof(fs_descriptors.sink),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_IN,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeFs,
+                },
+};
+
+static struct FuncDesc hs_descriptors = {
+        .intf = fastboot_interface,
+        .source =
+                {
+                        .bLength = sizeof(hs_descriptors.source),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_OUT,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeHs,
+                },
+        .sink =
+                {
+                        .bLength = sizeof(hs_descriptors.sink),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_IN,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeHs,
+                },
+};
+
+static struct SsFuncDesc ss_descriptors = {
+        .intf = fastboot_interface,
+        .source =
+                {
+                        .bLength = sizeof(ss_descriptors.source),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_OUT,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketsizeSs,
+                },
+        .source_comp =
+                {
+                        .bLength = sizeof(ss_descriptors.source_comp),
+                        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+                        .bMaxBurst = 15,
+                },
+        .sink =
+                {
+                        .bLength = sizeof(ss_descriptors.sink),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_IN,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketsizeSs,
+                },
+        .sink_comp =
+                {
+                        .bLength = sizeof(ss_descriptors.sink_comp),
+                        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+                        .bMaxBurst = 15,
+                },
+};
+
+#define STR_INTERFACE_ "fastboot"
+
+static const struct {
+    struct usb_functionfs_strings_head header;
+    struct {
+        __le16 code;
+        const char str1[sizeof(STR_INTERFACE_)];
+    } __attribute__((packed)) lang0;
+} __attribute__((packed)) strings = {
+        .header =
+                {
+                        .magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
+                        .length = htole32(sizeof(strings)),
+                        .str_count = htole32(1),
+                        .lang_count = htole32(1),
+                },
+        .lang0 =
+                {
+                        htole16(0x0409), /* en-us */
+                        STR_INTERFACE_,
+                },
+};
+
+static struct DescV2 v2_descriptor = {
+        .header =
+                {
+                        .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+                        .length = htole32(sizeof(v2_descriptor)),
+                        .flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
+                                 FUNCTIONFS_HAS_SS_DESC,
+                },
+        .fs_count = 3,
+        .hs_count = 3,
+        .ss_count = 5,
+        .fs_descs = fs_descriptors,
+        .hs_descs = hs_descriptors,
+        .ss_descs = ss_descriptors,
+};
+
+// Reimplementing since usb_ffs_close() does not close the control FD.
+static void CloseFunctionFs(usb_handle* h) {
+    if (h->bulk_in > 0) {
+        close(h->bulk_in);
+        h->bulk_in = -1;
+    }
+    if (h->bulk_out > 0) {
+        close(h->bulk_out);
+        h->bulk_out = -1;
+    }
+    if (h->control > 0) {
+        close(h->control);
+        h->control = -1;
+    }
+}
+
+static bool InitFunctionFs(usb_handle* h) {
+    LOG(INFO) << "initializing functionfs";
+
+    if (h->control < 0) {  // might have already done this before
+        LOG(INFO) << "opening control endpoint " << kUsbFfsFastbootEp0;
+        h->control = open(kUsbFfsFastbootEp0, O_RDWR);
+        if (h->control < 0) {
+            PLOG(ERROR) << "cannot open control endpoint " << kUsbFfsFastbootEp0;
+            goto err;
+        }
+
+        auto ret = write(h->control, &v2_descriptor, sizeof(v2_descriptor));
+        if (ret < 0) {
+            PLOG(ERROR) << "cannot write descriptors " << kUsbFfsFastbootEp0;
+            goto err;
+        }
+
+        ret = write(h->control, &strings, sizeof(strings));
+        if (ret < 0) {
+            PLOG(ERROR) << "cannot write strings " << kUsbFfsFastbootEp0;
+            goto err;
+        }
+        // Signal only when writing the descriptors to ffs
+        android::base::SetProperty("sys.usb.ffs.ready", "1");
+    }
+
+    h->bulk_out = open(kUsbFfsFastbootOut, O_RDONLY);
+    if (h->bulk_out < 0) {
+        PLOG(ERROR) << "cannot open bulk-out endpoint " << kUsbFfsFastbootOut;
+        goto err;
+    }
+
+    h->bulk_in = open(kUsbFfsFastbootIn, O_WRONLY);
+    if (h->bulk_in < 0) {
+        PLOG(ERROR) << "cannot open bulk-in endpoint " << kUsbFfsFastbootIn;
+        goto err;
+    }
+
+    h->read_aiob.fd = h->bulk_out;
+    h->write_aiob.fd = h->bulk_in;
+    h->reads_zero_packets = false;
+    return true;
+
+err:
+    CloseFunctionFs(h);
+    return false;
+}
+
+ClientUsbTransport::ClientUsbTransport()
+    : handle_(std::unique_ptr<usb_handle>(create_usb_handle(kFbFfsNumBufs, kFbFfsBufSize))) {
+    if (!InitFunctionFs(handle_.get())) {
+        handle_.reset(nullptr);
+    }
+}
+
+ssize_t ClientUsbTransport::Read(void* data, size_t len) {
+    if (handle_ == nullptr || len > SSIZE_MAX) {
+        return -1;
+    }
+    char* char_data = static_cast<char*>(data);
+    size_t bytes_read_total = 0;
+    while (bytes_read_total < len) {
+        auto bytes_to_read = std::min(len - bytes_read_total, kFbFfsNumBufs * kFbFfsBufSize);
+        auto bytes_read_now = handle_->read(handle_.get(), char_data, bytes_to_read);
+        if (bytes_read_now < 0) {
+            return bytes_read_total;
+        }
+        bytes_read_total += bytes_read_now;
+        char_data += bytes_read_now;
+        if (static_cast<size_t>(bytes_read_now) < bytes_to_read) {
+            break;
+        }
+    }
+    return bytes_read_total;
+}
+
+ssize_t ClientUsbTransport::Write(const void* data, size_t len) {
+    if (handle_ == nullptr || len > SSIZE_MAX) {
+        return -1;
+    }
+    const char* char_data = reinterpret_cast<const char*>(data);
+    size_t bytes_written_total = 0;
+    while (bytes_written_total < len) {
+        auto bytes_to_write = std::min(len - bytes_written_total, kFbFfsNumBufs * kFbFfsBufSize);
+        auto bytes_written_now = handle_->write(handle_.get(), data, bytes_to_write);
+        if (bytes_written_now < 0) {
+            return bytes_written_total;
+        }
+        bytes_written_total += bytes_written_now;
+        char_data += bytes_written_now;
+        if (static_cast<size_t>(bytes_written_now) < bytes_to_write) {
+            break;
+        }
+    }
+    return bytes_written_total;
+}
+
+int ClientUsbTransport::Close() {
+    if (handle_ == nullptr) {
+        return -1;
+    }
+    CloseFunctionFs(handle_.get());
+    return 0;
+}
diff --git a/fastboot/device/usb_client.h b/fastboot/device/usb_client.h
new file mode 100644
index 0000000..3694f9a
--- /dev/null
+++ b/fastboot/device/usb_client.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include <adbd/usb.h>
+
+#include "transport.h"
+
+class ClientUsbTransport : public Transport {
+  public:
+    ClientUsbTransport();
+    ~ClientUsbTransport() override = default;
+
+    ssize_t Read(void* data, size_t len) override;
+    ssize_t Write(const void* data, size_t len) override;
+    int Close() override;
+
+  private:
+    std::unique_ptr<usb_handle> handle_;
+
+    DISALLOW_COPY_AND_ASSIGN(ClientUsbTransport);
+};
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 321e6ba..fb06d19 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1333,6 +1333,8 @@
     bool wants_wipe = false;
     bool wants_reboot = false;
     bool wants_reboot_bootloader = false;
+    bool wants_reboot_recovery = false;
+    bool wants_reboot_fastboot = false;
     bool skip_reboot = false;
     bool wants_set_active = false;
     bool skip_secondary = false;
@@ -1555,6 +1557,12 @@
                 if (what == "bootloader") {
                     wants_reboot = false;
                     wants_reboot_bootloader = true;
+                } else if (what == "recovery") {
+                    wants_reboot = false;
+                    wants_reboot_recovery = true;
+                } else if (what == "fastboot") {
+                    wants_reboot = false;
+                    wants_reboot_fastboot = true;
                 } else {
                     syntax_error("unknown reboot target %s", what.c_str());
                 }
@@ -1563,6 +1571,10 @@
             if (!args.empty()) syntax_error("junk after reboot command");
         } else if (command == "reboot-bootloader") {
             wants_reboot_bootloader = true;
+        } else if (command == "reboot-recovery") {
+            wants_reboot_recovery = true;
+        } else if (command == "reboot-fastboot") {
+            wants_reboot_fastboot = true;
         } else if (command == "continue") {
             fb_queue_command("continue", "resuming boot");
         } else if (command == "boot") {
@@ -1680,6 +1692,12 @@
     } else if (wants_reboot_bootloader) {
         fb_queue_command("reboot-bootloader", "rebooting into bootloader");
         fb_queue_wait_for_disconnect();
+    } else if (wants_reboot_recovery) {
+        fb_queue_command("reboot-recovery", "rebooting into recovery");
+        fb_queue_wait_for_disconnect();
+    } else if (wants_reboot_fastboot) {
+        fb_queue_command("reboot-fastboot", "rebooting into fastboot");
+        fb_queue_wait_for_disconnect();
     }
 
     int status = fb_execute_queue() ? EXIT_FAILURE : EXIT_SUCCESS;