Merge "init: clean up logging initialization"
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 20d0db5..9b48702 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -264,15 +264,6 @@
     send_packet(cp, t);
 }
 
-// qual_overwrite is used to overwrite a qualifier string.  dst is a
-// pointer to a char pointer.  It is assumed that if *dst is non-NULL, it
-// was malloc'ed and needs to freed.  *dst will be set to a dup of src.
-// TODO: switch to std::string for these atransport fields instead.
-static void qual_overwrite(char** dst, const std::string& src) {
-    free(*dst);
-    *dst = strdup(src.c_str());
-}
-
 void parse_banner(const std::string& banner, atransport* t) {
     D("parse_banner: %s", banner.c_str());
 
@@ -296,11 +287,11 @@
             const std::string& key = key_value[0];
             const std::string& value = key_value[1];
             if (key == "ro.product.name") {
-                qual_overwrite(&t->product, value);
+                t->product = value;
             } else if (key == "ro.product.model") {
-                qual_overwrite(&t->model, value);
+                t->model = value;
             } else if (key == "ro.product.device") {
-                qual_overwrite(&t->device, value);
+                t->device = value;
             } else if (key == "features") {
                 t->SetFeatures(value);
             }
@@ -424,8 +415,8 @@
                     /* Other READY messages must use the same local-id */
                     s->ready(s);
                 } else {
-                    D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s",
-                      p->msg.arg0, p->msg.arg1, s->peer->id, p->msg.arg1, t->serial);
+                    D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s", p->msg.arg0,
+                      p->msg.arg1, s->peer->id, p->msg.arg1, t->serial.c_str());
                 }
             } else {
                 // When receiving A_OKAY from device for A_OPEN request, the host server may
@@ -451,8 +442,8 @@
                  * socket has a peer on the same transport.
                  */
                 if (p->msg.arg0 == 0 && s->peer && s->peer->transport != t) {
-                    D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s",
-                      p->msg.arg1, t->serial, s->peer->transport->serial);
+                    D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s", p->msg.arg1,
+                      t->serial.c_str(), s->peer->transport->serial.c_str());
                 } else {
                     s->close(s);
                 }
@@ -1171,7 +1162,7 @@
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
-            return SendOkay(reply_fd, t->serial ? t->serial : "unknown");
+            return SendOkay(reply_fd, !t->serial.empty() ? t->serial : "unknown");
         } else {
             return SendFail(reply_fd, error);
         }
@@ -1180,7 +1171,7 @@
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
-            return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown");
+            return SendOkay(reply_fd, !t->devpath.empty() ? t->devpath : "unknown");
         } else {
             return SendFail(reply_fd, error);
         }
diff --git a/adb/adb.h b/adb/adb.h
index c884166..7e9af9e 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -156,11 +156,6 @@
 
 int handle_forward_request(const char* service, atransport* transport, int reply_fd);
 
-#if !ADB_HOST
-void framebuffer_service(int fd, void* cookie);
-void set_verity_enabled_state_service(int fd, void* cookie);
-#endif
-
 /* packet allocator */
 apacket* get_apacket(void);
 void put_apacket(apacket* p);
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index ea5a44e..f4a92e3 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -136,9 +136,10 @@
         }
         //  <device-serial> " " <local-name> " " <remote-name> "\n"
         // Entries from "adb reverse" have no serial.
-        android::base::StringAppendF(&result, "%s %s %s\n",
-                                     l->transport->serial ? l->transport->serial : "(reverse)",
-                                     l->local_name.c_str(), l->connect_to.c_str());
+        android::base::StringAppendF(
+                &result, "%s %s %s\n",
+                !l->transport->serial.empty() ? l->transport->serial.c_str() : "(reverse)",
+                l->local_name.c_str(), l->connect_to.c_str());
     }
     return result;
 }
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 80d0dd3..7791895 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -362,9 +362,8 @@
 }
 
 static void copy_to_file(int inFd, int outFd) {
-    const size_t BUFSIZE = 32 * 1024;
-    char* buf = (char*) malloc(BUFSIZE);
-    if (buf == nullptr) fatal("couldn't allocate buffer for copy_to_file");
+    constexpr size_t BUFSIZE = 32 * 1024;
+    std::vector<char> buf(BUFSIZE);
     int len;
     long total = 0;
     int old_stdin_mode = -1;
@@ -376,9 +375,9 @@
 
     while (true) {
         if (inFd == STDIN_FILENO) {
-            len = unix_read(inFd, buf, BUFSIZE);
+            len = unix_read(inFd, buf.data(), BUFSIZE);
         } else {
-            len = adb_read(inFd, buf, BUFSIZE);
+            len = adb_read(inFd, buf.data(), BUFSIZE);
         }
         if (len == 0) {
             D("copy_to_file() : read 0 bytes; exiting");
@@ -389,10 +388,10 @@
             break;
         }
         if (outFd == STDOUT_FILENO) {
-            fwrite(buf, 1, len, stdout);
+            fwrite(buf.data(), 1, len, stdout);
             fflush(stdout);
         } else {
-            adb_write(outFd, buf, len);
+            adb_write(outFd, buf.data(), len);
         }
         total += len;
     }
@@ -400,7 +399,6 @@
     stdinout_raw_epilogue(inFd, outFd, old_stdin_mode, old_stdout_mode);
 
     D("copy_to_file() finished after %lu bytes", total);
-    free(buf);
 }
 
 static void send_window_size_change(int fd, std::unique_ptr<ShellProtocol>& shell) {
@@ -1142,24 +1140,22 @@
 static void write_zeros(int bytes, int fd) {
     int old_stdin_mode = -1;
     int old_stdout_mode = -1;
-    char* buf = (char*) calloc(1, bytes);
-    if (buf == nullptr) fatal("couldn't allocate buffer for write_zeros");
+    std::vector<char> buf(bytes);
 
     D("write_zeros(%d) -> %d", bytes, fd);
 
     stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
 
     if (fd == STDOUT_FILENO) {
-        fwrite(buf, 1, bytes, stdout);
+        fwrite(buf.data(), 1, bytes, stdout);
         fflush(stdout);
     } else {
-        adb_write(fd, buf, bytes);
+        adb_write(fd, buf.data(), bytes);
     }
 
     stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
 
     D("write_zeros() finished");
-    free(buf);
 }
 
 static int backup(int argc, const char** argv) {
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index f7be8f9..0b71363 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -525,12 +525,11 @@
     return true;
 }
 
-void file_sync_service(int fd, void*) {
+void file_sync_service(android::base::unique_fd fd) {
     std::vector<char> buffer(SYNC_DATA_MAX);
 
-    while (handle_sync_command(fd, buffer)) {
+    while (handle_sync_command(fd.get(), buffer)) {
     }
 
     D("sync: done");
-    adb_close(fd);
 }
diff --git a/adb/daemon/framebuffer_service.cpp b/adb/daemon/framebuffer_service.cpp
index 20e03f9..9a620ab 100644
--- a/adb/daemon/framebuffer_service.cpp
+++ b/adb/daemon/framebuffer_service.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "framebuffer_service.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/fb.h>
@@ -55,8 +57,7 @@
     unsigned int alpha_length;
 } __attribute__((packed));
 
-void framebuffer_service(int fd, void *cookie)
-{
+void framebuffer_service(android::base::unique_fd fd) {
     struct fbinfo fbinfo;
     unsigned int i, bsize;
     char buf[640];
@@ -65,7 +66,7 @@
     int fds[2];
     pid_t pid;
 
-    if (pipe2(fds, O_CLOEXEC) < 0) goto pipefail;
+    if (pipe2(fds, O_CLOEXEC) < 0) return;
 
     pid = fork();
     if (pid < 0) goto done;
@@ -168,7 +169,7 @@
     }
 
     /* write header */
-    if(!WriteFdExactly(fd, &fbinfo, sizeof(fbinfo))) goto done;
+    if (!WriteFdExactly(fd.get(), &fbinfo, sizeof(fbinfo))) goto done;
 
     /* write data */
     for(i = 0; i < fbinfo.size; i += bsize) {
@@ -176,13 +177,11 @@
       if (i + bsize > fbinfo.size)
         bsize = fbinfo.size - i;
       if(!ReadFdExactly(fd_screencap, buf, bsize)) goto done;
-      if(!WriteFdExactly(fd, buf, bsize)) goto done;
+      if (!WriteFdExactly(fd.get(), buf, bsize)) goto done;
     }
 
 done:
     adb_close(fds[0]);
 
     TEMP_FAILURE_RETRY(waitpid(pid, nullptr, 0));
-pipefail:
-    adb_close(fd);
 }
diff --git a/adb/daemon/framebuffer_service.h b/adb/daemon/framebuffer_service.h
new file mode 100644
index 0000000..d99c6fe
--- /dev/null
+++ b/adb/daemon/framebuffer_service.h
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+#ifndef _DAEMON_FRAMEBUFFER_SERVICE_H_
+#define _DAEMON_FRAMEBUFFER_SERVICE_H_
+
+#include <android-base/unique_fd.h>
+
+void framebuffer_service(android::base::unique_fd fd);
+
+#endif  // _DAEMON_FRAMEBUFFER_SERVICE_H_
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 830b35d..dfdc365 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mount.h>
+#include <sys/statvfs.h>
 #include <sys/vfs.h>
 #include <unistd.h>
 
@@ -43,6 +44,7 @@
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
 #include "fs_mgr.h"
+#include "set_verity_enable_state_service.h"
 
 // Returns the device used to mount a directory in /proc/mounts.
 static std::string find_proc_mount(const char* dir) {
@@ -145,6 +147,17 @@
     return true;
 }
 
+static unsigned long get_mount_flags(int fd, const char* dir) {
+    struct statvfs st_vfs;
+    if (statvfs(dir, &st_vfs) == -1) {
+        // Even though we could not get the original mount flags, assume that
+        // the mount was originally read-only.
+        WriteFdFmt(fd, "statvfs of the %s mount failed: %s.\n", dir, strerror(errno));
+        return MS_RDONLY;
+    }
+    return st_vfs.f_flag;
+}
+
 static bool remount_partition(int fd, const char* dir) {
     if (!directory_exists(dir)) {
         return true;
@@ -163,14 +176,23 @@
                    dir, dev.c_str(), strerror(errno));
         return false;
     }
-    if (mount(dev.c_str(), dir, "none", MS_REMOUNT | MS_BIND, nullptr) == -1) {
+
+    unsigned long remount_flags = get_mount_flags(fd, dir);
+    if ((remount_flags & MS_RDONLY) == 0) {
+        // Mount is already writable.
+        return true;
+    }
+    remount_flags &= ~MS_RDONLY;
+    remount_flags |= MS_REMOUNT;
+
+    if (mount(dev.c_str(), dir, "none", remount_flags | MS_BIND, nullptr) == -1) {
         // This is useful for cases where the superblock is already marked as
         // read-write, but the mount itself is read-only, such as containers
         // where the remount with just MS_REMOUNT is forbidden by the kernel.
         WriteFdFmt(fd, "remount of the %s mount failed: %s.\n", dir, strerror(errno));
         return false;
     }
-    if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
+    if (mount(dev.c_str(), dir, "none", remount_flags, nullptr) == -1) {
         WriteFdFmt(fd, "remount of the %s superblock failed: %s\n", dir, strerror(errno));
         return false;
     }
@@ -197,14 +219,11 @@
     android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd.c_str());
 }
 
-void remount_service(int fd, void* cookie) {
-    unique_fd close_fd(fd);
-
-    const char* cmd = reinterpret_cast<const char*>(cookie);
-    bool user_requested_reboot = cmd && !strcmp(cmd, "-R");
+void remount_service(android::base::unique_fd fd, const std::string& cmd) {
+    bool user_requested_reboot = cmd != "-R";
 
     if (getuid() != 0) {
-        WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
+        WriteFdExactly(fd.get(), "Not running as root. Try \"adb root\" first.\n");
         return;
     }
 
@@ -225,7 +244,7 @@
         if (dev.empty() || !fs_has_shared_blocks(dev.c_str())) {
             continue;
         }
-        if (can_unshare_blocks(fd, dev.c_str())) {
+        if (can_unshare_blocks(fd.get(), dev.c_str())) {
             dedup.emplace(partition);
         }
     }
@@ -236,12 +255,12 @@
     if (user_requested_reboot) {
         if (!dedup.empty() || verity_enabled) {
             if (verity_enabled) {
-                set_verity_enabled_state_service(fd, nullptr);
+                set_verity_enabled_state_service(android::base::unique_fd(dup(fd.get())), false);
             }
-            reboot_for_remount(fd, !dedup.empty());
+            reboot_for_remount(fd.get(), !dedup.empty());
             return;
         }
-        WriteFdExactly(fd, "No reboot needed, skipping -R.\n");
+        WriteFdExactly(fd.get(), "No reboot needed, skipping -R.\n");
     }
 
     // If we need to disable-verity, but we also need to perform a recovery
@@ -250,17 +269,14 @@
     if (verity_enabled && dedup.empty()) {
         // Allow remount but warn of likely bad effects
         bool both = system_verified && vendor_verified;
-        WriteFdFmt(fd,
-                   "dm_verity is enabled on the %s%s%s partition%s.\n",
-                   system_verified ? "system" : "",
-                   both ? " and " : "",
-                   vendor_verified ? "vendor" : "",
-                   both ? "s" : "");
-        WriteFdExactly(fd,
+        WriteFdFmt(fd.get(), "dm_verity is enabled on the %s%s%s partition%s.\n",
+                   system_verified ? "system" : "", both ? " and " : "",
+                   vendor_verified ? "vendor" : "", both ? "s" : "");
+        WriteFdExactly(fd.get(),
                        "Use \"adb disable-verity\" to disable verity.\n"
                        "If you do not, remount may succeed, however, you will still "
                        "not be able to write to these volumes.\n");
-        WriteFdExactly(fd,
+        WriteFdExactly(fd.get(),
                        "Alternately, use \"adb remount -R\" to disable verity "
                        "and automatically reboot.\n");
     }
@@ -271,29 +287,29 @@
         if (dedup.count(partition)) {
             continue;
         }
-        success &= remount_partition(fd, partition.c_str());
+        success &= remount_partition(fd.get(), partition.c_str());
     }
 
     if (!dedup.empty()) {
-        WriteFdExactly(fd,
+        WriteFdExactly(fd.get(),
                        "The following partitions are deduplicated and cannot "
                        "yet be remounted:\n");
         for (const std::string& name : dedup) {
-            WriteFdFmt(fd, "  %s\n", name.c_str());
+            WriteFdFmt(fd.get(), "  %s\n", name.c_str());
         }
 
-        WriteFdExactly(fd,
+        WriteFdExactly(fd.get(),
                        "To reboot and un-deduplicate the listed partitions, "
                        "please retry with adb remount -R.\n");
         if (system_verified || vendor_verified) {
-            WriteFdExactly(fd, "Note: verity will be automatically disabled after reboot.\n");
+            WriteFdExactly(fd.get(), "Note: verity will be automatically disabled after reboot.\n");
         }
         return;
     }
 
     if (!success) {
-        WriteFdExactly(fd, "remount failed\n");
+        WriteFdExactly(fd.get(), "remount failed\n");
     } else {
-        WriteFdExactly(fd, "remount succeeded\n");
+        WriteFdExactly(fd.get(), "remount succeeded\n");
     }
 }
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
index dbeee28..0f804e9 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -16,6 +16,7 @@
 
 #define TRACE_TAG ADB
 
+#include "set_verity_enable_state_service.h"
 #include "sysdeps.h"
 
 #include <fcntl.h>
@@ -25,8 +26,8 @@
 #include <stdio.h>
 #include <sys/stat.h>
 
-#include "android-base/properties.h"
-#include "android-base/stringprintf.h"
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <log/log_properties.h>
 
 #include "adb.h"
@@ -131,12 +132,9 @@
     return true;
 }
 
-void set_verity_enabled_state_service(int fd, void* cookie) {
-    unique_fd closer(fd);
+void set_verity_enabled_state_service(android::base::unique_fd fd, bool enable) {
     bool any_changed = false;
 
-    bool enable = (cookie != nullptr);
-
     // Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
     // contract, androidboot.vbmeta.digest is set by the bootloader
     // when using AVB).
@@ -147,12 +145,12 @@
     // VB1.0 dm-verity is only enabled on certain builds.
     if (!using_avb) {
         if (!kAllowDisableVerity) {
-            WriteFdFmt(fd, "%s-verity only works for userdebug builds\n",
+            WriteFdFmt(fd.get(), "%s-verity only works for userdebug builds\n",
                        enable ? "enable" : "disable");
         }
 
         if (!android::base::GetBoolProperty("ro.secure", false)) {
-            WriteFdFmt(fd, "verity not enabled - ENG build\n");
+            WriteFdFmt(fd.get(), "verity not enabled - ENG build\n");
             return;
         }
     }
@@ -160,7 +158,7 @@
     // Should never be possible to disable dm-verity on a USER build
     // regardless of using AVB or VB1.0.
     if (!__android_log_is_debuggable()) {
-        WriteFdFmt(fd, "verity cannot be disabled/enabled - USER build\n");
+        WriteFdFmt(fd.get(), "verity cannot be disabled/enabled - USER build\n");
         return;
     }
 
@@ -168,10 +166,10 @@
         // Yep, the system is using AVB.
         AvbOps* ops = avb_ops_user_new();
         if (ops == nullptr) {
-            WriteFdFmt(fd, "Error getting AVB ops\n");
+            WriteFdFmt(fd.get(), "Error getting AVB ops\n");
             return;
         }
-        if (set_avb_verity_enabled_state(fd, ops, enable)) {
+        if (set_avb_verity_enabled_state(fd.get(), ops, enable)) {
             any_changed = true;
         }
         avb_ops_user_free(ops);
@@ -181,14 +179,14 @@
         // read all fstab entries at once from all sources
         fstab = fs_mgr_read_fstab_default();
         if (!fstab) {
-            WriteFdFmt(fd, "Failed to read fstab\nMaybe run adb root?\n");
+            WriteFdFmt(fd.get(), "Failed to read fstab\nMaybe run adb root?\n");
             return;
         }
 
         // Loop through entries looking for ones that vold manages.
         for (int i = 0; i < fstab->num_entries; i++) {
             if (fs_mgr_is_verified(&fstab->recs[i])) {
-                if (set_verity_enabled_state(fd, fstab->recs[i].blk_device,
+                if (set_verity_enabled_state(fd.get(), fstab->recs[i].blk_device,
                                              fstab->recs[i].mount_point, enable)) {
                     any_changed = true;
                 }
@@ -197,6 +195,6 @@
     }
 
     if (any_changed) {
-        WriteFdFmt(fd, "Now reboot your device for settings to take effect\n");
+        WriteFdFmt(fd.get(), "Now reboot your device for settings to take effect\n");
     }
 }
diff --git a/adb/daemon/set_verity_enable_state_service.h b/adb/daemon/set_verity_enable_state_service.h
new file mode 100644
index 0000000..9f84f35
--- /dev/null
+++ b/adb/daemon/set_verity_enable_state_service.h
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+#ifndef _DAEMON_SET_VERITY_ENABLED_STATE_SERVICE_H_
+#define _DAEMON_SET_VERITY_ENABLED_STATE_SERVICE_H_
+
+#include <android-base/unique_fd.h>
+
+void set_verity_enabled_state_service(android::base::unique_fd fd, bool enable);
+
+#endif  // _DAEMON_SET_VERITY_ENABLED_STATE_SERVICE_H_
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 6606efd..608b3ad 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -20,6 +20,8 @@
 #include <string>
 #include <vector>
 
+#include <android-base/unique_fd.h>
+
 #define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
 
 #define ID_LSTAT_V1 MKID('S','T','A','T')
@@ -79,7 +81,7 @@
     } status;
 };
 
-void file_sync_service(int fd, void* cookie);
+void file_sync_service(android::base::unique_fd fd);
 bool do_sync_ls(const char* path);
 bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync);
 bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
diff --git a/adb/remount_service.h b/adb/remount_service.h
index 7bda1be..45821ee 100644
--- a/adb/remount_service.h
+++ b/adb/remount_service.h
@@ -19,7 +19,9 @@
 
 #include <string>
 
+#include <android-base/unique_fd.h>
+
 bool make_block_device_writable(const std::string&);
-void remount_service(int, void*);
+void remount_service(android::base::unique_fd, const std::string&);
 
 #endif
diff --git a/adb/services.cpp b/adb/services.cpp
index 1fa7ecc..3d418cb 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -37,6 +37,7 @@
 #include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 
 #if !ADB_HOST
@@ -49,6 +50,10 @@
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_utils.h"
+#if !ADB_HOST
+#include "daemon/framebuffer_service.h"
+#include "daemon/set_verity_enable_state_service.h"
+#endif
 #include "file_sync_service.h"
 #include "remount_service.h"
 #include "services.h"
@@ -57,81 +62,67 @@
 #include "sysdeps.h"
 #include "transport.h"
 
-struct stinfo {
-    const char* service_name;
-    void (*func)(int fd, void *cookie);
-    int fd;
-    void *cookie;
-};
+namespace {
 
-static void service_bootstrap_func(void* x) {
-    stinfo* sti = reinterpret_cast<stinfo*>(x);
-    adb_thread_setname(android::base::StringPrintf("%s svc %d", sti->service_name, sti->fd));
-    sti->func(sti->fd, sti->cookie);
-    free(sti);
+void service_bootstrap_func(std::string service_name,
+                            std::function<void(android::base::unique_fd)> func,
+                            android::base::unique_fd fd) {
+    adb_thread_setname(android::base::StringPrintf("%s svc %d", service_name.c_str(), fd.get()));
+    func(std::move(fd));
 }
 
 #if !ADB_HOST
 
-void restart_root_service(int fd, void *cookie) {
+void restart_root_service(android::base::unique_fd fd) {
     if (getuid() == 0) {
-        WriteFdExactly(fd, "adbd is already running as root\n");
-        adb_close(fd);
-    } else {
-        if (!__android_log_is_debuggable()) {
-            WriteFdExactly(fd, "adbd cannot run as root in production builds\n");
-            adb_close(fd);
-            return;
-        }
-
-        android::base::SetProperty("service.adb.root", "1");
-        WriteFdExactly(fd, "restarting adbd as root\n");
-        adb_close(fd);
+        WriteFdExactly(fd.get(), "adbd is already running as root\n");
+        return;
     }
+    if (!__android_log_is_debuggable()) {
+        WriteFdExactly(fd.get(), "adbd cannot run as root in production builds\n");
+        return;
+    }
+
+    android::base::SetProperty("service.adb.root", "1");
+    WriteFdExactly(fd.get(), "restarting adbd as root\n");
 }
 
-void restart_unroot_service(int fd, void *cookie) {
+void restart_unroot_service(android::base::unique_fd fd) {
     if (getuid() != 0) {
-        WriteFdExactly(fd, "adbd not running as root\n");
-        adb_close(fd);
-    } else {
-        android::base::SetProperty("service.adb.root", "0");
-        WriteFdExactly(fd, "restarting adbd as non root\n");
-        adb_close(fd);
+        WriteFdExactly(fd.get(), "adbd not running as root\n");
+        return;
     }
+    android::base::SetProperty("service.adb.root", "0");
+    WriteFdExactly(fd.get(), "restarting adbd as non root\n");
 }
 
-void restart_tcp_service(int fd, void *cookie) {
-    int port = (int) (uintptr_t) cookie;
+void restart_tcp_service(android::base::unique_fd fd, int port) {
     if (port <= 0) {
-        WriteFdFmt(fd, "invalid port %d\n", port);
-        adb_close(fd);
+        WriteFdFmt(fd.get(), "invalid port %d\n", port);
         return;
     }
 
     android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
-    WriteFdFmt(fd, "restarting in TCP mode port: %d\n", port);
-    adb_close(fd);
+    WriteFdFmt(fd.get(), "restarting in TCP mode port: %d\n", port);
 }
 
-void restart_usb_service(int fd, void *cookie) {
+void restart_usb_service(android::base::unique_fd fd) {
     android::base::SetProperty("service.adb.tcp.port", "0");
-    WriteFdExactly(fd, "restarting in USB mode\n");
-    adb_close(fd);
+    WriteFdExactly(fd.get(), "restarting in USB mode\n");
 }
 
-static bool reboot_service_impl(int fd, const char* arg) {
-    const char* reboot_arg = arg;
+bool reboot_service_impl(android::base::unique_fd fd, const std::string& arg) {
+    std::string reboot_arg = arg;
     bool auto_reboot = false;
 
-    if (strcmp(reboot_arg, "sideload-auto-reboot") == 0) {
+    if (reboot_arg == "sideload-auto-reboot") {
         auto_reboot = true;
         reboot_arg = "sideload";
     }
 
     // It reboots into sideload mode by setting "--sideload" or "--sideload_auto_reboot"
     // in the command file.
-    if (strcmp(reboot_arg, "sideload") == 0) {
+    if (reboot_arg == "sideload") {
         if (getuid() != 0) {
             WriteFdExactly(fd, "'adb root' is required for 'adb reboot sideload'.\n");
             return false;
@@ -151,8 +142,8 @@
 
     sync();
 
-    if (!reboot_arg || !reboot_arg[0]) reboot_arg = "adb";
-    std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg);
+    if (reboot_arg.empty()) reboot_arg = "adb";
+    std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
     if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
         WriteFdFmt(fd, "reboot (%s) failed\n", reboot_string.c_str());
         return false;
@@ -161,23 +152,19 @@
     return true;
 }
 
-void reboot_service(int fd, void* arg) {
-    if (reboot_service_impl(fd, static_cast<const char*>(arg))) {
-        // Don't return early. Give the reboot command time to take effect
-        // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
-        while (true) {
-            pause();
-        }
+void reboot_service(android::base::unique_fd fd, const std::string& arg) {
+    if (!reboot_service_impl(std::move(fd), arg)) {
+        return;
     }
-
-    free(arg);
-    adb_close(fd);
+    // Don't return early. Give the reboot command time to take effect
+    // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
+    while (true) {
+        pause();
+    }
 }
 
-static void reconnect_service(int fd, void* arg) {
+void reconnect_service(android::base::unique_fd fd, atransport* t) {
     WriteFdExactly(fd, "done");
-    adb_close(fd);
-    atransport* t = static_cast<atransport*>(arg);
     kick_transport(t);
 }
 
@@ -197,7 +184,7 @@
 
 // Shell service string can look like:
 //   shell[,arg1,arg2,...]:[command]
-static int ShellService(const std::string& args, const atransport* transport) {
+int ShellService(const std::string& args, const atransport* transport) {
     size_t delimiter_index = args.find(':');
     if (delimiter_index == std::string::npos) {
         LOG(ERROR) << "No ':' found in shell service arguments: " << args;
@@ -236,16 +223,17 @@
 
 #endif  // !ADB_HOST
 
-static int create_service_thread(const char* service_name, void (*func)(int, void*), void* cookie) {
+android::base::unique_fd create_service_thread(const char* service_name,
+                                               std::function<void(android::base::unique_fd)> func) {
     int s[2];
     if (adb_socketpair(s)) {
         printf("cannot create service socket pair\n");
-        return -1;
+        return android::base::unique_fd();
     }
     D("socketpair: (%d,%d)", s[0], s[1]);
 
 #if !ADB_HOST
-    if (func == &file_sync_service) {
+    if (strcmp(service_name, "sync") == 0) {
         // Set file sync service socket to maximum size
         int max_buf = LINUX_MAX_SOCKET_SIZE;
         adb_setsockopt(s[0], SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
@@ -253,21 +241,14 @@
     }
 #endif // !ADB_HOST
 
-    stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
-    if (sti == nullptr) {
-        fatal("cannot allocate stinfo");
-    }
-    sti->service_name = service_name;
-    sti->func = func;
-    sti->cookie = cookie;
-    sti->fd = s[1];
-
-    std::thread(service_bootstrap_func, sti).detach();
+    std::thread(service_bootstrap_func, service_name, func, android::base::unique_fd(s[1])).detach();
 
     D("service thread started, %d:%d",s[0], s[1]);
-    return s[0];
+    return android::base::unique_fd(s[0]);
 }
 
+}  // namespace
+
 int service_to_fd(const char* name, atransport* transport) {
     int ret = -1;
 
@@ -280,54 +261,60 @@
 #if !ADB_HOST
     } else if(!strncmp("dev:", name, 4)) {
         ret = unix_open(name + 4, O_RDWR | O_CLOEXEC);
-    } else if(!strncmp(name, "framebuffer:", 12)) {
-        ret = create_service_thread("fb", framebuffer_service, nullptr);
+    } else if (!strncmp(name, "framebuffer:", 12)) {
+        ret = create_service_thread("fb", framebuffer_service).release();
     } else if (!strncmp(name, "jdwp:", 5)) {
-        ret = create_jdwp_connection_fd(atoi(name+5));
-    } else if(!strncmp(name, "shell", 5)) {
+        ret = create_jdwp_connection_fd(atoi(name + 5));
+    } else if (!strncmp(name, "shell", 5)) {
         ret = ShellService(name + 5, transport);
-    } else if(!strncmp(name, "exec:", 5)) {
+    } else if (!strncmp(name, "exec:", 5)) {
         ret = StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
-    } else if(!strncmp(name, "sync:", 5)) {
-        ret = create_service_thread("sync", file_sync_service, nullptr);
-    } else if(!strncmp(name, "remount:", 8)) {
-        const char* options = name + strlen("remount:");
-        void* cookie = const_cast<void*>(reinterpret_cast<const void*>(options));
-        ret = create_service_thread("remount", remount_service, cookie);
-    } else if(!strncmp(name, "reboot:", 7)) {
-        void* arg = strdup(name + 7);
-        if (arg == NULL) return -1;
-        ret = create_service_thread("reboot", reboot_service, arg);
-        if (ret < 0) free(arg);
-    } else if(!strncmp(name, "root:", 5)) {
-        ret = create_service_thread("root", restart_root_service, nullptr);
-    } else if(!strncmp(name, "unroot:", 7)) {
-        ret = create_service_thread("unroot", restart_unroot_service, nullptr);
-    } else if(!strncmp(name, "backup:", 7)) {
-        ret = StartSubprocess(android::base::StringPrintf("/system/bin/bu backup %s",
-                                                          (name + 7)).c_str(),
-                              nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
-    } else if(!strncmp(name, "restore:", 8)) {
+    } else if (!strncmp(name, "sync:", 5)) {
+        ret = create_service_thread("sync", file_sync_service).release();
+    } else if (!strncmp(name, "remount:", 8)) {
+        std::string options(name + strlen("remount:"));
+        ret = create_service_thread("remount",
+                                    std::bind(remount_service, std::placeholders::_1, options))
+                      .release();
+    } else if (!strncmp(name, "reboot:", 7)) {
+        std::string arg(name + strlen("reboot:"));
+        ret = create_service_thread("reboot", std::bind(reboot_service, std::placeholders::_1, arg))
+                      .release();
+    } else if (!strncmp(name, "root:", 5)) {
+        ret = create_service_thread("root", restart_root_service).release();
+    } else if (!strncmp(name, "unroot:", 7)) {
+        ret = create_service_thread("unroot", restart_unroot_service).release();
+    } else if (!strncmp(name, "backup:", 7)) {
+        ret = StartSubprocess(
+                android::base::StringPrintf("/system/bin/bu backup %s", (name + 7)).c_str(),
+                nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+    } else if (!strncmp(name, "restore:", 8)) {
         ret = StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
                               SubprocessProtocol::kNone);
-    } else if(!strncmp(name, "tcpip:", 6)) {
+    } else if (!strncmp(name, "tcpip:", 6)) {
         int port;
         if (sscanf(name + 6, "%d", &port) != 1) {
             return -1;
         }
-        ret = create_service_thread("tcp", restart_tcp_service, reinterpret_cast<void*>(port));
-    } else if(!strncmp(name, "usb:", 4)) {
-        ret = create_service_thread("usb", restart_usb_service, nullptr);
+        ret = create_service_thread("tcp",
+                                    std::bind(restart_tcp_service, std::placeholders::_1, port))
+                      .release();
+    } else if (!strncmp(name, "usb:", 4)) {
+        ret = create_service_thread("usb", restart_usb_service).release();
     } else if (!strncmp(name, "reverse:", 8)) {
         ret = reverse_service(name + 8, transport);
-    } else if(!strncmp(name, "disable-verity:", 15)) {
-        ret = create_service_thread("verity-on", set_verity_enabled_state_service,
-                                    reinterpret_cast<void*>(0));
-    } else if(!strncmp(name, "enable-verity:", 15)) {
-        ret = create_service_thread("verity-off", set_verity_enabled_state_service,
-                                    reinterpret_cast<void*>(1));
+    } else if (!strncmp(name, "disable-verity:", 15)) {
+        ret = create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
+                                                           std::placeholders::_1, false))
+                      .release();
+    } else if (!strncmp(name, "enable-verity:", 15)) {
+        ret = create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
+                                                            std::placeholders::_1, true))
+                      .release();
     } else if (!strcmp(name, "reconnect")) {
-        ret = create_service_thread("reconnect", reconnect_service, transport);
+        ret = create_service_thread("reconnect",
+                                    std::bind(reconnect_service, std::placeholders::_1, transport))
+                      .release();
 #endif
     }
     if (ret >= 0) {
@@ -420,19 +407,16 @@
     }
 }
 
-static void connect_service(int fd, void* data) {
-    char* host = reinterpret_cast<char*>(data);
+static void connect_service(android::base::unique_fd fd, std::string host) {
     std::string response;
-    if (!strncmp(host, "emu:", 4)) {
-        connect_emulator(host + 4, &response);
+    if (!strncmp(host.c_str(), "emu:", 4)) {
+        connect_emulator(host.c_str() + 4, &response);
     } else {
-        connect_device(host, &response);
+        connect_device(host.c_str(), &response);
     }
-    free(host);
 
     // Send response for emulator and device
-    SendProtocolString(fd, response);
-    adb_close(fd);
+    SendProtocolString(fd.get(), response);
 }
 #endif
 
@@ -445,7 +429,7 @@
     } else if (android::base::StartsWith(name, "wait-for-")) {
         name += strlen("wait-for-");
 
-        std::unique_ptr<state_info> sinfo(new state_info);
+        std::unique_ptr<state_info> sinfo = std::make_unique<state_info>();
         if (sinfo == nullptr) {
             fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
             return nullptr;
@@ -481,17 +465,18 @@
             return nullptr;
         }
 
-        int fd = create_service_thread("wait", wait_for_state, sinfo.get());
+        int fd = create_service_thread(
+                         "wait", std::bind(wait_for_state, std::placeholders::_1, sinfo.get()))
+                         .release();
         if (fd != -1) {
             sinfo.release();
         }
         return create_local_socket(fd);
     } else if (!strncmp(name, "connect:", 8)) {
-        char* host = strdup(name + 8);
-        int fd = create_service_thread("connect", connect_service, host);
-        if (fd == -1) {
-            free(host);
-        }
+        std::string host(name + strlen("connect:"));
+        int fd = create_service_thread("connect",
+                                       std::bind(connect_service, std::placeholders::_1, host))
+                         .release();
         return create_local_socket(fd);
     }
     return nullptr;
diff --git a/adb/sysdeps/memory.h b/adb/sysdeps/memory.h
index 0e4c509..4108aff 100644
--- a/adb/sysdeps/memory.h
+++ b/adb/sysdeps/memory.h
@@ -23,6 +23,23 @@
 // We don't have C++14 on Windows yet.
 // Reimplement std::make_unique ourselves until we do.
 
+namespace internal {
+
+template <typename T>
+struct array_known_bounds;
+
+template <typename T>
+struct array_known_bounds<T[]> {
+    constexpr static bool value = false;
+};
+
+template <typename T, size_t N>
+struct array_known_bounds<T[N]> {
+    constexpr static bool value = true;
+};
+
+}  // namespace internal
+
 namespace std {
 
 template <typename T, typename... Args>
@@ -31,6 +48,18 @@
     return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
 }
 
+template <typename T>
+typename std::enable_if<std::is_array<T>::value && !internal::array_known_bounds<T>::value,
+                        std::unique_ptr<T>>::type
+make_unique(std::size_t size) {
+    return std::unique_ptr<T>(new typename std::remove_extent<T>::type[size]());
+}
+
+template <typename T, typename... Args>
+typename std::enable_if<std::is_array<T>::value && internal::array_known_bounds<T>::value,
+                        std::unique_ptr<T>>::type
+make_unique(Args&&... args) = delete;
+
 }  // namespace std
 
 #endif
diff --git a/adb/transport.cpp b/adb/transport.cpp
index f2c40d2..638940c 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -173,18 +173,18 @@
             attempt = reconnect_queue_.front();
             reconnect_queue_.pop();
             if (attempt.transport->kicked()) {
-                D("transport %s was kicked. giving up on it.", attempt.transport->serial);
+                D("transport %s was kicked. giving up on it.", attempt.transport->serial.c_str());
                 remove_transport(attempt.transport);
                 continue;
             }
         }
-        D("attempting to reconnect %s", attempt.transport->serial);
+        D("attempting to reconnect %s", attempt.transport->serial.c_str());
 
         if (!attempt.transport->Reconnect()) {
-            D("attempting to reconnect %s failed.", attempt.transport->serial);
+            D("attempting to reconnect %s failed.", attempt.transport->serial.c_str());
             if (attempt.attempts_left == 0) {
                 D("transport %s exceeded the number of retry attempts. giving up on it.",
-                  attempt.transport->serial);
+                  attempt.transport->serial.c_str());
                 remove_transport(attempt.transport);
                 continue;
             }
@@ -197,7 +197,7 @@
             continue;
         }
 
-        D("reconnection to %s succeeded.", attempt.transport->serial);
+        D("reconnection to %s succeeded.", attempt.transport->serial.c_str());
         register_transport(attempt.transport);
     }
 }
@@ -402,14 +402,14 @@
         p->msg.data_check = calculate_apacket_checksum(p);
     }
 
-    VLOG(TRANSPORT) << dump_packet(t->serial, "to remote", p);
+    VLOG(TRANSPORT) << dump_packet(t->serial.c_str(), "to remote", p);
 
     if (t == nullptr) {
         fatal("Transport is null");
     }
 
     if (t->Write(p) != 0) {
-        D("%s: failed to enqueue packet, closing transport", t->serial);
+        D("%s: failed to enqueue packet, closing transport", t->serial.c_str());
         t->Kick();
     }
 }
@@ -619,19 +619,13 @@
     t = m.transport;
 
     if (m.action == 0) {
-        D("transport: %s deleting", t->serial);
+        D("transport: %s deleting", t->serial.c_str());
 
         {
             std::lock_guard<std::recursive_mutex> lock(transport_lock);
             transport_list.remove(t);
         }
 
-        if (t->product) free(t->product);
-        if (t->serial) free(t->serial);
-        if (t->model) free(t->model);
-        if (t->device) free(t->device);
-        if (t->devpath) free(t->devpath);
-
         delete t;
 
         update_transports();
@@ -646,11 +640,11 @@
         t->connection()->SetTransportName(t->serial_name());
         t->connection()->SetReadCallback([t](Connection*, std::unique_ptr<apacket> p) {
             if (!check_header(p.get(), t)) {
-                D("%s: remote read: bad header", t->serial);
+                D("%s: remote read: bad header", t->serial.c_str());
                 return false;
             }
 
-            VLOG(TRANSPORT) << dump_packet(t->serial, "from remote", p.get());
+            VLOG(TRANSPORT) << dump_packet(t->serial.c_str(), "from remote", p.get());
             apacket* packet = p.release();
 
             // TODO: Does this need to run on the main thread?
@@ -658,7 +652,7 @@
             return true;
         });
         t->connection()->SetErrorCallback([t](Connection*, const std::string& error) {
-            D("%s: connection terminated: %s", t->serial, error.c_str());
+            D("%s: connection terminated: %s", t->serial.c_str(), error.c_str());
             fdevent_run_on_main_thread([t]() {
                 handle_offline(t);
                 transport_unref(t);
@@ -717,7 +711,7 @@
     tmsg m;
     m.transport = transport;
     m.action = 1;
-    D("transport: %s registered", transport->serial);
+    D("transport: %s registered", transport->serial.c_str());
     if (transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
@@ -727,7 +721,7 @@
     tmsg m;
     m.transport = transport;
     m.action = 0;
-    D("transport: %s removed", transport->serial);
+    D("transport: %s removed", transport->serial.c_str());
     if (transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
@@ -743,38 +737,38 @@
     if (t->ref_count == 0) {
         t->connection()->Stop();
         if (t->IsTcpDevice() && !t->kicked()) {
-            D("transport: %s unref (attempting reconnection) %d", t->serial, t->kicked());
+            D("transport: %s unref (attempting reconnection) %d", t->serial.c_str(), t->kicked());
             reconnect_handler.TrackTransport(t);
         } else {
-            D("transport: %s unref (kicking and closing)", t->serial);
+            D("transport: %s unref (kicking and closing)", t->serial.c_str());
             remove_transport(t);
         }
     } else {
-        D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
+        D("transport: %s unref (count=%zu)", t->serial.c_str(), t->ref_count);
     }
 }
 
-static int qual_match(const char* to_test, const char* prefix, const char* qual,
+static int qual_match(const std::string& to_test, const char* prefix, const std::string& qual,
                       bool sanitize_qual) {
-    if (!to_test || !*to_test) /* Return true if both the qual and to_test are null strings. */
-        return !qual || !*qual;
+    if (to_test.empty()) /* Return true if both the qual and to_test are empty strings. */
+        return qual.empty();
 
-    if (!qual) return 0;
+    if (qual.empty()) return 0;
 
+    const char* ptr = to_test.c_str();
     if (prefix) {
         while (*prefix) {
-            if (*prefix++ != *to_test++) return 0;
+            if (*prefix++ != *ptr++) return 0;
         }
     }
 
-    while (*qual) {
-        char ch = *qual++;
+    for (char ch : qual) {
         if (sanitize_qual && !isalnum(ch)) ch = '_';
-        if (ch != *to_test++) return 0;
+        if (ch != *ptr++) return 0;
     }
 
-    /* Everything matched so far.  Return true if *to_test is a NUL. */
-    return !*to_test;
+    /* Everything matched so far.  Return true if *ptr is a NUL. */
+    return !*ptr;
 }
 
 atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id,
@@ -921,7 +915,7 @@
 
 void atransport::Kick() {
     if (!kicked_.exchange(true)) {
-        D("kicking transport %p %s", this, this->serial);
+        D("kicking transport %p %s", this, this->serial.c_str());
         this->connection()->Stop();
     }
 }
@@ -1040,7 +1034,7 @@
 }
 
 bool atransport::MatchesTarget(const std::string& target) const {
-    if (serial) {
+    if (!serial.empty()) {
         if (target == serial) {
             return true;
         } else if (type == kTransportLocal) {
@@ -1069,10 +1063,9 @@
         }
     }
 
-    return (devpath && target == devpath) ||
-           qual_match(target.c_str(), "product:", product, false) ||
-           qual_match(target.c_str(), "model:", model, true) ||
-           qual_match(target.c_str(), "device:", device, false);
+    return (target == devpath) || qual_match(target, "product:", product, false) ||
+           qual_match(target, "model:", model, true) ||
+           qual_match(target, "device:", device, false);
 }
 
 void atransport::SetConnectionEstablished(bool success) {
@@ -1093,9 +1086,9 @@
     return str;
 }
 
-static void append_transport_info(std::string* result, const char* key, const char* value,
+static void append_transport_info(std::string* result, const char* key, const std::string& value,
                                   bool alphanumeric) {
-    if (value == nullptr || *value == '\0') {
+    if (value.empty()) {
         return;
     }
 
@@ -1105,8 +1098,8 @@
 }
 
 static void append_transport(const atransport* t, std::string* result, bool long_listing) {
-    const char* serial = t->serial;
-    if (!serial || !serial[0]) {
+    std::string serial = t->serial;
+    if (serial.empty()) {
         serial = "(no serial number)";
     }
 
@@ -1115,7 +1108,8 @@
         *result += '\t';
         *result += t->connection_state_name();
     } else {
-        android::base::StringAppendF(result, "%-22s %s", serial, t->connection_state_name().c_str());
+        android::base::StringAppendF(result, "%-22s %s", serial.c_str(),
+                                     t->connection_state_name().c_str());
 
         append_transport_info(result, "", t->devpath, false);
         append_transport_info(result, "product:", t->product, false);
@@ -1138,7 +1132,7 @@
         if (x->type != y->type) {
             return x->type < y->type;
         }
-        return strcmp(x->serial, y->serial) < 0;
+        return x->serial < y->serial;
     });
 
     std::string result;
@@ -1181,7 +1175,7 @@
 
     std::unique_lock<std::recursive_mutex> lock(transport_lock);
     for (const auto& transport : pending_list) {
-        if (transport->serial && strcmp(serial, transport->serial) == 0) {
+        if (strcmp(serial, transport->serial.c_str()) == 0) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
                             << " is already in pending_list and fails to register";
             delete t;
@@ -1190,7 +1184,7 @@
     }
 
     for (const auto& transport : transport_list) {
-        if (transport->serial && strcmp(serial, transport->serial) == 0) {
+        if (strcmp(serial, transport->serial.c_str()) == 0) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
                             << " is already in transport_list and fails to register";
             delete t;
@@ -1199,7 +1193,7 @@
     }
 
     pending_list.push_front(t);
-    t->serial = strdup(serial);
+    t->serial = serial;
 
     lock.unlock();
 
@@ -1220,7 +1214,7 @@
 
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (auto& t : transport_list) {
-        if (t->serial && strcmp(serial, t->serial) == 0) {
+        if (strcmp(serial, t->serial.c_str()) == 0) {
             result = t;
             break;
         }
@@ -1251,11 +1245,11 @@
     D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb, serial ? serial : "");
     init_usb_transport(t, usb);
     if (serial) {
-        t->serial = strdup(serial);
+        t->serial = serial;
     }
 
     if (devpath) {
-        t->devpath = strdup(devpath);
+        t->devpath = devpath;
     }
 
     {
diff --git a/adb/transport.h b/adb/transport.h
index cb20615..e9c9d37 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -238,11 +238,11 @@
     TransportType type = kTransportAny;
 
     // Used to identify transports for clients.
-    char* serial = nullptr;
-    char* product = nullptr;
-    char* model = nullptr;
-    char* device = nullptr;
-    char* devpath = nullptr;
+    std::string serial;
+    std::string product;
+    std::string model;
+    std::string device;
+    std::string devpath;
 
     bool IsTcpDevice() const { return type == kTransportLocal; }
 
@@ -253,7 +253,7 @@
     char token[TOKEN_SIZE] = {};
     size_t failed_auth_attempts = 0;
 
-    std::string serial_name() const { return serial ? serial : "<unknown>"; }
+    std::string serial_name() const { return !serial.empty() ? serial : "<unknown>"; }
     std::string connection_state_name() const;
 
     void update_version(int version, size_t payload);
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index d987d4f..8c628d8 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -86,9 +86,9 @@
     ASSERT_EQ(0U, t.features().size());
     ASSERT_EQ(kCsHost, t.GetConnectionState());
 
-    ASSERT_EQ(nullptr, t.product);
-    ASSERT_EQ(nullptr, t.model);
-    ASSERT_EQ(nullptr, t.device);
+    ASSERT_EQ(std::string(), t.product);
+    ASSERT_EQ(std::string(), t.model);
+    ASSERT_EQ(std::string(), t.device);
 }
 
 TEST(transport, parse_banner_product_features) {
diff --git a/adb/types.h b/adb/types.h
index c6b3f07..a3e5d48 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 #include <deque>
+#include <memory>
 #include <type_traits>
 #include <utility>
 #include <vector>
@@ -37,7 +38,7 @@
 
     template <typename Iterator>
     Block(Iterator begin, Iterator end) : Block(end - begin) {
-        std::copy(begin, end, data_);
+        std::copy(begin, end, data_.get());
     }
 
     Block(const Block& copy) = delete;
@@ -73,11 +74,11 @@
     void assign(InputIt begin, InputIt end) {
         clear();
         allocate(end - begin);
-        std::copy(begin, end, data_);
+        std::copy(begin, end, data_.get());
     }
 
     void clear() {
-        free(data_);
+        data_.reset();
         capacity_ = 0;
         size_ = 0;
     }
@@ -86,11 +87,11 @@
     size_t size() const { return size_; }
     bool empty() const { return size() == 0; }
 
-    char* data() { return data_; }
-    const char* data() const { return data_; }
+    char* data() { return data_.get(); }
+    const char* data() const { return data_.get(); }
 
-    char* begin() { return data_; }
-    const char* begin() const { return data_; }
+    char* begin() { return data_.get(); }
+    const char* begin() const { return data_.get(); }
 
     char* end() { return data() + size_; }
     const char* end() const { return data() + size_; }
@@ -108,13 +109,13 @@
         CHECK_EQ(0ULL, capacity_);
         CHECK_EQ(0ULL, size_);
         if (size != 0) {
-            data_ = static_cast<char*>(malloc(size));
+            data_ = std::make_unique<char[]>(size);
             capacity_ = size;
             size_ = size;
         }
     }
 
-    char* data_ = nullptr;
+    std::unique_ptr<char[]> data_;
     size_t capacity_ = 0;
     size_t size_ = 0;
 };
diff --git a/base/Android.bp b/base/Android.bp
index 3d80d97..46a0233 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -135,6 +135,7 @@
         "strings_test.cpp",
         "test_main.cpp",
         "test_utils_test.cpp",
+        "unique_fd_test.cpp",
     ],
     target: {
         android: {
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
index fc68d56..bb54c99 100644
--- a/base/include/android-base/parseint.h
+++ b/base/include/android-base/parseint.h
@@ -47,7 +47,9 @@
   if (max < result) {
     return false;
   }
-  *out = static_cast<T>(result);
+  if (out != nullptr) {
+    *out = static_cast<T>(result);
+  }
   return true;
 }
 
@@ -87,7 +89,9 @@
   if (result < min || max < result) {
     return false;
   }
-  *out = static_cast<T>(result);
+  if (out != nullptr) {
+    *out = static_cast<T>(result);
+  }
   return true;
 }
 
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index c733081..c6936f1 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -42,10 +42,35 @@
 //
 // unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help
 // you find this class if you're searching for one of those names.
+
+#if defined(__BIONIC__)
+#include <android/fdsan.h>
+#endif
+
 namespace android {
 namespace base {
 
 struct DefaultCloser {
+#if defined(__BIONIC__)
+  static void Tag(int fd, void* old_addr, void* new_addr) {
+    if (android_fdsan_exchange_owner_tag) {
+      uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
+                                                        reinterpret_cast<uint64_t>(old_addr));
+      uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
+                                                        reinterpret_cast<uint64_t>(new_addr));
+      android_fdsan_exchange_owner_tag(fd, old_tag, new_tag);
+    }
+  }
+  static void Close(int fd, void* addr) {
+    if (android_fdsan_close_with_tag) {
+      uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
+                                                    reinterpret_cast<uint64_t>(addr));
+      android_fdsan_close_with_tag(fd, tag);
+    } else {
+      close(fd);
+    }
+  }
+#else
   static void Close(int fd) {
     // Even if close(2) fails with EINTR, the fd will have been closed.
     // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone
@@ -53,40 +78,75 @@
     // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
     ::close(fd);
   }
+#endif
 };
 
 template <typename Closer>
 class unique_fd_impl final {
  public:
-  unique_fd_impl() : value_(-1) {}
+  unique_fd_impl() {}
 
-  explicit unique_fd_impl(int value) : value_(value) {}
+  explicit unique_fd_impl(int fd) { reset(fd); }
   ~unique_fd_impl() { reset(); }
 
-  unique_fd_impl(unique_fd_impl&& other) : value_(other.release()) {}
+  unique_fd_impl(unique_fd_impl&& other) { reset(other.release()); }
   unique_fd_impl& operator=(unique_fd_impl&& s) {
-    reset(s.release());
+    int fd = s.fd_;
+    s.fd_ = -1;
+    reset(fd, &s);
     return *this;
   }
 
-  void reset(int new_value = -1) {
-    if (value_ != -1) {
-      Closer::Close(value_);
-    }
-    value_ = new_value;
-  }
+  void reset(int new_value = -1) { reset(new_value, nullptr); }
 
-  int get() const { return value_; }
+  int get() const { return fd_; }
   operator int() const { return get(); }
 
   int release() __attribute__((warn_unused_result)) {
-    int ret = value_;
-    value_ = -1;
+    tag(fd_, this, nullptr);
+    int ret = fd_;
+    fd_ = -1;
     return ret;
   }
 
  private:
-  int value_;
+  void reset(int new_value, void* previous_tag) {
+    if (fd_ != -1) {
+      close(fd_, this);
+    }
+
+    fd_ = new_value;
+    if (new_value != -1) {
+      tag(new_value, previous_tag, this);
+    }
+  }
+
+  int fd_ = -1;
+
+  // Template magic to use Closer::Tag if available, and do nothing if not.
+  // If Closer::Tag exists, this implementation is preferred, because int is a better match.
+  // If not, this implementation is SFINAEd away, and the no-op below is the only one that exists.
+  template <typename T = Closer>
+  static auto tag(int fd, void* old_tag, void* new_tag)
+      -> decltype(T::Tag(fd, old_tag, new_tag), void()) {
+    T::Tag(fd, old_tag, new_tag);
+  }
+
+  template <typename T = Closer>
+  static void tag(long, void*, void*) {
+    // No-op.
+  }
+
+  // Same as above, to select between Closer::Close(int) and Closer::Close(int, void*).
+  template <typename T = Closer>
+  static auto close(int fd, void* tag_value) -> decltype(T::Close(fd, tag_value), void()) {
+    T::Close(fd, tag_value);
+  }
+
+  template <typename T = Closer>
+  static auto close(int fd, void*) -> decltype(T::Close(fd), void()) {
+    T::Close(fd);
+  }
 
   unique_fd_impl(const unique_fd_impl&);
   void operator=(const unique_fd_impl&);
diff --git a/base/parseint_test.cpp b/base/parseint_test.cpp
index fb1c339..8f9ed77 100644
--- a/base/parseint_test.cpp
+++ b/base/parseint_test.cpp
@@ -36,6 +36,10 @@
   ASSERT_EQ(12, i);
   ASSERT_FALSE(android::base::ParseInt("-12", &i, 0, 15));
   ASSERT_FALSE(android::base::ParseInt("16", &i, 0, 15));
+
+  ASSERT_FALSE(android::base::ParseInt<int>("x", nullptr));
+  ASSERT_FALSE(android::base::ParseInt<int>("123x", nullptr));
+  ASSERT_TRUE(android::base::ParseInt<int>("1234", nullptr));
 }
 
 TEST(parseint, unsigned_smoke) {
@@ -55,6 +59,10 @@
   ASSERT_EQ(12u, i);
   ASSERT_FALSE(android::base::ParseUint("-12", &i, 15u));
   ASSERT_FALSE(android::base::ParseUint("16", &i, 15u));
+
+  ASSERT_FALSE(android::base::ParseUint<unsigned short>("x", nullptr));
+  ASSERT_FALSE(android::base::ParseUint<unsigned short>("123x", nullptr));
+  ASSERT_TRUE(android::base::ParseUint<unsigned short>("1234", nullptr));
 }
 
 TEST(parseint, no_implicit_octal) {
diff --git a/base/unique_fd_test.cpp b/base/unique_fd_test.cpp
new file mode 100644
index 0000000..3fdf12a
--- /dev/null
+++ b/base/unique_fd_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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/unique_fd.h"
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+using android::base::unique_fd;
+
+TEST(unique_fd, unowned_close) {
+#if defined(__BIONIC__)
+  unique_fd fd(open("/dev/null", O_RDONLY));
+  EXPECT_DEATH(close(fd.get()), "incorrect tag");
+#endif
+}
+
+TEST(unique_fd, untag_on_release) {
+  unique_fd fd(open("/dev/null", O_RDONLY));
+  close(fd.release());
+}
+
+TEST(unique_fd, move) {
+  unique_fd fd(open("/dev/null", O_RDONLY));
+  unique_fd fd_moved = std::move(fd);
+  ASSERT_EQ(-1, fd.get());
+  ASSERT_GT(fd_moved.get(), -1);
+}
+
+TEST(unique_fd, unowned_close_after_move) {
+#if defined(__BIONIC__)
+  unique_fd fd(open("/dev/null", O_RDONLY));
+  unique_fd fd_moved = std::move(fd);
+  ASSERT_EQ(-1, fd.get());
+  ASSERT_GT(fd_moved.get(), -1);
+  EXPECT_DEATH(close(fd_moved.get()), "incorrect tag");
+#endif
+}
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
new file mode 100644
index 0000000..2b4a954
--- /dev/null
+++ b/fastboot/Android.bp
@@ -0,0 +1,56 @@
+cc_library_host_static {
+    name: "libfastboot2",
+
+    //host_supported: true,
+
+    compile_multilib: "first",
+    srcs: [
+      "bootimg_utils.cpp",
+      "fs.cpp",
+      "socket.cpp",
+      "tcp.cpp",
+      "udp.cpp",
+      "util.cpp",
+      "fastboot_driver.cpp",
+    ],
+
+    static_libs: [
+      "libziparchive",
+      "libsparse",
+      "libutils",
+      "liblog",
+      "libz",
+      "libdiagnose_usb",
+      "libbase",
+      "libcutils",
+      "libgtest",
+      "libgtest_main",
+      "libbase",
+      "libadb_host"
+    ],
+
+    header_libs: [
+      "bootimg_headers"
+    ],
+
+    export_header_lib_headers: [
+      "bootimg_headers"
+    ],
+
+
+    target: {
+      linux: {
+        srcs: ["usb_linux.cpp"],
+      },
+    },
+
+    cflags: [
+      "-Wall",
+      "-Wextra",
+      "-Werror",
+      "-Wunreachable-code",
+    ],
+
+    export_include_dirs: ["."],
+
+}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 983e195..60c338c 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -50,12 +50,12 @@
     bootimg_utils.cpp \
     engine.cpp \
     fastboot.cpp \
-    fs.cpp\
-    protocol.cpp \
+    fs.cpp \
     socket.cpp \
     tcp.cpp \
     udp.cpp \
     util.cpp \
+    fastboot_driver.cpp \
 
 LOCAL_SRC_FILES_darwin := usb_osx.cpp
 LOCAL_SRC_FILES_linux := usb_linux.cpp
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
index 2d12d50..2088ae3 100644
--- a/fastboot/OWNERS
+++ b/fastboot/OWNERS
@@ -1,3 +1,4 @@
 dpursell@google.com
 enh@google.com
 jmgao@google.com
+tomcherry@google.com
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index c9814e4..1152007 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -28,7 +28,7 @@
 
 #include "bootimg_utils.h"
 
-#include "fastboot.h"
+#include "util.h"
 
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 1087573..63ee2af 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -25,8 +25,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-
-#include "fastboot.h"
+#include "engine.h"
 
 #include <errno.h>
 #include <stdarg.h>
@@ -78,17 +77,20 @@
 };
 
 static std::vector<std::unique_ptr<Action>> action_list;
+static fastboot::FastBootDriver* fb = nullptr;
 
-bool fb_getvar(Transport* transport, const std::string& key, std::string* value) {
-    std::string cmd = FB_CMD_GETVAR ":" + key;
+void fb_init(fastboot::FastBootDriver& fbi) {
+    fb = &fbi;
+    auto cb = [](std::string& info) { fprintf(stderr, "(bootloader) %s\n", info.c_str()); };
+    fb->SetInfoCallback(cb);
+}
 
-    char buf[FB_RESPONSE_SZ + 1];
-    memset(buf, 0, sizeof(buf));
-    if (fb_command_response(transport, cmd, buf)) {
-        return false;
-    }
-    *value = buf;
-    return true;
+const std::string fb_get_error() {
+    return fb->Error();
+}
+
+bool fb_getvar(const std::string& key, std::string* value) {
+    return !fb->GetVar(key, value);
 }
 
 static int cb_default(Action& a, int status, const char* resp) {
@@ -310,7 +312,7 @@
     queue_action(OP_WAIT_FOR_DISCONNECT, "");
 }
 
-int64_t fb_execute_queue(Transport* transport) {
+int64_t fb_execute_queue() {
     int64_t status = 0;
     for (auto& a : action_list) {
         a->start = now();
@@ -319,33 +321,34 @@
             verbose("\n");
         }
         if (a->op == OP_DOWNLOAD) {
-            status = fb_download_data(transport, a->data, a->size);
+            char* cbuf = static_cast<char*>(a->data);
+            status = fb->Download(cbuf, a->size);
             status = a->func(*a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_DOWNLOAD_FD) {
-            status = fb_download_data_fd(transport, a->fd, a->size);
+            status = fb->Download(a->fd, a->size);
             status = a->func(*a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_COMMAND) {
-            status = fb_command(transport, a->cmd);
+            status = fb->RawCommand(a->cmd);
             status = a->func(*a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_QUERY) {
-            char resp[FB_RESPONSE_SZ + 1] = {};
-            status = fb_command_response(transport, a->cmd, resp);
-            status = a->func(*a, status, status ? fb_get_error().c_str() : resp);
+            std::string resp;
+            status = fb->RawCommand(a->cmd, &resp);
+            status = a->func(*a, status, status ? fb_get_error().c_str() : resp.c_str());
             if (status) break;
         } else if (a->op == OP_NOTICE) {
             // We already showed the notice because it's in `Action::msg`.
             fprintf(stderr, "\n");
         } else if (a->op == OP_DOWNLOAD_SPARSE) {
-            status = fb_download_data_sparse(transport, reinterpret_cast<sparse_file*>(a->data));
+            status = fb->Download(reinterpret_cast<sparse_file*>(a->data));
             status = a->func(*a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
-            transport->WaitForDisconnect();
+            fb->WaitForDisconnect();
         } else if (a->op == OP_UPLOAD) {
-            status = fb_upload_data(transport, reinterpret_cast<char*>(a->data));
+            status = fb->Upload(reinterpret_cast<const char*>(a->data));
             status = a->func(*a, status, status ? fb_get_error().c_str() : "");
         } else {
             die("unknown action: %d", a->op);
diff --git a/fastboot/fastboot.h b/fastboot/engine.h
similarity index 74%
rename from fastboot/fastboot.h
rename to fastboot/engine.h
index a93c0ac..74aaa6a 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/engine.h
@@ -34,23 +34,24 @@
 #include <string>
 
 #include <bootimg.h>
+#include "fastboot_driver.h"
+#include "util.h"
 
 #include "constants.h"
 
 class Transport;
 struct sparse_file;
 
-/* protocol.c - fastboot protocol */
-int fb_command(Transport* transport, const std::string& cmd);
-int fb_command_response(Transport* transport, const std::string& cmd, char* response);
-int64_t fb_download_data(Transport* transport, const void* data, uint32_t size);
-int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size);
-int fb_download_data_sparse(Transport* transport, struct sparse_file* s);
-int64_t fb_upload_data(Transport* transport, const char* outfile);
 const std::string fb_get_error();
 
+//#define FB_COMMAND_SZ (fastboot::FB_COMMAND_SZ)
+//#define FB_RESPONSE_SZ (fastboot::FB_RESPONSE_SZ)
+
 /* engine.c - high level command queue engine */
-bool fb_getvar(Transport* transport, const std::string& key, std::string* value);
+
+void fb_init(fastboot::FastBootDriver& fbi);
+
+bool fb_getvar(const std::string& key, std::string* value);
 void fb_queue_flash(const std::string& partition, void* data, uint32_t sz);
 void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz);
 void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
@@ -68,24 +69,13 @@
 void fb_queue_upload(const std::string& outfile);
 void fb_queue_notice(const std::string& notice);
 void fb_queue_wait_for_disconnect(void);
-int64_t fb_execute_queue(Transport* transport);
+int64_t fb_execute_queue();
 void fb_set_active(const std::string& slot);
 
-/* util stuff */
-double now();
-char* xstrdup(const char*);
-void set_verbose();
-
-// These printf-like functions are implemented in terms of vsnprintf, so they
-// use the same attribute for compile-time format string checking.
-void die(const char* fmt, ...) __attribute__((__noreturn__))
-__attribute__((__format__(__printf__, 1, 2)));
-void verbose(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
-
 /* Current product */
 extern char cur_product[FB_RESPONSE_SZ + 1];
 
-class FastBoot {
+class FastBootTool {
   public:
     int Main(int argc, char* argv[]);
 
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 5aa87d9..263ea17 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -60,7 +60,7 @@
 
 #include "bootimg_utils.h"
 #include "diagnose_usb.h"
-#include "fastboot.h"
+#include "engine.h"
 #include "fs.h"
 #include "tcp.h"
 #include "transport.h"
@@ -592,7 +592,7 @@
 }
 
 #define MAX_OPTIONS 32
-static void check_requirement(Transport* transport, char* line) {
+static void check_requirement(char* line) {
     char *val[MAX_OPTIONS];
     unsigned count;
     char *x;
@@ -635,7 +635,7 @@
     if (!strcmp(name, "partition-exists")) {
         const char* partition_name = val[0];
         std::string has_slot;
-        if (!fb_getvar(transport, std::string("has-slot:") + partition_name, &has_slot) ||
+        if (!fb_getvar(std::string("has-slot:") + partition_name, &has_slot) ||
             (has_slot != "yes" && has_slot != "no")) {
             die("device doesn't have required partition %s!", partition_name);
         }
@@ -674,18 +674,18 @@
     fb_queue_require(product, var, invert, count, out);
 }
 
-static void check_requirements(Transport* transport, char* data, int64_t sz) {
+static void check_requirements(char* data, int64_t sz) {
     char* s = data;
     while (sz-- > 0) {
         if (*s == '\n') {
             *s++ = 0;
-            check_requirement(transport, data);
+            check_requirement(data);
             data = s;
         } else {
             s++;
         }
     }
-    if (fb_execute_queue(transport)) die("requirements not met!");
+    if (fb_execute_queue()) die("requirements not met!");
 }
 
 static void queue_info_dump() {
@@ -716,9 +716,9 @@
     return out_s;
 }
 
-static int64_t get_target_sparse_limit(Transport* transport) {
+static int64_t get_target_sparse_limit() {
     std::string max_download_size;
-    if (!fb_getvar(transport, "max-download-size", &max_download_size) ||
+    if (!fb_getvar("max-download-size", &max_download_size) ||
         max_download_size.empty()) {
         verbose("target didn't report max-download-size");
         return 0;
@@ -736,13 +736,13 @@
     return limit;
 }
 
-static int64_t get_sparse_limit(Transport* transport, int64_t size) {
+static int64_t get_sparse_limit(int64_t size) {
     int64_t limit = sparse_limit;
     if (limit == 0) {
         // Unlimited, so see what the target device's limit is.
         // TODO: shouldn't we apply this limit even if you've used -S?
         if (target_sparse_limit == -1) {
-            target_sparse_limit = get_target_sparse_limit(transport);
+            target_sparse_limit = get_target_sparse_limit();
         }
         if (target_sparse_limit > 0) {
             limit = target_sparse_limit;
@@ -758,14 +758,14 @@
     return 0;
 }
 
-static bool load_buf_fd(Transport* transport, int fd, struct fastboot_buffer* buf) {
+static bool load_buf_fd(int fd, struct fastboot_buffer* buf) {
     int64_t sz = get_file_size(fd);
     if (sz == -1) {
         return false;
     }
 
     lseek64(fd, 0, SEEK_SET);
-    int64_t limit = get_sparse_limit(transport, sz);
+    int64_t limit = get_sparse_limit(sz);
     if (limit) {
         sparse_file** s = load_sparse_files(fd, limit);
         if (s == nullptr) {
@@ -783,7 +783,7 @@
     return true;
 }
 
-static bool load_buf(Transport* transport, const char* fname, struct fastboot_buffer* buf) {
+static bool load_buf(const char* fname, struct fastboot_buffer* buf) {
     unique_fd fd(TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_BINARY)));
 
     if (fd == -1) {
@@ -799,7 +799,7 @@
         return false;
     }
 
-    return load_buf_fd(transport, fd.release(), buf);
+    return load_buf_fd(fd.release(), buf);
 }
 
 static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf) {
@@ -871,23 +871,23 @@
     }
 }
 
-static std::string get_current_slot(Transport* transport) {
+static std::string get_current_slot() {
     std::string current_slot;
-    if (!fb_getvar(transport, "current-slot", &current_slot)) return "";
+    if (!fb_getvar("current-slot", &current_slot)) return "";
     return current_slot;
 }
 
-static int get_slot_count(Transport* transport) {
+static int get_slot_count() {
     std::string var;
     int count = 0;
-    if (!fb_getvar(transport, "slot-count", &var) || !android::base::ParseInt(var, &count)) {
+    if (!fb_getvar("slot-count", &var) || !android::base::ParseInt(var, &count)) {
         return 0;
     }
     return count;
 }
 
-static bool supports_AB(Transport* transport) {
-  return get_slot_count(transport) >= 2;
+static bool supports_AB() {
+  return get_slot_count() >= 2;
 }
 
 // Given a current slot, this returns what the 'other' slot is.
@@ -898,25 +898,25 @@
     return std::string(1, next);
 }
 
-static std::string get_other_slot(Transport* transport, const std::string& current_slot) {
-    return get_other_slot(current_slot, get_slot_count(transport));
+static std::string get_other_slot(const std::string& current_slot) {
+    return get_other_slot(current_slot, get_slot_count());
 }
 
-static std::string get_other_slot(Transport* transport, int count) {
-    return get_other_slot(get_current_slot(transport), count);
+static std::string get_other_slot(int count) {
+    return get_other_slot(get_current_slot(), count);
 }
 
-static std::string get_other_slot(Transport* transport) {
-    return get_other_slot(get_current_slot(transport), get_slot_count(transport));
+static std::string get_other_slot() {
+    return get_other_slot(get_current_slot(), get_slot_count());
 }
 
-static std::string verify_slot(Transport* transport, const std::string& slot_name, bool allow_all) {
+static std::string verify_slot(const std::string& slot_name, bool allow_all) {
     std::string slot = slot_name;
     if (slot == "all") {
         if (allow_all) {
             return "all";
         } else {
-            int count = get_slot_count(transport);
+            int count = get_slot_count();
             if (count > 0) {
                 return "a";
             } else {
@@ -925,11 +925,11 @@
         }
     }
 
-    int count = get_slot_count(transport);
+    int count = get_slot_count();
     if (count == 0) die("Device does not support slots");
 
     if (slot == "other") {
-        std::string other = get_other_slot(transport, count);
+        std::string other = get_other_slot( count);
         if (other == "") {
            die("No known slots");
         }
@@ -946,22 +946,22 @@
     exit(1);
 }
 
-static std::string verify_slot(Transport* transport, const std::string& slot) {
-   return verify_slot(transport, slot, true);
+static std::string verify_slot(const std::string& slot) {
+   return verify_slot(slot, true);
 }
 
-static void do_for_partition(Transport* transport, const std::string& part, const std::string& slot,
+static void do_for_partition(const std::string& part, const std::string& slot,
                              const std::function<void(const std::string&)>& func, bool force_slot) {
     std::string has_slot;
     std::string current_slot;
 
-    if (!fb_getvar(transport, "has-slot:" + part, &has_slot)) {
+    if (!fb_getvar("has-slot:" + part, &has_slot)) {
         /* If has-slot is not supported, the answer is no. */
         has_slot = "no";
     }
     if (has_slot == "yes") {
         if (slot == "") {
-            current_slot = get_current_slot(transport);
+            current_slot = get_current_slot();
             if (current_slot == "") {
                 die("Failed to identify current slot");
             }
@@ -983,30 +983,30 @@
  * partition names. If force_slot is true, it will fail if a slot is specified, and the given
  * partition does not support slots.
  */
-static void do_for_partitions(Transport* transport, const std::string& part, const std::string& slot,
+static void do_for_partitions(const std::string& part, const std::string& slot,
                               const std::function<void(const std::string&)>& func, bool force_slot) {
     std::string has_slot;
 
     if (slot == "all") {
-        if (!fb_getvar(transport, "has-slot:" + part, &has_slot)) {
+        if (!fb_getvar("has-slot:" + part, &has_slot)) {
             die("Could not check if partition %s has slot %s", part.c_str(), slot.c_str());
         }
         if (has_slot == "yes") {
-            for (int i=0; i < get_slot_count(transport); i++) {
-                do_for_partition(transport, part, std::string(1, (char)(i + 'a')), func, force_slot);
+            for (int i=0; i < get_slot_count(); i++) {
+                do_for_partition(part, std::string(1, (char)(i + 'a')), func, force_slot);
             }
         } else {
-            do_for_partition(transport, part, "", func, force_slot);
+            do_for_partition(part, "", func, force_slot);
         }
     } else {
-        do_for_partition(transport, part, slot, func, force_slot);
+        do_for_partition(part, slot, func, force_slot);
     }
 }
 
-static void do_flash(Transport* transport, const char* pname, const char* fname) {
+static void do_flash(const char* pname, const char* fname) {
     struct fastboot_buffer buf;
 
-    if (!load_buf(transport, fname, &buf)) {
+    if (!load_buf(fname, &buf)) {
         die("cannot load '%s': %s", fname, strerror(errno));
     }
     flash_buf(pname, &buf);
@@ -1022,20 +1022,20 @@
 
 // Sets slot_override as the active slot. If slot_override is blank,
 // set current slot as active instead. This clears slot-unbootable.
-static void set_active(Transport* transport, const std::string& slot_override) {
-    if (!supports_AB(transport)) return;
+static void set_active(const std::string& slot_override) {
+    if (!supports_AB()) return;
 
     if (slot_override != "") {
         fb_set_active(slot_override);
     } else {
-        std::string current_slot = get_current_slot(transport);
+        std::string current_slot = get_current_slot();
         if (current_slot != "") {
             fb_set_active(current_slot);
         }
     }
 }
 
-static void do_update(Transport* transport, const char* filename, const std::string& slot_override, bool skip_secondary) {
+static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
     queue_info_dump();
 
     fb_queue_query_save("product", cur_product, sizeof(cur_product));
@@ -1052,17 +1052,17 @@
         die("update package '%s' has no android-info.txt", filename);
     }
 
-    check_requirements(transport, reinterpret_cast<char*>(data), sz);
+    check_requirements(reinterpret_cast<char*>(data), sz);
 
     std::string secondary;
     if (!skip_secondary) {
         if (slot_override != "") {
-            secondary = get_other_slot(transport, slot_override);
+            secondary = get_other_slot(slot_override);
         } else {
-            secondary = get_other_slot(transport);
+            secondary = get_other_slot();
         }
         if (secondary == "") {
-            if (supports_AB(transport)) {
+            if (supports_AB()) {
                 fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
             }
             skip_secondary = true;
@@ -1087,7 +1087,7 @@
         }
 
         fastboot_buffer buf;
-        if (!load_buf_fd(transport, fd, &buf)) {
+        if (!load_buf_fd(fd, &buf)) {
             die("cannot load %s from flash: %s", images[i].img_name, strerror(errno));
         }
 
@@ -1099,13 +1099,13 @@
              * program exits.
              */
         };
-        do_for_partitions(transport, images[i].part_name, slot, update, false);
+        do_for_partitions(images[i].part_name, slot, update, false);
     }
 
     if (slot_override == "all") {
-        set_active(transport, "a");
+        set_active("a");
     } else {
-        set_active(transport, slot_override);
+        set_active(slot_override);
     }
 
     CloseArchive(zip);
@@ -1125,7 +1125,7 @@
     fb_queue_command("signature", "installing signature");
 }
 
-static void do_flashall(Transport* transport, const std::string& slot_override, bool skip_secondary) {
+static void do_flashall(const std::string& slot_override, bool skip_secondary) {
     std::string fname;
     queue_info_dump();
 
@@ -1138,17 +1138,17 @@
     void* data = load_file(fname.c_str(), &sz);
     if (data == nullptr) die("could not load android-info.txt: %s", strerror(errno));
 
-    check_requirements(transport, reinterpret_cast<char*>(data), sz);
+    check_requirements(reinterpret_cast<char*>(data), sz);
 
     std::string secondary;
     if (!skip_secondary) {
         if (slot_override != "") {
-            secondary = get_other_slot(transport, slot_override);
+            secondary = get_other_slot(slot_override);
         } else {
-            secondary = get_other_slot(transport);
+            secondary = get_other_slot();
         }
         if (secondary == "") {
-            if (supports_AB(transport)) {
+            if (supports_AB()) {
                 fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
             }
             skip_secondary = true;
@@ -1165,7 +1165,7 @@
         if (!slot) continue;
         fname = find_item_given_name(images[i].img_name);
         fastboot_buffer buf;
-        if (!load_buf(transport, fname.c_str(), &buf)) {
+        if (!load_buf(fname.c_str(), &buf)) {
             if (images[i].is_optional) continue;
             die("could not load '%s': %s", images[i].img_name, strerror(errno));
         }
@@ -1174,13 +1174,13 @@
             do_send_signature(fname.c_str());
             flash_buf(partition.c_str(), &buf);
         };
-        do_for_partitions(transport, images[i].part_name, slot, flashall, false);
+        do_for_partitions(images[i].part_name, slot, flashall, false);
     }
 
     if (slot_override == "all") {
-        set_active(transport, "a");
+        set_active("a");
     } else {
-        set_active(transport, slot_override);
+        set_active(slot_override);
     }
 }
 
@@ -1210,9 +1210,9 @@
     return var;
 }
 
-static unsigned fb_get_flash_block_size(Transport* transport, std::string name) {
+static unsigned fb_get_flash_block_size(std::string name) {
     std::string sizeString;
-    if (!fb_getvar(transport, name, &sizeString) || sizeString.empty()) {
+    if (!fb_getvar(name, &sizeString) || sizeString.empty()) {
         // This device does not report flash block sizes, so return 0.
         return 0;
     }
@@ -1230,7 +1230,7 @@
     return size;
 }
 
-static void fb_perform_format(Transport* transport,
+static void fb_perform_format(
                               const std::string& partition, int skip_if_not_supported,
                               const std::string& type_override, const std::string& size_override,
                               const std::string& initial_dir) {
@@ -1250,7 +1250,7 @@
         limit = sparse_limit;
     }
 
-    if (!fb_getvar(transport, "partition-type:" + partition, &partition_type)) {
+    if (!fb_getvar("partition-type:" + partition, &partition_type)) {
         errMsg = "Can't determine partition type.\n";
         goto failed;
     }
@@ -1262,7 +1262,7 @@
         partition_type = type_override;
     }
 
-    if (!fb_getvar(transport, "partition-size:" + partition, &partition_size)) {
+    if (!fb_getvar("partition-size:" + partition, &partition_size)) {
         errMsg = "Unable to get partition size\n";
         goto failed;
     }
@@ -1294,8 +1294,8 @@
     }
 
     unsigned eraseBlkSize, logicalBlkSize;
-    eraseBlkSize = fb_get_flash_block_size(transport, "erase-block-size");
-    logicalBlkSize = fb_get_flash_block_size(transport, "logical-block-size");
+    eraseBlkSize = fb_get_flash_block_size("erase-block-size");
+    logicalBlkSize = fb_get_flash_block_size("logical-block-size");
 
     if (fs_generator_generate(gen, output.path, size, initial_dir,
             eraseBlkSize, logicalBlkSize)) {
@@ -1308,7 +1308,7 @@
         fprintf(stderr, "Cannot open generated image: %s\n", strerror(errno));
         return;
     }
-    if (!load_buf_fd(transport, fd.release(), &buf)) {
+    if (!load_buf_fd(fd.release(), &buf)) {
         fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
         return;
     }
@@ -1323,7 +1323,7 @@
     fprintf(stderr, "FAILED (%s)\n", fb_get_error().c_str());
 }
 
-int FastBoot::Main(int argc, char* argv[]) {
+int FastBootTool::Main(int argc, char* argv[]) {
     bool wants_wipe = false;
     bool wants_reboot = false;
     bool wants_reboot_bootloader = false;
@@ -1470,23 +1470,25 @@
     if (transport == nullptr) {
         return 1;
     }
+    fastboot::FastBootDriver fb(transport);
+    fb_init(fb);
 
     const double start = now();
 
-    if (slot_override != "") slot_override = verify_slot(transport, slot_override);
-    if (next_active != "") next_active = verify_slot(transport, next_active, false);
+    if (slot_override != "") slot_override = verify_slot(slot_override);
+    if (next_active != "") next_active = verify_slot(next_active, false);
 
     if (wants_set_active) {
         if (next_active == "") {
             if (slot_override == "") {
                 std::string current_slot;
-                if (fb_getvar(transport, "current-slot", &current_slot)) {
-                    next_active = verify_slot(transport, current_slot, false);
+                if (fb_getvar("current-slot", &current_slot)) {
+                    next_active = verify_slot(current_slot, false);
                 } else {
                     wants_set_active = false;
                 }
             } else {
-                next_active = verify_slot(transport, slot_override, false);
+                next_active = verify_slot(slot_override, false);
             }
         }
     }
@@ -1502,7 +1504,7 @@
             std::string partition = next_arg(&args);
             auto erase = [&](const std::string& partition) {
                 std::string partition_type;
-                if (fb_getvar(transport, std::string("partition-type:") + partition,
+                if (fb_getvar(std::string("partition-type:") + partition,
                               &partition_type) &&
                     fs_get_generator(partition_type) != nullptr) {
                     fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
@@ -1511,7 +1513,7 @@
 
                 fb_queue_erase(partition);
             };
-            do_for_partitions(transport, partition, slot_override, erase, true);
+            do_for_partitions(partition, slot_override, erase, true);
         } else if (android::base::StartsWith(command, "format")) {
             // Parsing for: "format[:[type][:[size]]]"
             // Some valid things:
@@ -1529,9 +1531,9 @@
             std::string partition = next_arg(&args);
 
             auto format = [&](const std::string& partition) {
-                fb_perform_format(transport, partition, 0, type_override, size_override, "");
+                fb_perform_format(partition, 0, type_override, size_override, "");
             };
-            do_for_partitions(transport, partition.c_str(), slot_override, format, true);
+            do_for_partitions(partition.c_str(), slot_override, format, true);
         } else if (command == "signature") {
             std::string filename = next_arg(&args);
             data = load_file(filename.c_str(), &sz);
@@ -1579,9 +1581,9 @@
             if (fname.empty()) die("cannot determine image filename for '%s'", pname.c_str());
 
             auto flash = [&](const std::string &partition) {
-                do_flash(transport, partition.c_str(), fname.c_str());
+                do_flash(partition.c_str(), fname.c_str());
             };
-            do_for_partitions(transport, pname.c_str(), slot_override, flash, true);
+            do_for_partitions(pname.c_str(), slot_override, flash, true);
         } else if (command == "flash:raw") {
             std::string partition = next_arg(&args);
             std::string kernel = next_arg(&args);
@@ -1594,13 +1596,13 @@
             auto flashraw = [&](const std::string& partition) {
                 fb_queue_flash(partition, data, sz);
             };
-            do_for_partitions(transport, partition, slot_override, flashraw, true);
+            do_for_partitions(partition, slot_override, flashraw, true);
         } else if (command == "flashall") {
             if (slot_override == "all") {
                 fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
-                do_flashall(transport, slot_override, true);
+                do_flashall(slot_override, true);
             } else {
-                do_flashall(transport, slot_override, skip_secondary);
+                do_flashall(slot_override, skip_secondary);
             }
             wants_reboot = true;
         } else if (command == "update") {
@@ -1612,16 +1614,16 @@
             if (!args.empty()) {
                 filename = next_arg(&args);
             }
-            do_update(transport, filename.c_str(), slot_override, skip_secondary || slot_all);
+            do_update(filename.c_str(), slot_override, skip_secondary || slot_all);
             wants_reboot = true;
         } else if (command == "set_active") {
-            std::string slot = verify_slot(transport, next_arg(&args), false);
+            std::string slot = verify_slot(next_arg(&args), false);
             fb_set_active(slot);
         } else if (command == "stage") {
             std::string filename = next_arg(&args);
 
             struct fastboot_buffer buf;
-            if (!load_buf(transport, filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
+            if (!load_buf(filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
                 die("cannot load '%s'", filename.c_str());
             }
             fb_queue_download_fd(filename, buf.fd, buf.sz);
@@ -1650,16 +1652,16 @@
         std::vector<std::string> partitions = { "userdata", "cache", "metadata" };
         for (const auto& partition : partitions) {
             std::string partition_type;
-            if (!fb_getvar(transport, std::string{"partition-type:"} + partition, &partition_type)) continue;
+            if (!fb_getvar(std::string{"partition-type:"} + partition, &partition_type)) continue;
             if (partition_type.empty()) continue;
             fb_queue_erase(partition);
             if (partition == "userdata" && set_fbe_marker) {
                 fprintf(stderr, "setting FBE marker on initial userdata...\n");
                 std::string initial_userdata_dir = create_fbemarker_tmpdir();
-                fb_perform_format(transport, partition, 1, "", "", initial_userdata_dir);
+                fb_perform_format(partition, 1, "", "", initial_userdata_dir);
                 delete_fbemarker_tmpdir(initial_userdata_dir);
             } else {
-                fb_perform_format(transport, partition, 1, "", "", "");
+                fb_perform_format(partition, 1, "", "", "");
             }
         }
     }
@@ -1674,12 +1676,12 @@
         fb_queue_wait_for_disconnect();
     }
 
-    int status = fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
+    int status = fb_execute_queue() ? EXIT_FAILURE : EXIT_SUCCESS;
     fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
     return status;
 }
 
-void FastBoot::ParseOsPatchLevel(boot_img_hdr_v1* hdr, const char* arg) {
+void FastBootTool::ParseOsPatchLevel(boot_img_hdr_v1* hdr, const char* arg) {
     unsigned year, month, day;
     if (sscanf(arg, "%u-%u-%u", &year, &month, &day) != 3) {
         syntax_error("OS patch level should be YYYY-MM-DD: %s", arg);
@@ -1689,7 +1691,7 @@
     hdr->SetOsPatchLevel(year, month);
 }
 
-void FastBoot::ParseOsVersion(boot_img_hdr_v1* hdr, const char* arg) {
+void FastBootTool::ParseOsVersion(boot_img_hdr_v1* hdr, const char* arg) {
     unsigned major = 0, minor = 0, patch = 0;
     std::vector<std::string> versions = android::base::Split(arg, ".");
     if (versions.size() < 1 || versions.size() > 3 ||
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
new file mode 100644
index 0000000..c308420
--- /dev/null
+++ b/fastboot/fastboot_driver.cpp
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2018 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 "fastboot_driver.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <algorithm>
+#include <chrono>
+#include <fstream>
+#include <memory>
+#include <regex>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <utils/FileMap.h>
+#include "fastboot_driver.h"
+#include "transport.h"
+
+namespace fastboot {
+
+/*************************** PUBLIC *******************************/
+FastBootDriver::FastBootDriver(Transport* transport, std::function<void(std::string&)> info,
+                               bool no_checks)
+    : transport(transport) {
+    info_cb_ = info;
+    disable_checks_ = no_checks;
+}
+
+RetCode FastBootDriver::Boot(std::string* response, std::vector<std::string>* info) {
+    return RawCommand(Commands::BOOT, response, info);
+}
+
+RetCode FastBootDriver::Continue(std::string* response, std::vector<std::string>* info) {
+    return RawCommand(Commands::CONTINUE, response, info);
+}
+
+RetCode FastBootDriver::Erase(const std::string& part, std::string* response,
+                              std::vector<std::string>* info) {
+    return RawCommand(Commands::ERASE + part, response, info);
+}
+
+RetCode FastBootDriver::Flash(const std::string& part, std::string* response,
+                              std::vector<std::string>* info) {
+    return RawCommand(Commands::FLASH + part, response, info);
+}
+
+RetCode FastBootDriver::GetVar(const std::string& key, std::string* val,
+                               std::vector<std::string>* info) {
+    return RawCommand(Commands::GET_VAR + key, val, info);
+}
+
+RetCode FastBootDriver::GetVarAll(std::vector<std::string>* response) {
+    std::string tmp;
+    return GetVar("all", &tmp, response);
+}
+
+RetCode FastBootDriver::Powerdown(std::string* response, std::vector<std::string>* info) {
+    return RawCommand(Commands::POWERDOWN, response, info);
+}
+
+RetCode FastBootDriver::Reboot(std::string* response, std::vector<std::string>* info) {
+    return RawCommand(Commands::REBOOT, response, info);
+}
+
+RetCode FastBootDriver::SetActive(const std::string& part, std::string* response,
+                                  std::vector<std::string>* info) {
+    return RawCommand(Commands::SET_ACTIVE + part, response, info);
+}
+
+RetCode FastBootDriver::Verify(uint32_t num, std::string* response, std::vector<std::string>* info) {
+    std::string cmd = android::base::StringPrintf("%s%08" PRIx32, Commands::VERIFY.c_str(), num);
+    return RawCommand(cmd, response, info);
+}
+
+RetCode FastBootDriver::FlashPartition(const std::string& part, const std::vector<char>& data) {
+    RetCode ret;
+    if ((ret = Download(data))) {
+        return ret;
+    }
+    return RawCommand(Commands::FLASH + part);
+}
+
+RetCode FastBootDriver::FlashPartition(const std::string& part, int fd, uint32_t sz) {
+    RetCode ret;
+    if ((ret = Download(fd, sz))) {
+        return ret;
+    }
+    return RawCommand(Commands::FLASH + part);
+}
+
+RetCode FastBootDriver::FlashPartition(const std::string& part, sparse_file* s) {
+    RetCode ret;
+    if ((ret = Download(s))) {
+        return ret;
+    }
+    return RawCommand(Commands::FLASH + part);
+}
+
+RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint32_t>>* parts) {
+    std::vector<std::string> all;
+    RetCode ret;
+    if ((ret = GetVarAll(&all))) {
+        return ret;
+    }
+
+    std::regex reg("partition-size[[:s:]]*:[[:s:]]*([[:w:]]+)[[:s:]]*:[[:s:]]*0x([[:d:]]+)");
+    std::smatch sm;
+
+    for (auto& s : all) {
+        if (std::regex_match(s, sm, reg)) {
+            std::string m1(sm[1]);
+            std::string m2(sm[2]);
+            uint32_t tmp = strtol(m2.c_str(), 0, 16);
+            parts->push_back(std::make_tuple(m1, tmp));
+        }
+    }
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::Require(const std::string& var, const std::vector<std::string>& allowed,
+                                bool* reqmet, bool invert) {
+    *reqmet = invert;
+    RetCode ret;
+    std::string response;
+    if ((ret = GetVar(var, &response))) {
+        return ret;
+    }
+
+    // Now check if we have a match
+    for (const auto s : allowed) {
+        // If it ends in *, and starting substring match
+        if (response == s || (s.length() && s.back() == '*' &&
+                              !response.compare(0, s.length() - 1, s, 0, s.length() - 1))) {
+            *reqmet = !invert;
+            break;
+        }
+    }
+
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::Download(int fd, size_t size, std::string* response,
+                                 std::vector<std::string>* info) {
+    RetCode ret;
+
+    if ((size <= 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
+        error_ = "File is too large to download";
+        return BAD_ARG;
+    }
+
+    uint32_t u32size = static_cast<uint32_t>(size);
+    if ((ret = DownloadCommand(u32size, response, info))) {
+        return ret;
+    }
+
+    // Write the buffer
+    if ((ret = SendBuffer(fd, size))) {
+        return ret;
+    }
+
+    // Wait for response
+    return HandleResponse(response, info);
+}
+
+RetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,
+                                 std::vector<std::string>* info) {
+    return Download(buf.data(), buf.size(), response, info);
+}
+
+RetCode FastBootDriver::Download(const char* buf, uint32_t size, std::string* response,
+                                 std::vector<std::string>* info) {
+    RetCode ret;
+    error_ = "";
+    if ((size == 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
+        error_ = "Buffer is too large or 0 bytes";
+        return BAD_ARG;
+    }
+
+    if ((ret = DownloadCommand(size, response, info))) {
+        return ret;
+    }
+
+    // Write the buffer
+    if ((ret = SendBuffer(buf, size))) {
+        return ret;
+    }
+
+    // Wait for response
+    return HandleResponse(response, info);
+}
+
+RetCode FastBootDriver::Download(sparse_file* s, std::string* response,
+                                 std::vector<std::string>* info) {
+    error_ = "";
+    int64_t size = sparse_file_len(s, true, false);
+    if (size <= 0 || size > MAX_DOWNLOAD_SIZE) {
+        error_ = "Sparse file is too large or invalid";
+        return BAD_ARG;
+    }
+
+    RetCode ret;
+    uint32_t u32size = static_cast<uint32_t>(size);
+    if ((ret = DownloadCommand(u32size, response, info))) {
+        return ret;
+    }
+
+    struct SparseCBPrivate {
+        FastBootDriver* self;
+        std::vector<char> tpbuf;
+    } cb_priv;
+    cb_priv.self = this;
+
+    auto cb = [](void* priv, const void* buf, size_t len) -> int {
+        SparseCBPrivate* data = static_cast<SparseCBPrivate*>(priv);
+        const char* cbuf = static_cast<const char*>(buf);
+        return data->self->SparseWriteCallback(data->tpbuf, cbuf, len);
+    };
+
+    if (sparse_file_callback(s, true, false, cb, &cb_priv) < 0) {
+        error_ = "Error reading sparse file";
+        return IO_ERROR;
+    }
+
+    // Now flush
+    if (cb_priv.tpbuf.size() && (ret = SendBuffer(cb_priv.tpbuf))) {
+        return ret;
+    }
+
+    return HandleResponse(response, info);
+}
+
+RetCode FastBootDriver::Upload(const std::string& outfile, std::string* response,
+                               std::vector<std::string>* info) {
+    RetCode ret;
+    int dsize;
+    if ((ret = RawCommand(Commands::UPLOAD, response, info, &dsize)) || dsize == 0) {
+        error_ = "Upload request failed";
+        return ret;
+    }
+
+    std::vector<char> data;
+    data.resize(dsize);
+
+    if ((ret = ReadBuffer(data))) {
+        return ret;
+    }
+
+    std::ofstream ofs;
+    ofs.open(outfile, std::ofstream::out | std::ofstream::binary);
+    if (ofs.fail()) {
+        error_ = android::base::StringPrintf("Failed to open '%s'", outfile.c_str());
+        return IO_ERROR;
+    }
+    ofs.write(data.data(), data.size());
+    if (ofs.fail() || ofs.bad()) {
+        error_ = android::base::StringPrintf("Writing to '%s' failed", outfile.c_str());
+        return IO_ERROR;
+    }
+    ofs.close();
+
+    return HandleResponse(response, info);
+}
+
+// Helpers
+void FastBootDriver::SetInfoCallback(std::function<void(std::string&)> info) {
+    info_cb_ = info;
+}
+
+const std::string FastBootDriver::RCString(RetCode rc) {
+    switch (rc) {
+        case SUCCESS:
+            return std::string("Success");
+
+        case BAD_ARG:
+            return std::string("Invalid Argument");
+
+        case IO_ERROR:
+            return std::string("I/O Error");
+
+        case BAD_DEV_RESP:
+            return std::string("Invalid Device Response");
+
+        case DEVICE_FAIL:
+            return std::string("Device Error");
+
+        case TIMEOUT:
+            return std::string("Timeout");
+
+        default:
+            return std::string("Unknown Error");
+    }
+}
+
+std::string FastBootDriver::Error() {
+    return error_;
+}
+
+RetCode FastBootDriver::WaitForDisconnect() {
+    return transport->WaitForDisconnect() ? IO_ERROR : SUCCESS;
+}
+
+/****************************** PROTECTED *************************************/
+RetCode FastBootDriver::RawCommand(const std::string& cmd, std::string* response,
+                                   std::vector<std::string>* info, int* dsize) {
+    error_ = "";  // Clear any pending error
+    if (cmd.size() > FB_COMMAND_SZ && !disable_checks_) {
+        error_ = "Command length to RawCommand() is too long";
+        return BAD_ARG;
+    }
+
+    if (transport->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
+        error_ = ErrnoStr("Write to device failed");
+        return IO_ERROR;
+    }
+
+    // Read the response
+    return HandleResponse(response, info, dsize);
+}
+
+RetCode FastBootDriver::DownloadCommand(uint32_t size, std::string* response,
+                                        std::vector<std::string>* info) {
+    std::string cmd(android::base::StringPrintf("%s%08" PRIx32, Commands::DOWNLOAD.c_str(), size));
+    RetCode ret;
+    if ((ret = RawCommand(cmd, response, info))) {
+        return ret;
+    }
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::HandleResponse(std::string* response, std::vector<std::string>* info,
+                                       int* dsize) {
+    char status[FB_RESPONSE_SZ + 1];
+    auto start = std::chrono::system_clock::now();
+
+    auto set_response = [response](std::string s) {
+        if (response) *response = std::move(s);
+    };
+    auto add_info = [info](std::string s) {
+        if (info) info->push_back(std::move(s));
+    };
+
+    // erase response
+    set_response("");
+    while ((std::chrono::system_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
+        int r = transport->Read(status, FB_RESPONSE_SZ);
+        if (r < 0) {
+            error_ = ErrnoStr("Status read failed");
+            return IO_ERROR;
+        }
+
+        status[r] = '\0';  // Need the null terminator
+        std::string input(status);
+        if (android::base::StartsWith(input, "INFO")) {
+            std::string tmp = input.substr(strlen("INFO"));
+            info_cb_(tmp);
+            add_info(std::move(tmp));
+        } else if (android::base::StartsWith(input, "OKAY")) {
+            set_response(input.substr(strlen("OKAY")));
+            return SUCCESS;
+        } else if (android::base::StartsWith(input, "FAIL")) {
+            error_ = android::base::StringPrintf("remote: '%s'", status + strlen("FAIL"));
+            set_response(input.substr(strlen("FAIL")));
+            return DEVICE_FAIL;
+        } else if (android::base::StartsWith(input, "DATA")) {
+            std::string tmp = input.substr(strlen("DATA"));
+            uint32_t num = strtol(tmp.c_str(), 0, 16);
+            if (num > MAX_DOWNLOAD_SIZE) {
+                error_ = android::base::StringPrintf("Data size too large (%d)", num);
+                return BAD_DEV_RESP;
+            }
+            if (dsize) *dsize = num;
+            set_response(std::move(tmp));
+            return SUCCESS;
+        } else {
+            error_ = android::base::StringPrintf("Device sent unknown status code: %s", status);
+            return BAD_DEV_RESP;
+        }
+
+    }  // End of while loop
+
+    return TIMEOUT;
+}
+
+std::string FastBootDriver::ErrnoStr(const std::string& msg) {
+    return android::base::StringPrintf("%s (%s)", msg.c_str(), strerror(errno));
+}
+
+const std::string FastBootDriver::Commands::BOOT = "boot";
+const std::string FastBootDriver::Commands::CONTINUE = "continue";
+const std::string FastBootDriver::Commands::DOWNLOAD = "download:";
+const std::string FastBootDriver::Commands::ERASE = "erase:";
+const std::string FastBootDriver::Commands::FLASH = "flash:";
+const std::string FastBootDriver::Commands::GET_VAR = "getvar:";
+const std::string FastBootDriver::Commands::POWERDOWN = "powerdown";
+const std::string FastBootDriver::Commands::REBOOT = "reboot";
+const std::string FastBootDriver::Commands::SET_ACTIVE = "set_active:";
+const std::string FastBootDriver::Commands::UPLOAD = "upload";
+const std::string FastBootDriver::Commands::VERIFY = "verify:";
+
+/******************************* PRIVATE **************************************/
+RetCode FastBootDriver::SendBuffer(int fd, size_t size) {
+    static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
+    off64_t offset = 0;
+    uint32_t remaining = size;
+    RetCode ret;
+
+    while (remaining) {
+        // Memory map the file
+        android::FileMap filemap;
+        size_t len = std::min(remaining, MAX_MAP_SIZE);
+
+        if (!filemap.create(NULL, fd, offset, len, true)) {
+            error_ = "Creating filemap failed";
+            return IO_ERROR;
+        }
+
+        if ((ret = SendBuffer(filemap.getDataPtr(), len))) {
+            return ret;
+        }
+
+        remaining -= len;
+        offset += len;
+    }
+
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::SendBuffer(const std::vector<char>& buf) {
+    // Write the buffer
+    return SendBuffer(buf.data(), buf.size());
+}
+
+RetCode FastBootDriver::SendBuffer(const void* buf, size_t size) {
+    // Write the buffer
+    ssize_t tmp = transport->Write(buf, size);
+
+    if (tmp < 0) {
+        error_ = ErrnoStr("Write to device failed in SendBuffer()");
+        return IO_ERROR;
+    } else if (static_cast<size_t>(tmp) != size) {
+        error_ = android::base::StringPrintf("Failed to write all %zu bytes", size);
+
+        return IO_ERROR;
+    }
+
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::ReadBuffer(std::vector<char>& buf) {
+    // Read the buffer
+    return ReadBuffer(buf.data(), buf.size());
+}
+
+RetCode FastBootDriver::ReadBuffer(void* buf, size_t size) {
+    // Read the buffer
+    ssize_t tmp = transport->Read(buf, size);
+
+    if (tmp < 0) {
+        error_ = ErrnoStr("Read from device failed in ReadBuffer()");
+        return IO_ERROR;
+    } else if (static_cast<size_t>(tmp) != size) {
+        error_ = android::base::StringPrintf("Failed to read all %zu bytes", size);
+        return IO_ERROR;
+    }
+
+    return SUCCESS;
+}
+
+int FastBootDriver::SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len) {
+    size_t total = 0;
+    size_t to_write = std::min(TRANSPORT_CHUNK_SIZE - tpbuf.size(), len);
+
+    // Handle the residual
+    tpbuf.insert(tpbuf.end(), data, data + to_write);
+    if (tpbuf.size() < TRANSPORT_CHUNK_SIZE) {  // Nothing enough to send rn
+        return 0;
+    }
+
+    if (SendBuffer(tpbuf)) {
+        error_ = ErrnoStr("Send failed in SparseWriteCallback()");
+        return -1;
+    }
+    tpbuf.clear();
+    total += to_write;
+
+    // Now we need to send a multiple of chunk size
+    size_t nchunks = (len - total) / TRANSPORT_CHUNK_SIZE;
+    size_t nbytes = TRANSPORT_CHUNK_SIZE * nchunks;
+    if (SendBuffer(data + total, nbytes)) {
+        error_ = ErrnoStr("Send failed in SparseWriteCallback()");
+        return -1;
+    }
+    total += nbytes;
+
+    if (len - total > 0) {  // We have residual data to save for next time
+        tpbuf.assign(data + total, data + len);
+    }
+
+    return 0;
+}
+
+}  // End namespace fastboot
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
new file mode 100644
index 0000000..9fdd317
--- /dev/null
+++ b/fastboot/fastboot_driver.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+#pragma once
+#include <cstdlib>
+#include <deque>
+#include <limits>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <bootimg.h>
+#include <inttypes.h>
+#include <sparse/sparse.h>
+#include "transport.h"
+
+class Transport;
+
+namespace fastboot {
+
+static constexpr int FB_COMMAND_SZ = 64;
+static constexpr int FB_RESPONSE_SZ = 64;
+
+enum RetCode : int {
+    SUCCESS = 0,
+    BAD_ARG,
+    IO_ERROR,
+    BAD_DEV_RESP,
+    DEVICE_FAIL,
+    TIMEOUT,
+};
+
+class FastBootDriver {
+  public:
+    static constexpr int RESP_TIMEOUT = 10;  // 10 seconds
+    static constexpr uint32_t MAX_DOWNLOAD_SIZE = std::numeric_limits<uint32_t>::max();
+    static constexpr size_t TRANSPORT_CHUNK_SIZE = 1024;
+
+    FastBootDriver(Transport* transport,
+                   std::function<void(std::string&)> info = [](std::string&) {},
+                   bool no_checks = false);
+
+    RetCode Boot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode Continue(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode Download(int fd, size_t size, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    RetCode Download(const std::vector<char>& buf, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    // This will be removed after fastboot is modified to use a vector
+    RetCode Download(const char* buf, uint32_t size, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    RetCode Download(sparse_file* s, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    RetCode Erase(const std::string& part, std::string* response = nullptr,
+                  std::vector<std::string>* info = nullptr);
+    RetCode Flash(const std::string& part, std::string* response = nullptr,
+                  std::vector<std::string>* info = nullptr);
+    RetCode GetVar(const std::string& key, std::string* val,
+                   std::vector<std::string>* info = nullptr);
+    RetCode GetVarAll(std::vector<std::string>* response);
+    RetCode Powerdown(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode Reboot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode SetActive(const std::string& part, std::string* response = nullptr,
+                      std::vector<std::string>* info = nullptr);
+    RetCode Upload(const std::string& outfile, std::string* response = nullptr,
+                   std::vector<std::string>* info = nullptr);
+    RetCode Verify(uint32_t num, std::string* response = nullptr,
+                   std::vector<std::string>* info = nullptr);
+
+    /* HIGHER LEVEL COMMANDS -- Composed of the commands above */
+    RetCode FlashPartition(const std::string& part, const std::vector<char>& data);
+    RetCode FlashPartition(const std::string& part, int fd, uint32_t sz);
+    RetCode FlashPartition(const std::string& part, sparse_file* s);
+
+    RetCode Partitions(std::vector<std::tuple<std::string, uint32_t>>* parts);
+    RetCode Require(const std::string& var, const std::vector<std::string>& allowed, bool* reqmet,
+                    bool invert = false);
+
+    /* HELPERS */
+    void SetInfoCallback(std::function<void(std::string&)> info);
+    const std::string RCString(RetCode rc);
+    std::string Error();
+    RetCode WaitForDisconnect();
+
+    // This is temporarily public for engine.cpp
+    RetCode RawCommand(const std::string& cmd, std::string* response = nullptr,
+                       std::vector<std::string>* info = nullptr, int* dsize = nullptr);
+
+  protected:
+    RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,
+                            std::vector<std::string>* info = nullptr);
+    RetCode HandleResponse(std::string* response = nullptr,
+                           std::vector<std::string>* info = nullptr, int* dsize = nullptr);
+
+    std::string ErrnoStr(const std::string& msg);
+
+    // More like a namespace...
+    struct Commands {
+        static const std::string BOOT;
+        static const std::string CONTINUE;
+        static const std::string DOWNLOAD;
+        static const std::string ERASE;
+        static const std::string FLASH;
+        static const std::string GET_VAR;
+        static const std::string POWERDOWN;
+        static const std::string REBOOT;
+        static const std::string SET_ACTIVE;
+        static const std::string UPLOAD;
+        static const std::string VERIFY;
+    };
+
+    Transport* const transport;
+
+  private:
+    RetCode SendBuffer(int fd, size_t size);
+    RetCode SendBuffer(const std::vector<char>& buf);
+    RetCode SendBuffer(const void* buf, size_t size);
+
+    RetCode ReadBuffer(std::vector<char>& buf);
+    RetCode ReadBuffer(void* buf, size_t size);
+
+    int SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len);
+
+    std::string error_;
+    std::function<void(std::string&)> info_cb_;
+    bool disable_checks_;
+};
+
+}  // namespace fastboot
diff --git a/fastboot/fastboot_test.cpp b/fastboot/fastboot_test.cpp
index 1681427..43201fa 100644
--- a/fastboot/fastboot_test.cpp
+++ b/fastboot/fastboot_test.cpp
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#include "fastboot.h"
+#include "engine.h"
 
 #include <gtest/gtest.h>
 
 TEST(FastBoot, ParseOsPatchLevel) {
-    FastBoot fb;
+    FastBootTool fb;
     boot_img_hdr_v1 hdr;
 
     hdr = {};
@@ -34,7 +34,7 @@
 }
 
 TEST(FastBoot, ParseOsVersion) {
-    FastBoot fb;
+    FastBootTool fb;
     boot_img_hdr_v1 hdr;
 
     hdr = {};
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 14dabaa..cc1714f 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -1,6 +1,5 @@
 #include "fs.h"
 
-#include "fastboot.h"
 
 #include <errno.h>
 #include <fcntl.h>
diff --git a/fastboot/main.cpp b/fastboot/main.cpp
index f1c8afb..c3683f7 100644
--- a/fastboot/main.cpp
+++ b/fastboot/main.cpp
@@ -26,9 +26,9 @@
  * SUCH DAMAGE.
  */
 
-#include "fastboot.h"
+#include "engine.h"
 
 int main(int argc, char* argv[]) {
-    FastBoot fb;
+    FastBootTool fb;
     return fb.Main(argc, argv);
 }
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
deleted file mode 100644
index e625095..0000000
--- a/fastboot/protocol.cpp
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-#define round_down(a, b) \
-    ({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); })
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <algorithm>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <sparse/sparse.h>
-#include <utils/FileMap.h>
-
-#include "constants.h"
-#include "fastboot.h"
-#include "transport.h"
-
-static std::string g_error;
-
-using android::base::unique_fd;
-using android::base::WriteStringToFile;
-
-const std::string fb_get_error() {
-    return g_error;
-}
-
-static int64_t check_response(Transport* transport, uint32_t size, char* response) {
-    char status[FB_RESPONSE_SZ + 1];
-
-    while (true) {
-        int r = transport->Read(status, FB_RESPONSE_SZ);
-        if (r < 0) {
-            g_error = android::base::StringPrintf("status read failed (%s)", strerror(errno));
-            transport->Close();
-            return -1;
-        }
-        status[r] = 0;
-
-        if (static_cast<size_t>(r) < strlen(RESPONSE_OKAY)) {
-            g_error = android::base::StringPrintf("status malformed (%d bytes)", r);
-            transport->Close();
-            return -1;
-        }
-
-        if (!memcmp(status, RESPONSE_INFO, strlen(RESPONSE_INFO))) {
-            verbose("received INFO \"%s\"", status + strlen(RESPONSE_INFO));
-            fprintf(stderr, "(bootloader) %s\n", status + strlen(RESPONSE_INFO));
-            continue;
-        }
-
-        if (!memcmp(status, RESPONSE_OKAY, strlen(RESPONSE_OKAY))) {
-            verbose("received OKAY \"%s\"", status + strlen(RESPONSE_OKAY));
-            if (response) {
-                strcpy(response, status + strlen(RESPONSE_OKAY));
-            }
-            return 0;
-        }
-
-        if (!memcmp(status, RESPONSE_FAIL, strlen(RESPONSE_FAIL))) {
-            verbose("received FAIL \"%s\"", status + strlen(RESPONSE_FAIL));
-            if (static_cast<size_t>(r) > strlen(RESPONSE_FAIL)) {
-                g_error = android::base::StringPrintf("remote: %s", status + strlen(RESPONSE_FAIL));
-            } else {
-                g_error = "remote failure";
-            }
-            return -1;
-        }
-
-        if (!memcmp(status, RESPONSE_DATA, strlen(RESPONSE_DATA)) && size > 0){
-            verbose("received DATA %s", status + strlen(RESPONSE_DATA));
-            uint32_t dsize = strtol(status + strlen(RESPONSE_DATA), 0, 16);
-            if (dsize > size) {
-                g_error = android::base::StringPrintf("data size too large (%d)", dsize);
-                transport->Close();
-                return -1;
-            }
-            return dsize;
-        }
-
-        verbose("received unknown status code \"%4.4s\"", status);
-        g_error = "unknown status code";
-        transport->Close();
-        break;
-    }
-
-    return -1;
-}
-
-static int64_t _command_start(Transport* transport, const std::string& cmd, uint32_t size,
-                              char* response) {
-    if (cmd.size() > FB_COMMAND_SZ) {
-        g_error = android::base::StringPrintf("command too large (%zu)", cmd.size());
-        return -1;
-    }
-
-    if (response) {
-        response[0] = 0;
-    }
-
-    verbose("sending command \"%s\"", cmd.c_str());
-
-    if (transport->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
-        g_error = android::base::StringPrintf("command write failed (%s)", strerror(errno));
-        transport->Close();
-        return -1;
-    }
-
-    return check_response(transport, size, response);
-}
-
-static int64_t _command_write_data(Transport* transport, const void* data, uint32_t size) {
-    verbose("sending data (%" PRIu32 " bytes)", size);
-
-    int64_t r = transport->Write(data, size);
-    if (r < 0) {
-        g_error = android::base::StringPrintf("data write failure (%s)", strerror(errno));
-        transport->Close();
-        return -1;
-    }
-    if (r != static_cast<int64_t>(size)) {
-        g_error = "data write failure (short transfer)";
-        transport->Close();
-        return -1;
-    }
-    return r;
-}
-
-static int64_t _command_read_data(Transport* transport, void* data, uint32_t size) {
-    verbose("reading data (%" PRIu32 " bytes)", size);
-
-    int64_t r = transport->Read(data, size);
-    if (r < 0) {
-        g_error = android::base::StringPrintf("data read failure (%s)", strerror(errno));
-        transport->Close();
-        return -1;
-    }
-    if (r != (static_cast<int64_t>(size))) {
-        g_error = "data read failure (short transfer)";
-        transport->Close();
-        return -1;
-    }
-    return r;
-}
-
-static int64_t _command_end(Transport* transport) {
-    return check_response(transport, 0, 0) < 0 ? -1 : 0;
-}
-
-static int64_t _command_send(Transport* transport, const std::string& cmd, const void* data,
-                             uint32_t size, char* response) {
-    if (size == 0) {
-        return -1;
-    }
-
-    int64_t r = _command_start(transport, cmd, size, response);
-    if (r < 0) {
-        return -1;
-    }
-    r = _command_write_data(transport, data, size);
-    if (r < 0) {
-        return -1;
-    }
-
-    r = _command_end(transport);
-    if (r < 0) {
-        return -1;
-    }
-
-    return size;
-}
-
-static int64_t _command_send_fd(Transport* transport, const std::string& cmd, int fd, uint32_t size,
-                                char* response) {
-    static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
-    off64_t offset = 0;
-    uint32_t remaining = size;
-
-    if (_command_start(transport, cmd, size, response) < 0) {
-        return -1;
-    }
-
-    while (remaining) {
-        android::FileMap filemap;
-        size_t len = std::min(remaining, MAX_MAP_SIZE);
-
-        if (!filemap.create(NULL, fd, offset, len, true)) {
-            return -1;
-        }
-
-        if (_command_write_data(transport, filemap.getDataPtr(), len) < 0) {
-            return -1;
-        }
-
-        remaining -= len;
-        offset += len;
-    }
-
-    if (_command_end(transport) < 0) {
-        return -1;
-    }
-
-    return size;
-}
-
-static int _command_send_no_data(Transport* transport, const std::string& cmd, char* response) {
-    return _command_start(transport, cmd, 0, response);
-}
-
-int fb_command(Transport* transport, const std::string& cmd) {
-    return _command_send_no_data(transport, cmd, 0);
-}
-
-int fb_command_response(Transport* transport, const std::string& cmd, char* response) {
-    return _command_send_no_data(transport, cmd, response);
-}
-
-int64_t fb_download_data(Transport* transport, const void* data, uint32_t size) {
-    std::string cmd(android::base::StringPrintf(
-                FB_CMD_DOWNLOAD ":" "%08x", size));
-    return _command_send(transport, cmd.c_str(), data, size, 0) < 0 ? -1 : 0;
-}
-
-int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size) {
-    std::string cmd(android::base::StringPrintf(
-                FB_CMD_DOWNLOAD ":" "%08x", size));
-    return _command_send_fd(transport, cmd.c_str(), fd, size, 0) < 0 ? -1 : 0;
-}
-
-int64_t fb_upload_data(Transport* transport, const char* outfile) {
-    // positive return value is the upload size sent by the device
-    int64_t r = _command_start(transport, FB_CMD_UPLOAD,
-            std::numeric_limits<int32_t>::max(), nullptr);
-    if (r <= 0) {
-        g_error = android::base::StringPrintf("command start failed (%s)", strerror(errno));
-        return r;
-    }
-
-    std::string data;
-    data.resize(r);
-    if ((r = _command_read_data(transport, &data[0], data.size())) == -1) {
-        return r;
-    }
-
-    if (!WriteStringToFile(data, outfile, true)) {
-        g_error = android::base::StringPrintf("write to '%s' failed", outfile);
-        return -1;
-    }
-
-    return _command_end(transport);
-}
-
-static constexpr size_t TRANSPORT_BUF_SIZE = 1024;
-static char transport_buf[TRANSPORT_BUF_SIZE];
-static size_t transport_buf_len;
-
-static int fb_download_data_sparse_write(void* priv, const void* data, size_t len) {
-    const char* ptr = static_cast<const char*>(data);
-    if (transport_buf_len) {
-        size_t to_write = std::min(TRANSPORT_BUF_SIZE - transport_buf_len, len);
-
-        memcpy(transport_buf + transport_buf_len, ptr, to_write);
-        transport_buf_len += to_write;
-        ptr += to_write;
-        len -= to_write;
-    }
-
-    Transport* transport = static_cast<Transport*>(priv);
-    if (transport_buf_len == TRANSPORT_BUF_SIZE) {
-        int64_t r = _command_write_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
-        if (r != static_cast<int64_t>(TRANSPORT_BUF_SIZE)) {
-            return -1;
-        }
-        transport_buf_len = 0;
-    }
-
-    if (len > TRANSPORT_BUF_SIZE) {
-        if (transport_buf_len > 0) {
-            g_error = "internal error: transport_buf not empty";
-            return -1;
-        }
-        size_t to_write = round_down(len, TRANSPORT_BUF_SIZE);
-        int64_t r = _command_write_data(transport, ptr, to_write);
-        if (r != static_cast<int64_t>(to_write)) {
-            return -1;
-        }
-        ptr += to_write;
-        len -= to_write;
-    }
-
-    if (len > 0) {
-        if (len > TRANSPORT_BUF_SIZE) {
-            g_error = "internal error: too much left for transport_buf";
-            return -1;
-        }
-        memcpy(transport_buf, ptr, len);
-        transport_buf_len = len;
-    }
-
-    return 0;
-}
-
-static int fb_download_data_sparse_flush(Transport* transport) {
-    if (transport_buf_len > 0) {
-        int64_t r = _command_write_data(transport, transport_buf, transport_buf_len);
-        if (r != static_cast<int64_t>(transport_buf_len)) {
-            return -1;
-        }
-        transport_buf_len = 0;
-    }
-    return 0;
-}
-
-int fb_download_data_sparse(Transport* transport, struct sparse_file* s) {
-    int64_t size = sparse_file_len(s, true, false);
-    if (size <= 0 || size > std::numeric_limits<uint32_t>::max()) {
-        return -1;
-    }
-
-    std::string cmd(android::base::StringPrintf(
-                FB_CMD_DOWNLOAD ":" "%08" PRIx64, size));
-    int r = _command_start(transport, cmd, size, 0);
-    if (r < 0) {
-        return -1;
-    }
-
-    r = sparse_file_callback(s, true, false, fb_download_data_sparse_write, transport);
-    if (r < 0) {
-        return -1;
-    }
-
-    r = fb_download_data_sparse_flush(transport);
-    if (r < 0) {
-        return -1;
-    }
-
-    return _command_end(transport);
-}
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index cdab4f1..386dd30 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -47,8 +47,8 @@
 #include <memory>
 #include <thread>
 
-#include "fastboot.h"
 #include "usb.h"
+#include "util.h"
 
 using namespace std::chrono_literals;
 
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index 3dab5ac..0e5fba1 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -106,6 +106,7 @@
 
     if (nullptr == ret->adb_interface) {
         errno = GetLastError();
+        DBG("failed to open interface %S\n", interface_name);
         return nullptr;
     }
 
@@ -157,7 +158,7 @@
     unsigned count = 0;
     int ret;
 
-    DBG("usb_write %d\n", len);
+    DBG("usb_write %zu\n", len);
     if (nullptr != handle_) {
         // Perform write
         while(len > 0) {
@@ -195,7 +196,7 @@
     unsigned long read = 0;
     int ret;
 
-    DBG("usb_read %d\n", len);
+    DBG("usb_read %zu\n", len);
     if (nullptr != handle_) {
         while (1) {
             int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
@@ -269,19 +270,22 @@
         return 0;
 
     // Check vendor and product id first
-    if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
-                                 &device_desc)) {
+    if (!AdbGetUsbDeviceDescriptor(handle->adb_interface, &device_desc)) {
+        DBG("skipping device %x:%x\n", device_desc.idVendor, device_desc.idProduct);
         return 0;
     }
 
     // Then check interface properties
-    if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
-                                    &interf_desc)) {
+    if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface, &interf_desc)) {
+        DBG("skipping device %x:%x, failed to find interface\n", device_desc.idVendor,
+            device_desc.idProduct);
         return 0;
     }
 
     // Must have two endpoints
     if (2 != interf_desc.bNumEndpoints) {
+        DBG("skipping device %x:%x, incorrect number of endpoints\n", device_desc.idVendor,
+            device_desc.idProduct);
         return 0;
     }
 
@@ -305,9 +309,13 @@
     info.device_path[0] = 0;
 
     if (callback(&info) == 0) {
+        DBG("skipping device %x:%x, not selected by callback\n", device_desc.idVendor,
+            device_desc.idProduct);
         return 1;
     }
 
+    DBG("found device %x:%x (%s)\n", device_desc.idVendor, device_desc.idProduct,
+        info.serial_number);
     return 0;
 }
 
@@ -338,6 +346,7 @@
         }
         *copy_name = '\0';
 
+        DBG("attempting to open interface %S\n", next_interface->device_name);
         handle = do_usb_open(next_interface->device_name);
         if (NULL != handle) {
             // Lets see if this interface (device) belongs to us
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index 140270f..8f6e52a 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -33,7 +33,7 @@
 
 #include <sys/time.h>
 
-#include "fastboot.h"
+#include "util.h"
 
 static bool g_verbose = false;
 
diff --git a/fastboot/util.h b/fastboot/util.h
new file mode 100644
index 0000000..9033f93
--- /dev/null
+++ b/fastboot/util.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include <bootimg.h>
+
+/* util stuff */
+double now();
+char* xstrdup(const char*);
+void set_verbose();
+
+// These printf-like functions are implemented in terms of vsnprintf, so they
+// use the same attribute for compile-time format string checking.
+void die(const char* fmt, ...) __attribute__((__noreturn__))
+__attribute__((__format__(__printf__, 1, 2)));
+void verbose(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
diff --git a/init/devices.cpp b/init/devices.cpp
index ada1e28..ed4a739 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -31,7 +31,6 @@
 #include <selinux/selinux.h>
 
 #include "selinux.h"
-#include "ueventd.h"
 #include "util.h"
 
 #ifdef _INIT_INIT_H
diff --git a/init/util.cpp b/init/util.cpp
index 5f2b87d..7735717 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -37,12 +37,9 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#include <cutils/android_reboot.h>
 #include <cutils/sockets.h>
 #include <selinux/android.h>
 
-#include "reboot.h"
-
 #if defined(__ANDROID__)
 #include "selinux.h"
 #else