Merge "fs_mgr: __mount better detail for ENOENT"
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 6e143c1..e38e305 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -841,14 +841,19 @@
         return -1;
     }
 
-    std::string service = android::base::StringPrintf(
-        "sideload-host:%d:%d", static_cast<int>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
+    std::string service =
+            android::base::StringPrintf("sideload-host:%" PRId64 ":%d",
+                                        static_cast<int64_t>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
     std::string error;
     unique_fd device_fd(adb_connect(service, &error));
     if (device_fd < 0) {
-        // Try falling back to the older (<= K) sideload method. Maybe this
-        // is an older device that doesn't support sideload-host.
         fprintf(stderr, "adb: sideload connection failed: %s\n", error.c_str());
+
+        // If this is a small enough package, maybe this is an older device that doesn't
+        // support sideload-host. Try falling back to the older (<= K) sideload method.
+        if (sb.st_size > INT_MAX) {
+            return -1;
+        }
         fprintf(stderr, "adb: trying pre-KitKat sideload method...\n");
         return adb_sideload_legacy(filename, package_fd, static_cast<int>(sb.st_size));
     }
@@ -858,7 +863,7 @@
 
     char buf[SIDELOAD_HOST_BLOCK_SIZE];
 
-    size_t xfer = 0;
+    int64_t xfer = 0;
     int last_percent = -1;
     while (true) {
         if (!ReadFdExactly(device_fd, buf, 8)) {
@@ -874,20 +879,22 @@
             return 0;
         }
 
-        int block = strtol(buf, nullptr, 10);
-
-        size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
-        if (offset >= static_cast<size_t>(sb.st_size)) {
-            fprintf(stderr, "adb: failed to read block %d past end\n", block);
+        int64_t block = strtoll(buf, nullptr, 10);
+        int64_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
+        if (offset >= static_cast<int64_t>(sb.st_size)) {
+            fprintf(stderr,
+                    "adb: failed to read block %" PRId64 " at offset %" PRId64 ", past end %" PRId64
+                    "\n",
+                    block, offset, static_cast<int64_t>(sb.st_size));
             return -1;
         }
 
         size_t to_write = SIDELOAD_HOST_BLOCK_SIZE;
-        if ((offset + SIDELOAD_HOST_BLOCK_SIZE) > static_cast<size_t>(sb.st_size)) {
+        if ((offset + SIDELOAD_HOST_BLOCK_SIZE) > static_cast<int64_t>(sb.st_size)) {
             to_write = sb.st_size - offset;
         }
 
-        if (adb_lseek(package_fd, offset, SEEK_SET) != static_cast<int>(offset)) {
+        if (adb_lseek(package_fd, offset, SEEK_SET) != offset) {
             fprintf(stderr, "adb: failed to seek to package block: %s\n", strerror(errno));
             return -1;
         }
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 380dfa6..ae02525 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -35,6 +35,7 @@
 #include <string>
 #include <vector>
 
+#include <android-base/file.h>
 #include <android-base/properties.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
@@ -47,6 +48,8 @@
 #include "adb_utils.h"
 #include "set_verity_enable_state_service.h"
 
+using android::base::Realpath;
+
 // Returns the last device used to mount a directory in /proc/mounts.
 // This will find overlayfs entry where upperdir=lowerdir, to make sure
 // remount is associated with the correct directory.
@@ -55,9 +58,15 @@
     std::string mnt_fsname;
     if (!fp) return mnt_fsname;
 
+    // dir might be a symlink, e.g., /product -> /system/product in GSI.
+    std::string canonical_path;
+    if (!Realpath(dir, &canonical_path)) {
+        PLOG(ERROR) << "Realpath failed: " << dir;
+    }
+
     mntent* e;
     while ((e = getmntent(fp.get())) != nullptr) {
-        if (strcmp(dir, e->mnt_dir) == 0) {
+        if (canonical_path == e->mnt_dir) {
             mnt_fsname = e->mnt_fsname;
         }
     }
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index dee87bd..98a73eb 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -147,24 +147,34 @@
     return fde;
 }
 
-void fdevent_destroy(fdevent* fde) {
+unique_fd fdevent_release(fdevent* fde) {
     check_main_thread();
-    if (fde == nullptr) return;
+    if (!fde) {
+        return {};
+    }
+
     if (!(fde->state & FDE_CREATED)) {
         LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
     }
 
+    unique_fd result = std::move(fde->fd);
     if (fde->state & FDE_ACTIVE) {
-        g_poll_node_map.erase(fde->fd.get());
+        g_poll_node_map.erase(result.get());
+
         if (fde->state & FDE_PENDING) {
             g_pending_list.remove(fde);
         }
-        fde->fd.reset();
         fde->state = 0;
         fde->events = 0;
     }
 
     delete fde;
+    return result;
+}
+
+void fdevent_destroy(fdevent* fde) {
+    // Release, and then let unique_fd's destructor cleanup.
+    fdevent_release(fde);
 }
 
 static void fdevent_update(fdevent* fde, unsigned events) {
diff --git a/adb/fdevent.h b/adb/fdevent.h
index d501b86..df2339a 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -50,11 +50,12 @@
 */
 fdevent *fdevent_create(int fd, fd_func func, void *arg);
 
-/* Uninitialize and deallocate an fdevent object that was
-** created by fdevent_create()
-*/
+// Deallocate an fdevent object that was created by fdevent_create.
 void fdevent_destroy(fdevent *fde);
 
+// fdevent_destroy, except releasing the file descriptor previously owned by the fdevent.
+unique_fd fdevent_release(fdevent* fde);
+
 /* Change which events should cause notifications
 */
 void fdevent_set(fdevent *fde, unsigned events);
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index be0bdd0..b8d7e06 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -52,10 +52,11 @@
 #include <fcntl.h>
 #include <io.h>
 #include <process.h>
+#include <stdint.h>
 #include <sys/stat.h>
 #include <utime.h>
-#include <winsock2.h>
 #include <windows.h>
+#include <winsock2.h>
 #include <ws2tcpip.h>
 
 #include <memory>   // unique_ptr
@@ -92,7 +93,7 @@
 extern int adb_creat(const char* path, int mode);
 extern int adb_read(int fd, void* buf, int len);
 extern int adb_write(int fd, const void* buf, int len);
-extern int adb_lseek(int fd, int pos, int where);
+extern int64_t adb_lseek(int fd, int64_t pos, int where);
 extern int adb_shutdown(int fd, int direction = SHUT_RDWR);
 extern int adb_close(int fd);
 extern int adb_register_socket(SOCKET s);
@@ -315,25 +316,24 @@
 
 #else /* !_WIN32 a.k.a. Unix */
 
-#include <cutils/sockets.h>
 #include <fcntl.h>
-#include <poll.h>
-#include <signal.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-
-#include <pthread.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdarg.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
+#include <poll.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
 #include <string>
 
+#include <cutils/sockets.h>
+
 #define OS_PATH_SEPARATORS "/"
 #define OS_PATH_SEPARATOR '/'
 #define OS_PATH_SEPARATOR_STR "/"
@@ -443,12 +443,15 @@
 #undef   write
 #define  write  ___xxx_write
 
-static __inline__ int   adb_lseek(int  fd, int  pos, int  where)
-{
+static __inline__ int64_t adb_lseek(int fd, int64_t pos, int where) {
+#if defined(__APPLE__)
     return lseek(fd, pos, where);
+#else
+    return lseek64(fd, pos, where);
+#endif
 }
-#undef   lseek
-#define  lseek   ___xxx_lseek
+#undef lseek
+#define lseek ___xxx_lseek
 
 static __inline__  int    adb_unlink(const char*  path)
 {
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 026dd1c..8784757 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -52,12 +52,11 @@
 
 typedef const struct FHClassRec_* FHClass;
 typedef struct FHRec_* FH;
-typedef struct EventHookRec_* EventHook;
 
 typedef struct FHClassRec_ {
     void (*_fh_init)(FH);
     int (*_fh_close)(FH);
-    int (*_fh_lseek)(FH, int, int);
+    int64_t (*_fh_lseek)(FH, int64_t, int);
     int (*_fh_read)(FH, void*, int);
     int (*_fh_write)(FH, const void*, int);
     int (*_fh_writev)(FH, const adb_iovec*, int);
@@ -65,7 +64,7 @@
 
 static void _fh_file_init(FH);
 static int _fh_file_close(FH);
-static int _fh_file_lseek(FH, int, int);
+static int64_t _fh_file_lseek(FH, int64_t, int);
 static int _fh_file_read(FH, void*, int);
 static int _fh_file_write(FH, const void*, int);
 static int _fh_file_writev(FH, const adb_iovec*, int);
@@ -81,7 +80,7 @@
 
 static void _fh_socket_init(FH);
 static int _fh_socket_close(FH);
-static int _fh_socket_lseek(FH, int, int);
+static int64_t _fh_socket_lseek(FH, int64_t, int);
 static int _fh_socket_read(FH, void*, int);
 static int _fh_socket_write(FH, const void*, int);
 static int _fh_socket_writev(FH, const adb_iovec*, int);
@@ -318,10 +317,8 @@
     return wrote_bytes;
 }
 
-static int _fh_file_lseek(FH f, int pos, int origin) {
+static int64_t _fh_file_lseek(FH f, int64_t pos, int origin) {
     DWORD method;
-    DWORD result;
-
     switch (origin) {
         case SEEK_SET:
             method = FILE_BEGIN;
@@ -337,14 +334,13 @@
             return -1;
     }
 
-    result = SetFilePointer(f->fh_handle, pos, nullptr, method);
-    if (result == INVALID_SET_FILE_POINTER) {
+    LARGE_INTEGER li = {.QuadPart = pos};
+    if (!SetFilePointerEx(f->fh_handle, li, &li, method)) {
         errno = EIO;
         return -1;
-    } else {
-        f->eof = 0;
     }
-    return (int)result;
+    f->eof = 0;
+    return li.QuadPart;
 }
 
 /**************************************************************************/
@@ -491,14 +487,12 @@
     return f->clazz->_fh_writev(f, iov, iovcnt);
 }
 
-int adb_lseek(int fd, int pos, int where) {
+int64_t adb_lseek(int fd, int64_t pos, int where) {
     FH f = _fh_from_int(fd, __func__);
-
     if (!f) {
         errno = EBADF;
         return -1;
     }
-
     return f->clazz->_fh_lseek(f, pos, where);
 }
 
@@ -644,7 +638,7 @@
     return 0;
 }
 
-static int _fh_socket_lseek(FH f, int pos, int origin) {
+static int64_t _fh_socket_lseek(FH f, int64_t pos, int origin) {
     errno = EPIPE;
     return -1;
 }
diff --git a/adb/test_device.py b/adb/test_device.py
index 9f45115..c3166ff 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -751,7 +751,7 @@
                 shutil.rmtree(host_dir)
 
     def test_push_empty(self):
-        """Push a directory containing an empty directory to the device."""
+        """Push an empty directory to the device."""
         self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
         self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
 
@@ -767,9 +767,10 @@
 
             self.device.push(empty_dir_path, self.DEVICE_TEMP_DIR)
 
-            test_empty_cmd = ['[', '-d',
-                              os.path.join(self.DEVICE_TEMP_DIR, 'empty')]
+            remote_path = os.path.join(self.DEVICE_TEMP_DIR, "empty")
+            test_empty_cmd = ["[", "-d", remote_path, "]"]
             rc, _, _ = self.device.shell_nocheck(test_empty_cmd)
+
             self.assertEqual(rc, 0)
             self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
         finally:
diff --git a/base/Android.bp b/base/Android.bp
index 3d80d97..daa820a 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -56,6 +56,7 @@
         "test_utils.cpp",
     ],
 
+    cppflags: ["-Wexit-time-destructors"],
     shared_libs: ["liblog"],
     target: {
         android: {
@@ -68,13 +69,11 @@
             srcs: [
                 "errors_unix.cpp",
             ],
-            cppflags: ["-Wexit-time-destructors"],
         },
         darwin: {
             srcs: [
                 "errors_unix.cpp",
             ],
-            cppflags: ["-Wexit-time-destructors"],
         },
         linux_bionic: {
             enabled: true,
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index c17e00f..6700b6c 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -236,7 +236,7 @@
     {"reboot,rescueparty", 90},
     {"charge", 91},
     {"oem_tz_crash", 92},
-    {"uvlo", 93},
+    {"uvlo", 93},  // aliasReasons converts to reboot,undervoltage
     {"oem_ps_hold", 94},
     {"abnormal_reset", 95},
     {"oemerr_unknown", 96},
@@ -248,9 +248,9 @@
     {"watchdog_nonsec", 102},
     {"watchdog_apps_bark", 103},
     {"reboot_dmverity_corrupted", 104},
-    {"reboot_smpl", 105},
+    {"reboot_smpl", 105},  // aliasReasons converts to reboot,powerloss
     {"watchdog_sdi_apps_reset", 106},
-    {"smpl", 107},
+    {"smpl", 107},  // aliasReasons converts to reboot,powerloss
     {"oem_modem_failed_to_powerup", 108},
     {"reboot_normal", 109},
     {"oem_lpass_cfg", 110},
@@ -262,8 +262,8 @@
     {"oem_rpm_undef_error", 116},
     {"oem_crash_on_the_lk", 117},
     {"oem_rpm_reset", 118},
-    {"REUSE1", 119},  // Former dupe, can be re-used
-    {"REUSE2", 120},  // Former dupe, can be re-used
+    {"reboot,powerloss", 119},
+    {"reboot,undervoltage", 120},
     {"factory_cable", 121},
     {"oem_ar6320_failed_to_powerup", 122},
     {"watchdog_rpm_bite", 123},
@@ -840,6 +840,8 @@
         {"reboot,tool", "tool_by_pass_pwk"},
         {"!reboot,longkey", "reboot_longkey"},
         {"!reboot,longkey", "kpdpwr"},
+        {"!reboot,undervoltage", "uvlo"},
+        {"!reboot,powerloss", "smpl"},
         {"bootloader", ""},
     };
 
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 6b175af..50d18ed 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -122,6 +122,7 @@
     shared_libs: [
         "android.hardware.boot@1.0",
         "android.hardware.fastboot@1.0",
+        "android.hardware.health@2.0",
         "libadbd",
         "libasyncio",
         "libbase",
@@ -139,6 +140,10 @@
         "libutils",
     ],
 
+    static_libs: [
+        "libhealthhalutils",
+    ],
+
     cpp_std: "c++17",
 }
 
@@ -202,7 +207,6 @@
     cpp_std: "c++17",
     srcs: [
         "bootimg_utils.cpp",
-        "engine.cpp",
         "fastboot.cpp",
         "fs.cpp",
         "socket.cpp",
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 2a68a2b..2eaf006 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -60,3 +60,6 @@
 #define FB_VAR_IS_LOGICAL "is-logical"
 #define FB_VAR_IS_USERSPACE "is-userspace"
 #define FB_VAR_HW_REVISION "hw-revision"
+#define FB_VAR_VARIANT "variant"
+#define FB_VAR_OFF_MODE_CHARGE_STATE "off-mode-charge"
+#define FB_VAR_BATTERY_VOLTAGE "battery-voltage"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b7cafac..b02d968 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -82,6 +82,7 @@
             {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
             {FB_VAR_PRODUCT, {GetProduct, nullptr}},
             {FB_VAR_SERIALNO, {GetSerial, nullptr}},
+            {FB_VAR_VARIANT, {GetVariant, nullptr}},
             {FB_VAR_SECURE, {GetSecure, nullptr}},
             {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},
             {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},
@@ -94,6 +95,8 @@
             {FB_VAR_PARTITION_TYPE, {GetPartitionType, GetAllPartitionArgsWithSlot}},
             {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},
             {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
+            {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
+            {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
             {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}}};
 
     if (args.size() < 2) {
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 6862741..b843c05 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -20,6 +20,8 @@
 #include <android-base/strings.h>
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <android/hardware/fastboot/1.0/IFastboot.h>
+#include <healthhalutils/HealthHalUtils.h>
+
 #include <algorithm>
 
 #include "constants.h"
@@ -30,6 +32,8 @@
 using ::android::hardware::boot::V1_0::IBootControl;
 using ::android::hardware::boot::V1_0::Slot;
 using ::android::hardware::fastboot::V1_0::IFastboot;
+using ::android::hardware::health::V2_0::get_health_service;
+
 namespace sph = std::placeholders;
 
 FastbootDevice::FastbootDevice()
@@ -52,6 +56,7 @@
       }),
       transport_(std::make_unique<ClientUsbTransport>()),
       boot_control_hal_(IBootControl::getService()),
+      health_hal_(get_health_service()),
       fastboot_hal_(IFastboot::getService()) {}
 
 FastbootDevice::~FastbootDevice() {
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index 189cf80..2eb7177 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -24,6 +24,7 @@
 
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <android/hardware/fastboot/1.0/IFastboot.h>
+#include <android/hardware/health/2.0/IHealth.h>
 
 #include "commands.h"
 #include "transport.h"
@@ -53,12 +54,14 @@
     android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal() {
         return fastboot_hal_;
     }
+    android::sp<android::hardware::health::V2_0::IHealth> health_hal() { return health_hal_; }
 
   private:
     const std::unordered_map<std::string, CommandHandler> kCommandMap;
 
     std::unique_ptr<Transport> transport_;
     android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
+    android::sp<android::hardware::health::V2_0::IHealth> health_hal_;
     android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal_;
     std::vector<char> download_data_;
 };
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 002e043..01415d7 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -24,6 +24,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <ext4_utils/ext4_utils.h>
+#include <healthhalutils/HealthHalUtils.h>
 
 #include "fastboot_device.h"
 #include "flashing.h"
@@ -74,6 +75,73 @@
     return true;
 }
 
+bool GetVariant(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                std::string* message) {
+    auto fastboot_hal = device->fastboot_hal();
+    if (!fastboot_hal) {
+        *message = "Fastboot HAL not found";
+        return false;
+    }
+
+    Result ret;
+    auto ret_val = fastboot_hal->getVariant([&](std::string device_variant, Result result) {
+        *message = device_variant;
+        ret = result;
+    });
+    if (!ret_val.isOk() || ret.status != Status::SUCCESS) {
+        *message = "Unable to get device variant";
+        return false;
+    }
+
+    return true;
+}
+
+bool GetOffModeChargeState(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                           std::string* message) {
+    auto fastboot_hal = device->fastboot_hal();
+    if (!fastboot_hal) {
+        *message = "Fastboot HAL not found";
+        return false;
+    }
+
+    Result ret;
+    auto ret_val =
+            fastboot_hal->getOffModeChargeState([&](bool off_mode_charging_state, Result result) {
+                *message = off_mode_charging_state ? "1" : "0";
+                ret = result;
+            });
+    if (!ret_val.isOk() || (ret.status != Status::SUCCESS)) {
+        *message = "Unable to get off mode charge state";
+        return false;
+    }
+
+    return true;
+}
+
+bool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                       std::string* message) {
+    using android::hardware::health::V2_0::HealthInfo;
+    using android::hardware::health::V2_0::Result;
+
+    auto health_hal = device->health_hal();
+    if (!health_hal) {
+        *message = "Health HAL not found";
+        return false;
+    }
+
+    Result ret;
+    auto ret_val = health_hal->getHealthInfo([&](Result result, HealthInfo info) {
+        *message = std::to_string(info.legacy.batteryVoltage);
+        ret = result;
+    });
+    if (!ret_val.isOk() || (ret != Result::SUCCESS)) {
+        *message = "Unable to get battery voltage";
+        return false;
+    }
+
+    return true;
+}
+
 bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& /* args */,
                     std::string* message) {
     std::string suffix = device->GetCurrentSlot();
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 63f2670..e7c3c7c 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -52,7 +52,11 @@
                     std::string* message);
 bool GetHardwareRevision(FastbootDevice* device, const std::vector<std::string>& args,
                          std::string* message);
-
+bool GetVariant(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetOffModeChargeState(FastbootDevice* device, const std::vector<std::string>& args,
+                           std::string* message);
+bool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& args,
+                       std::string* message);
 // Helpers for getvar all.
 std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
 std::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device);
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
deleted file mode 100644
index a43e7a6..0000000
--- a/fastboot/engine.cpp
+++ /dev/null
@@ -1,197 +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.
- */
-#include "engine.h"
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#include <vector>
-
-#include <android-base/stringprintf.h>
-
-#include "constants.h"
-#include "transport.h"
-
-using android::base::StringPrintf;
-
-static fastboot::FastBootDriver* fb = nullptr;
-
-void fb_init(fastboot::FastBootDriver& fbi) {
-    fb = &fbi;
-    auto cb = [](std::string& info) { fprintf(stderr, "(bootloader) %s\n", info.c_str()); };
-    fb->SetInfoCallback(cb);
-}
-
-void fb_reinit(Transport* transport) {
-    if (Transport* old_transport = fb->set_transport(transport)) {
-        delete old_transport;
-    }
-}
-
-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 void HandleResult(double start, int status) {
-    if (status) {
-        fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
-        die("Command failed");
-    } else {
-        double split = now();
-        fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
-    }
-}
-
-#define RUN_COMMAND(command)         \
-    {                                \
-        double start = now();        \
-        auto status = (command);     \
-        HandleResult(start, status); \
-    }
-
-void fb_set_active(const std::string& slot) {
-    Status("Setting current slot to '" + slot + "'");
-    RUN_COMMAND(fb->SetActive(slot));
-}
-
-void fb_erase(const std::string& partition) {
-    Status("Erasing '" + partition + "'");
-    RUN_COMMAND(fb->Erase(partition));
-}
-
-void fb_flash_fd(const std::string& partition, int fd, uint32_t sz) {
-    Status(StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024));
-    RUN_COMMAND(fb->Download(fd, sz));
-
-    Status("Writing '" + partition + "'");
-    RUN_COMMAND(fb->Flash(partition));
-}
-
-void fb_flash(const std::string& partition, const std::vector<char>& data) {
-    Status(StringPrintf("Sending '%s' (%zu KB)", partition.c_str(), data.size() / 1024));
-    RUN_COMMAND(fb->Download(data));
-
-    Status("Writing '" + partition + "'");
-    RUN_COMMAND(fb->Flash(partition));
-}
-
-void fb_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
-                     size_t current, size_t total) {
-    Status(StringPrintf("Sending sparse '%s' %zu/%zu (%u KB)", partition.c_str(), current, total,
-                        sz / 1024));
-    RUN_COMMAND(fb->Download(s));
-
-    Status(StringPrintf("Writing sparse '%s' %zu/%zu", partition.c_str(), current, total));
-    RUN_COMMAND(fb->Flash(partition));
-}
-
-void fb_create_partition(const std::string& partition, const std::string& size) {
-    Status("Creating '" + partition + "'");
-    RUN_COMMAND(fb->RawCommand(FB_CMD_CREATE_PARTITION ":" + partition + ":" + size));
-}
-
-void fb_delete_partition(const std::string& partition) {
-    Status("Deleting '" + partition + "'");
-    RUN_COMMAND(fb->RawCommand(FB_CMD_DELETE_PARTITION ":" + partition));
-}
-
-void fb_resize_partition(const std::string& partition, const std::string& size) {
-    Status("Resizing '" + partition + "'");
-    RUN_COMMAND(fb->RawCommand(FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size));
-}
-
-void fb_display(const std::string& label, const std::string& var) {
-    std::string value;
-    auto status = fb->GetVar(var, &value);
-
-    if (status) {
-        fprintf(stderr, "getvar:%s FAILED (%s)\n", var.c_str(), fb->Error().c_str());
-        return;
-    }
-    fprintf(stderr, "%s: %s\n", label.c_str(), value.c_str());
-}
-
-void fb_reboot() {
-    fprintf(stderr, "Rebooting");
-    fb->Reboot();
-    fprintf(stderr, "\n");
-}
-
-void fb_command(const std::string& cmd, const std::string& msg) {
-    Status(msg);
-    RUN_COMMAND(fb->RawCommand(cmd));
-}
-
-void fb_download(const std::string& name, const std::vector<char>& data) {
-    Status("Downloading '" + name + "'");
-    RUN_COMMAND(fb->Download(data));
-}
-
-void fb_download_fd(const std::string& name, int fd, uint32_t sz) {
-    Status(StringPrintf("Sending '%s' (%u KB)", name.c_str(), sz / 1024));
-    RUN_COMMAND(fb->Download(fd, sz));
-}
-
-void fb_upload(const std::string& outfile) {
-    Status("Uploading '" + outfile + "'");
-    RUN_COMMAND(fb->Upload(outfile));
-}
-
-void fb_notice(const std::string& notice) {
-    Status(notice);
-    fprintf(stderr, "\n");
-}
-
-void fb_wait_for_disconnect() {
-    fb->WaitForDisconnect();
-}
-
-bool fb_reboot_to_userspace() {
-    Status("Rebooting to userspace fastboot");
-    verbose("\n");
-
-    if (fb->RebootTo("fastboot") != fastboot::RetCode::SUCCESS) {
-        fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
-        return false;
-    }
-    fprintf(stderr, "OKAY\n");
-
-    fb_reinit(nullptr);
-    return true;
-}
diff --git a/fastboot/engine.h b/fastboot/engine.h
deleted file mode 100644
index b681f5a..0000000
--- a/fastboot/engine.h
+++ /dev/null
@@ -1,79 +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.
- */
-
-#pragma once
-
-#include <inttypes.h>
-#include <stdlib.h>
-
-#include <string>
-
-#include <bootimg.h>
-#include "fastboot_driver.h"
-#include "util.h"
-
-#include "constants.h"
-
-class Transport;
-struct sparse_file;
-
-const std::string fb_get_error();
-
-void fb_init(fastboot::FastBootDriver& fbi);
-void fb_reinit(Transport* transport);
-
-bool fb_getvar(const std::string& key, std::string* value);
-void fb_flash(const std::string& partition, const std::vector<char>& data);
-void fb_flash_fd(const std::string& partition, int fd, uint32_t sz);
-void fb_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
-                     size_t current, size_t total);
-void fb_erase(const std::string& partition);
-void fb_display(const std::string& label, const std::string& var);
-void fb_reboot();
-void fb_command(const std::string& cmd, const std::string& msg);
-void fb_download(const std::string& name, const std::vector<char>& data);
-void fb_download_fd(const std::string& name, int fd, uint32_t sz);
-void fb_upload(const std::string& outfile);
-void fb_notice(const std::string& notice);
-void fb_wait_for_disconnect(void);
-void fb_create_partition(const std::string& partition, const std::string& size);
-void fb_delete_partition(const std::string& partition);
-void fb_resize_partition(const std::string& partition, const std::string& size);
-void fb_set_active(const std::string& slot);
-bool fb_reboot_to_userspace();
-
-/* Current product */
-extern char cur_product[FB_RESPONSE_SZ + 1];
-
-class FastBootTool {
-  public:
-    int Main(int argc, char* argv[]);
-
-    void ParseOsPatchLevel(boot_img_hdr_v1*, const char*);
-    void ParseOsVersion(boot_img_hdr_v1*, const char*);
-};
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 5173bab..5962650 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -26,6 +26,8 @@
  * SUCH DAMAGE.
  */
 
+#include "fastboot.h"
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -63,12 +65,13 @@
 
 #include "bootimg_utils.h"
 #include "diagnose_usb.h"
-#include "engine.h"
+#include "fastboot_driver.h"
 #include "fs.h"
 #include "tcp.h"
 #include "transport.h"
 #include "udp.h"
 #include "usb.h"
+#include "util.h"
 
 using android::base::ReadFully;
 using android::base::Split;
@@ -98,6 +101,8 @@
 
 static const std::string convert_fbe_marker_filename("convert_fbe");
 
+fastboot::FastBootDriver* fb = nullptr;
+
 enum fb_buffer_type {
     FB_BUFFER_FD,
     FB_BUFFER_SPARSE,
@@ -179,6 +184,28 @@
     return "";
 }
 
+double last_start_time;
+
+static void Status(const std::string& message) {
+    static constexpr char kStatusFormat[] = "%-50s ";
+    fprintf(stderr, kStatusFormat, message.c_str());
+    last_start_time = now();
+}
+
+static void Epilog(int status) {
+    if (status) {
+        fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
+        die("Command failed");
+    } else {
+        double split = now();
+        fprintf(stderr, "OKAY [%7.3fs]\n", (split - last_start_time));
+    }
+}
+
+static void InfoMessage(const std::string& info) {
+    fprintf(stderr, "(bootloader) %s\n", info.c_str());
+}
+
 static int64_t get_file_size(int fd) {
     struct stat sb;
     if (fstat(fd, &sb) == -1) {
@@ -606,9 +633,10 @@
     }
 
     std::string var_value;
-    if (!fb_getvar(var, &var_value)) {
+    if (fb->GetVar(var, &var_value) != fastboot::SUCCESS) {
         fprintf(stderr, "FAILED\n\n");
-        fprintf(stderr, "Could not getvar for '%s' (%s)\n\n", var.c_str(), fb_get_error().c_str());
+        fprintf(stderr, "Could not getvar for '%s' (%s)\n\n", var.c_str(),
+                fb->Error().c_str());
         die("requirements not met!");
     }
 
@@ -686,7 +714,7 @@
 static void HandlePartitionExists(const std::vector<std::string>& options) {
     const std::string& partition_name = options[0];
     std::string has_slot;
-    if (!fb_getvar("has-slot:" + partition_name, &has_slot) ||
+    if (fb->GetVar("has-slot:" + partition_name, &has_slot) != fastboot::SUCCESS ||
         (has_slot != "yes" && has_slot != "no")) {
         die("device doesn't have required partition %s!", partition_name.c_str());
     }
@@ -705,8 +733,8 @@
 
 static void CheckRequirements(const std::string& data) {
     std::string cur_product;
-    if (!fb_getvar("product", &cur_product)) {
-        fprintf(stderr, "getvar:product FAILED (%s)\n", fb_get_error().c_str());
+    if (fb->GetVar("product", &cur_product) != fastboot::SUCCESS) {
+        fprintf(stderr, "getvar:product FAILED (%s)\n", fb->Error().c_str());
     }
 
     auto lines = Split(data, "\n");
@@ -732,12 +760,24 @@
     }
 }
 
-static void dump_info() {
-    fb_notice("--------------------------------------------");
-    fb_display("Bootloader Version...", "version-bootloader");
-    fb_display("Baseband Version.....", "version-baseband");
-    fb_display("Serial Number........", "serialno");
-    fb_notice("--------------------------------------------");
+static void DisplayVarOrError(const std::string& label, const std::string& var) {
+    std::string value;
+
+    if (fb->GetVar(var, &value) != fastboot::SUCCESS) {
+        Status("getvar:" + var);
+        fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
+        return;
+    }
+    fprintf(stderr, "%s: %s\n", label.c_str(), value.c_str());
+}
+
+static void DumpInfo() {
+    fprintf(stderr, "--------------------------------------------\n");
+    DisplayVarOrError("Bootloader Version...", "version-bootloader");
+    DisplayVarOrError("Baseband Version.....", "version-baseband");
+    DisplayVarOrError("Serial Number........", "serialno");
+    fprintf(stderr, "--------------------------------------------\n");
+
 }
 
 static struct sparse_file** load_sparse_files(int fd, int64_t max_size) {
@@ -762,7 +802,7 @@
 
 static int64_t get_target_sparse_limit() {
     std::string max_download_size;
-    if (!fb_getvar("max-download-size", &max_download_size) ||
+    if (fb->GetVar("max-download-size", &max_download_size) != fastboot::SUCCESS ||
         max_download_size.empty()) {
         verbose("target didn't report max-download-size");
         return 0;
@@ -910,12 +950,12 @@
 
             for (size_t i = 0; i < sparse_files.size(); ++i) {
                 const auto& pair = sparse_files[i];
-                fb_flash_sparse(partition, pair.first, pair.second, i + 1, sparse_files.size());
+                fb->FlashPartition(partition, pair.first, pair.second, i + 1, sparse_files.size());
             }
             break;
         }
         case FB_BUFFER_FD:
-            fb_flash_fd(partition, buf->fd, buf->sz);
+            fb->FlashPartition(partition, buf->fd, buf->sz);
             break;
         default:
             die("unknown buffer type: %d", buf->type);
@@ -924,14 +964,15 @@
 
 static std::string get_current_slot() {
     std::string current_slot;
-    if (!fb_getvar("current-slot", &current_slot)) return "";
+    if (fb->GetVar("current-slot", &current_slot) != fastboot::SUCCESS) return "";
     return current_slot;
 }
 
 static int get_slot_count() {
     std::string var;
     int count = 0;
-    if (!fb_getvar("slot-count", &var) || !android::base::ParseInt(var, &count)) {
+    if (fb->GetVar("slot-count", &var) != fastboot::SUCCESS ||
+        !android::base::ParseInt(var, &count)) {
         return 0;
     }
     return count;
@@ -1006,7 +1047,7 @@
     std::string has_slot;
     std::string current_slot;
 
-    if (!fb_getvar("has-slot:" + part, &has_slot)) {
+    if (fb->GetVar("has-slot:" + part, &has_slot) != fastboot::SUCCESS) {
         /* If has-slot is not supported, the answer is no. */
         has_slot = "no";
     }
@@ -1039,7 +1080,7 @@
     std::string has_slot;
 
     if (slot == "all") {
-        if (!fb_getvar("has-slot:" + part, &has_slot)) {
+        if (fb->GetVar("has-slot:" + part, &has_slot) != fastboot::SUCCESS) {
             die("Could not check if partition %s has slot %s", part.c_str(), slot.c_str());
         }
         if (has_slot == "yes") {
@@ -1069,25 +1110,25 @@
     if (!supports_AB()) return;
 
     if (slot_override != "") {
-        fb_set_active(slot_override);
+        fb->SetActive(slot_override);
     } else {
         std::string current_slot = get_current_slot();
         if (current_slot != "") {
-            fb_set_active(current_slot);
+            fb->SetActive(current_slot);
         }
     }
 }
 
 static bool is_userspace_fastboot() {
     std::string value;
-    return fb_getvar("is-userspace", &value) && value == "yes";
+    return fb->GetVar("is-userspace", &value) == fastboot::SUCCESS && value == "yes";
 }
 
 static bool if_partition_exists(const std::string& partition, const std::string& slot) {
     std::string has_slot;
     std::string partition_name = partition;
 
-    if (fb_getvar("has-slot:" + partition, &has_slot) && has_slot == "yes") {
+    if (fb->GetVar("has-slot:" + partition, &has_slot) == fastboot::SUCCESS && has_slot == "yes") {
         if (slot == "") {
             std::string current_slot = get_current_slot();
             if (current_slot == "") {
@@ -1099,23 +1140,24 @@
         }
     }
     std::string partition_size;
-    return fb_getvar("partition-size:" + partition_name, &partition_size);
+    return fb->GetVar("partition-size:" + partition_name, &partition_size) == fastboot::SUCCESS;
 }
 
 static bool is_logical(const std::string& partition) {
     std::string value;
-    return fb_getvar("is-logical:" + partition, &value) && value == "yes";
+    return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
 }
 
 static void reboot_to_userspace_fastboot() {
-    if (!fb_reboot_to_userspace()) {
-        die("Must reboot to userspace fastboot to flash logical partitions");
-    }
+    fb->RebootTo("fastboot");
+
+    auto* old_transport = fb->set_transport(nullptr);
+    delete old_transport;
 
     // Give the current connection time to close.
     std::this_thread::sleep_for(std::chrono::milliseconds(1000));
 
-    fb_reinit(open_device());
+    fb->set_transport(open_device());
 }
 
 class ImageSource {
@@ -1156,6 +1198,7 @@
 }
 
 void FlashAllTool::Flash() {
+    DumpInfo();
     CheckRequirements();
     DetermineSecondarySlot();
     CollectImages();
@@ -1172,7 +1215,7 @@
     for (const auto& [image, slot] : os_images_) {
         auto resize_partition = [](const std::string& partition) -> void {
             if (is_logical(partition)) {
-                fb_resize_partition(partition, "0");
+                fb->ResizePartition(partition, "0");
             }
         };
         do_for_partitions(image->part_name, slot, resize_partition, false);
@@ -1248,12 +1291,12 @@
     auto flash = [&, this](const std::string& partition_name) {
         std::vector<char> signature_data;
         if (source_.ReadFile(image.sig_name, &signature_data)) {
-            fb_download("signature", signature_data);
-            fb_command("signature", "installing signature");
+            fb->Download("signature", signature_data);
+            fb->RawCommand("signature", "installing signature");
         }
 
         if (is_logical(partition_name)) {
-            fb_resize_partition(partition_name, std::to_string(buf->image_size));
+            fb->ResizePartition(partition_name, std::to_string(buf->image_size));
         }
         flash_buf(partition_name.c_str(), buf);
     };
@@ -1272,13 +1315,13 @@
     if (!is_userspace_fastboot()) {
         reboot_to_userspace_fastboot();
     }
-    fb_download_fd("super", fd, get_file_size(fd));
+    fb->Download("super", fd, get_file_size(fd));
 
     std::string command = "update-super:super";
     if (wipe_) {
         command += ":wipe";
     }
-    fb_command(command, "Updating super partition");
+    fb->RawCommand(command, "Updating super partition");
 }
 
 class ZipImageSource final : public ImageSource {
@@ -1300,8 +1343,6 @@
 }
 
 static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
-    dump_info();
-
     ZipArchiveHandle zip;
     int error = OpenArchive(filename, &zip);
     if (error != 0) {
@@ -1334,9 +1375,6 @@
 }
 
 static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) {
-    std::string fname;
-    dump_info();
-
     FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe);
     tool.Flash();
 }
@@ -1355,7 +1393,7 @@
     while (!args->empty()) {
         command += " " + next_arg(args);
     }
-    fb_command(command, "");
+    fb->RawCommand(command, "");
 }
 
 static std::string fb_fix_numeric_var(std::string var) {
@@ -1369,7 +1407,7 @@
 
 static unsigned fb_get_flash_block_size(std::string name) {
     std::string sizeString;
-    if (!fb_getvar(name, &sizeString) || sizeString.empty()) {
+    if (fb->GetVar(name, &sizeString) != fastboot::SUCCESS || sizeString.empty()) {
         // This device does not report flash block sizes, so return 0.
         return 0;
     }
@@ -1407,7 +1445,7 @@
         limit = sparse_limit;
     }
 
-    if (!fb_getvar("partition-type:" + partition, &partition_type)) {
+    if (fb->GetVar("partition-type:" + partition, &partition_type) != fastboot::SUCCESS) {
         errMsg = "Can't determine partition type.\n";
         goto failed;
     }
@@ -1419,7 +1457,7 @@
         partition_type = type_override;
     }
 
-    if (!fb_getvar("partition-size:" + partition, &partition_size)) {
+    if (fb->GetVar("partition-size:" + partition, &partition_size) != fastboot::SUCCESS) {
         errMsg = "Unable to get partition size\n";
         goto failed;
     }
@@ -1477,7 +1515,7 @@
         fprintf(stderr, "Erase successful, but not automatically formatting.\n");
         if (errMsg) fprintf(stderr, "%s", errMsg);
     }
-    fprintf(stderr, "FAILED (%s)\n", fb_get_error().c_str());
+    fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
 }
 
 int FastBootTool::Main(int argc, char* argv[]) {
@@ -1627,8 +1665,13 @@
     if (transport == nullptr) {
         return 1;
     }
-    fastboot::FastBootDriver fb(transport);
-    fb_init(fb);
+    fastboot::DriverCallbacks driver_callbacks = {
+        .prolog = Status,
+        .epilog = Epilog,
+        .info = InfoMessage,
+    };
+    fastboot::FastBootDriver fastboot_driver(transport, driver_callbacks, false);
+    fb = &fastboot_driver;
 
     const double start = now();
 
@@ -1639,7 +1682,7 @@
         if (next_active == "") {
             if (slot_override == "") {
                 std::string current_slot;
-                if (fb_getvar("current-slot", &current_slot)) {
+                if (fb->GetVar("current-slot", &current_slot) == fastboot::SUCCESS) {
                     next_active = verify_slot(current_slot, false);
                 } else {
                     wants_set_active = false;
@@ -1656,19 +1699,18 @@
 
         if (command == "getvar") {
             std::string variable = next_arg(&args);
-            fb_display(variable, variable);
+            DisplayVarOrError(variable, variable);
         } else if (command == "erase") {
             std::string partition = next_arg(&args);
             auto erase = [&](const std::string& partition) {
                 std::string partition_type;
-                if (fb_getvar(std::string("partition-type:") + partition,
-                              &partition_type) &&
+                if (fb->GetVar("partition-type:" + partition, &partition_type) == fastboot::SUCCESS &&
                     fs_get_generator(partition_type) != nullptr) {
                     fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
                             partition_type.c_str());
                 }
 
-                fb_erase(partition);
+                fb->Erase(partition);
             };
             do_for_partitions(partition, slot_override, erase, true);
         } else if (android::base::StartsWith(command, "format")) {
@@ -1698,8 +1740,8 @@
                 die("could not load '%s': %s", filename.c_str(), strerror(errno));
             }
             if (data.size() != 256) die("signature must be 256 bytes (got %zu)", data.size());
-            fb_download("signature", data);
-            fb_command("signature", "installing signature");
+            fb->Download("signature", data);
+            fb->RawCommand("signature", "installing signature");
         } else if (command == "reboot") {
             wants_reboot = true;
 
@@ -1727,7 +1769,7 @@
         } else if (command == "reboot-fastboot") {
             wants_reboot_fastboot = true;
         } else if (command == "continue") {
-            fb_command("continue", "resuming boot");
+            fb->Continue();
         } else if (command == "boot") {
             std::string kernel = next_arg(&args);
             std::string ramdisk;
@@ -1736,8 +1778,8 @@
             if (!args.empty()) second_stage = next_arg(&args);
 
             auto data = LoadBootableImage(kernel, ramdisk, second_stage);
-            fb_download("boot.img", data);
-            fb_command("boot", "booting");
+            fb->Download("boot.img", data);
+            fb->Boot();
         } else if (command == "flash") {
             std::string pname = next_arg(&args);
 
@@ -1763,7 +1805,7 @@
 
             auto data = LoadBootableImage(kernel, ramdisk, second_stage);
             auto flashraw = [&data](const std::string& partition) {
-                fb_flash(partition, data);
+                fb->FlashPartition(partition, data);
             };
             do_for_partitions(partition, slot_override, flashraw, true);
         } else if (command == "flashall") {
@@ -1787,7 +1829,7 @@
             wants_reboot = true;
         } else if (command == "set_active") {
             std::string slot = verify_slot(next_arg(&args), false);
-            fb_set_active(slot);
+            fb->SetActive(slot);
         } else if (command == "stage") {
             std::string filename = next_arg(&args);
 
@@ -1795,10 +1837,10 @@
             if (!load_buf(filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
                 die("cannot load '%s'", filename.c_str());
             }
-            fb_download_fd(filename, buf.fd, buf.sz);
+            fb->Download(filename, buf.fd, buf.sz);
         } else if (command == "get_staged") {
             std::string filename = next_arg(&args);
-            fb_upload(filename);
+            fb->Upload(filename);
         } else if (command == "oem") {
             do_oem_command("oem", &args);
         } else if (command == "flashing") {
@@ -1815,14 +1857,14 @@
         } else if (command == "create-logical-partition") {
             std::string partition = next_arg(&args);
             std::string size = next_arg(&args);
-            fb_create_partition(partition, size);
+            fb->CreatePartition(partition, size);
         } else if (command == "delete-logical-partition") {
             std::string partition = next_arg(&args);
-            fb_delete_partition(partition);
+            fb->DeletePartition(partition);
         } else if (command == "resize-logical-partition") {
             std::string partition = next_arg(&args);
             std::string size = next_arg(&args);
-            fb_resize_partition(partition, size);
+            fb->ResizePartition(partition, size);
         } else {
             syntax_error("unknown command %s", command.c_str());
         }
@@ -1832,9 +1874,11 @@
         std::vector<std::string> partitions = { "userdata", "cache", "metadata" };
         for (const auto& partition : partitions) {
             std::string partition_type;
-            if (!fb_getvar(std::string{"partition-type:"} + partition, &partition_type)) continue;
+            if (fb->GetVar("partition-type:" + partition, &partition_type) != fastboot::SUCCESS) {
+                continue;
+            }
             if (partition_type.empty()) continue;
-            fb_erase(partition);
+            fb->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();
@@ -1846,27 +1890,27 @@
         }
     }
     if (wants_set_active) {
-        fb_set_active(next_active);
+        fb->SetActive(next_active);
     }
     if (wants_reboot && !skip_reboot) {
-        fb_reboot();
-        fb_wait_for_disconnect();
+        fb->Reboot();
+        fb->WaitForDisconnect();
     } else if (wants_reboot_bootloader) {
-        fb_command("reboot-bootloader", "rebooting into bootloader");
-        fb_wait_for_disconnect();
+        fb->RebootTo("bootloader");
+        fb->WaitForDisconnect();
     } else if (wants_reboot_recovery) {
-        fb_command("reboot-recovery", "rebooting into recovery");
-        fb_wait_for_disconnect();
+        fb->RebootTo("recovery");
+        fb->WaitForDisconnect();
     } else if (wants_reboot_fastboot) {
-        fb_command("reboot-fastboot", "rebooting into fastboot");
-        fb_wait_for_disconnect();
+        fb->RebootTo("fastboot");
+        fb->WaitForDisconnect();
     }
 
     fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
 
-    if (Transport* old_transport = fb.set_transport(nullptr)) {
-        delete old_transport;
-    }
+    auto* old_transport = fb->set_transport(nullptr);
+    delete old_transport;
+
     return 0;
 }
 
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
new file mode 100644
index 0000000..9f18253
--- /dev/null
+++ b/fastboot/fastboot.h
@@ -0,0 +1,37 @@
+/*
+ * 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 <bootimg.h>
+
+class FastBootTool {
+  public:
+    int Main(int argc, char* argv[]);
+
+    void ParseOsPatchLevel(boot_img_hdr_v1*, const char*);
+    void ParseOsVersion(boot_img_hdr_v1*, const char*);
+};
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 72ba619..b1f3bc9 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -25,6 +25,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
 #include "fastboot_driver.h"
 
 #include <errno.h>
@@ -44,43 +45,56 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <utils/FileMap.h>
-#include "fastboot_driver.h"
+
+#include "constants.h"
 #include "transport.h"
 
+using android::base::StringPrintf;
+
 namespace fastboot {
 
 /*************************** PUBLIC *******************************/
-FastBootDriver::FastBootDriver(Transport* transport, std::function<void(std::string&)> info,
+FastBootDriver::FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks,
                                bool no_checks)
-    : transport_(transport) {
-    info_cb_ = info;
-    disable_checks_ = no_checks;
-}
+    : transport_(transport),
+      prolog_(std::move(driver_callbacks.prolog)),
+      epilog_(std::move(driver_callbacks.epilog)),
+      info_(std::move(driver_callbacks.info)),
+      disable_checks_(no_checks) {}
 
 FastBootDriver::~FastBootDriver() {
 }
 
 RetCode FastBootDriver::Boot(std::string* response, std::vector<std::string>* info) {
-    return RawCommand(Commands::BOOT, response, info);
+    return RawCommand(FB_CMD_BOOT, "Booting", response, info);
 }
 
 RetCode FastBootDriver::Continue(std::string* response, std::vector<std::string>* info) {
-    return RawCommand(Commands::CONTINUE, response, info);
+    return RawCommand(FB_CMD_CONTINUE, "Resuming boot", 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::CreatePartition(const std::string& partition, const std::string& size) {
+    return RawCommand(FB_CMD_CREATE_PARTITION ":" + partition + ":" + size,
+                      "Creating '" + partition + "'");
 }
 
-RetCode FastBootDriver::Flash(const std::string& part, std::string* response,
+RetCode FastBootDriver::DeletePartition(const std::string& partition) {
+    return RawCommand(FB_CMD_DELETE_PARTITION ":" + partition, "Deleting '" + partition + "'");
+}
+
+RetCode FastBootDriver::Erase(const std::string& partition, std::string* response,
                               std::vector<std::string>* info) {
-    return RawCommand(Commands::FLASH + part, response, info);
+    return RawCommand(FB_CMD_ERASE ":" + partition, "Erasing '" + partition + "'", response, info);
+}
+
+RetCode FastBootDriver::Flash(const std::string& partition, std::string* response,
+                              std::vector<std::string>* info) {
+    return RawCommand(FB_CMD_FLASH ":" + partition, "Writing '" + partition + "'", 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);
+    return RawCommand(FB_CMD_GETVAR ":" + key, val, info);
 }
 
 RetCode FastBootDriver::GetVarAll(std::vector<std::string>* response) {
@@ -89,44 +103,52 @@
 }
 
 RetCode FastBootDriver::Reboot(std::string* response, std::vector<std::string>* info) {
-    return RawCommand(Commands::REBOOT, response, info);
+    return RawCommand(FB_CMD_REBOOT, "Rebooting", response, info);
 }
 
 RetCode FastBootDriver::RebootTo(std::string target, std::string* response,
                                  std::vector<std::string>* info) {
-    return RawCommand("reboot-" + target, response, info);
+    return RawCommand("reboot-" + target, "Rebooting into " + target, response, info);
+}
+
+RetCode FastBootDriver::ResizePartition(const std::string& partition, const std::string& size) {
+    return RawCommand(FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size,
+                      "Resizing '" + partition + "'");
 }
 
 RetCode FastBootDriver::SetActive(const std::string& slot, std::string* response,
                                   std::vector<std::string>* info) {
-    return RawCommand(Commands::SET_ACTIVE + slot, response, info);
+    return RawCommand(FB_CMD_SET_ACTIVE ":" + slot, "Setting current slot to '" + slot + "'",
+                      response, info);
 }
 
-RetCode FastBootDriver::FlashPartition(const std::string& part, const std::vector<char>& data) {
+RetCode FastBootDriver::FlashPartition(const std::string& partition,
+                                       const std::vector<char>& data) {
     RetCode ret;
-    if ((ret = Download(data))) {
+    if ((ret = Download(partition, data))) {
         return ret;
     }
-    return RawCommand(Commands::FLASH + part);
+    return Flash(partition);
 }
 
-RetCode FastBootDriver::FlashPartition(const std::string& part, int fd, uint32_t sz) {
+RetCode FastBootDriver::FlashPartition(const std::string& partition, int fd, uint32_t size) {
     RetCode ret;
-    if ((ret = Download(fd, sz))) {
+    if ((ret = Download(partition, fd, size))) {
         return ret;
     }
-    return RawCommand(Commands::FLASH + part);
+    return Flash(partition);
 }
 
-RetCode FastBootDriver::FlashPartition(const std::string& part, sparse_file* s) {
+RetCode FastBootDriver::FlashPartition(const std::string& partition, sparse_file* s, uint32_t size,
+                                       size_t current, size_t total) {
     RetCode ret;
-    if ((ret = Download(s))) {
+    if ((ret = Download(partition, s, size, current, total, false))) {
         return ret;
     }
-    return RawCommand(Commands::FLASH + part);
+    return Flash(partition);
 }
 
-RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint64_t>>* parts) {
+RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint64_t>>* partitions) {
     std::vector<std::string> all;
     RetCode ret;
     if ((ret = GetVarAll(&all))) {
@@ -141,12 +163,20 @@
             std::string m1(sm[1]);
             std::string m2(sm[2]);
             uint64_t tmp = strtoll(m2.c_str(), 0, 16);
-            parts->push_back(std::make_tuple(m1, tmp));
+            partitions->push_back(std::make_tuple(m1, tmp));
         }
     }
     return SUCCESS;
 }
 
+RetCode FastBootDriver::Download(const std::string& name, int fd, size_t size,
+                                 std::string* response, std::vector<std::string>* info) {
+    prolog_(StringPrintf("Sending '%s' (%zu KB)", name.c_str(), size / 1024));
+    auto result = Download(fd, size, response, info);
+    epilog_(result);
+    return result;
+}
+
 RetCode FastBootDriver::Download(int fd, size_t size, std::string* response,
                                  std::vector<std::string>* info) {
     RetCode ret;
@@ -170,6 +200,14 @@
     return HandleResponse(response, info);
 }
 
+RetCode FastBootDriver::Download(const std::string& name, const std::vector<char>& buf,
+                                 std::string* response, std::vector<std::string>* info) {
+    prolog_(StringPrintf("Sending '%s' (%zu KB)", name.c_str(), buf.size() / 1024));
+    auto result = Download(buf, response, info);
+    epilog_(result);
+    return result;
+}
+
 RetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,
                                  std::vector<std::string>* info) {
     RetCode ret;
@@ -192,6 +230,16 @@
     return HandleResponse(response, info);
 }
 
+RetCode FastBootDriver::Download(const std::string& partition, struct sparse_file* s, uint32_t size,
+                                 size_t current, size_t total, bool use_crc, std::string* response,
+                                 std::vector<std::string>* info) {
+    prolog_(StringPrintf("Sending sparse '%s' %zu/%zu (%u KB)", partition.c_str(), current, total,
+                         size / 1024));
+    auto result = Download(s, use_crc, response, info);
+    epilog_(result);
+    return result;
+}
+
 RetCode FastBootDriver::Download(sparse_file* s, bool use_crc, std::string* response,
                                  std::vector<std::string>* info) {
     error_ = "";
@@ -234,9 +282,17 @@
 
 RetCode FastBootDriver::Upload(const std::string& outfile, std::string* response,
                                std::vector<std::string>* info) {
+    prolog_("Uploading '" + outfile + "'");
+    auto result = UploadInner(outfile, response, info);
+    epilog_(result);
+    return result;
+}
+
+RetCode FastBootDriver::UploadInner(const std::string& outfile, std::string* response,
+                                    std::vector<std::string>* info) {
     RetCode ret;
     int dsize;
-    if ((ret = RawCommand(Commands::UPLOAD, response, info, &dsize))) {
+    if ((ret = RawCommand(FB_CMD_UPLOAD, response, info, &dsize))) {
         error_ = "Upload request failed: " + error_;
         return ret;
     }
@@ -270,8 +326,8 @@
 }
 
 // Helpers
-void FastBootDriver::SetInfoCallback(std::function<void(std::string&)> info) {
-    info_cb_ = info;
+void FastBootDriver::SetInfoCallback(std::function<void(const std::string&)> info) {
+    info_ = info;
 }
 
 const std::string FastBootDriver::RCString(RetCode rc) {
@@ -308,6 +364,15 @@
 }
 
 /****************************** PROTECTED *************************************/
+RetCode FastBootDriver::RawCommand(const std::string& cmd, const std::string& message,
+                                   std::string* response, std::vector<std::string>* info,
+                                   int* dsize) {
+    prolog_(message);
+    auto result = RawCommand(cmd, response, info, dsize);
+    epilog_(result);
+    return result;
+}
+
 RetCode FastBootDriver::RawCommand(const std::string& cmd, std::string* response,
                                    std::vector<std::string>* info, int* dsize) {
     error_ = "";  // Clear any pending error
@@ -327,7 +392,7 @@
 
 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));
+    std::string cmd(android::base::StringPrintf("%s:%08" PRIx32, FB_CMD_DOWNLOAD, size));
     RetCode ret;
     if ((ret = RawCommand(cmd, response, info))) {
         return ret;
@@ -360,7 +425,7 @@
         std::string input(status);
         if (android::base::StartsWith(input, "INFO")) {
             std::string tmp = input.substr(strlen("INFO"));
-            info_cb_(tmp);
+            info_(tmp);
             add_info(std::move(tmp));
         } else if (android::base::StartsWith(input, "OKAY")) {
             set_response(input.substr(strlen("OKAY")));
@@ -393,16 +458,6 @@
     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::REBOOT = "reboot";
-const std::string FastBootDriver::Commands::SET_ACTIVE = "set_active:";
-const std::string FastBootDriver::Commands::UPLOAD = "upload";
-
 /******************************* PRIVATE **************************************/
 RetCode FastBootDriver::SendBuffer(int fd, size_t size) {
     static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 2d45085..62bbe52 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -55,6 +55,12 @@
     TIMEOUT,
 };
 
+struct DriverCallbacks {
+    std::function<void(const std::string&)> prolog = [](const std::string&) {};
+    std::function<void(int)> epilog = [](int) {};
+    std::function<void(const std::string&)> info = [](const std::string&) {};
+};
+
 class FastBootDriver {
     friend class FastBootTest;
 
@@ -63,22 +69,30 @@
     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&) {},
+    FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks = {},
                    bool no_checks = false);
     ~FastBootDriver();
 
     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 CreatePartition(const std::string& partition, const std::string& size);
+    RetCode DeletePartition(const std::string& partition);
+    RetCode Download(const std::string& name, int fd, size_t size, 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::string& name, const std::vector<char>& buf,
+                     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);
+    RetCode Download(const std::string& partition, struct sparse_file* s, uint32_t sz,
+                     size_t current, size_t total, bool use_crc, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
     RetCode Download(sparse_file* s, bool use_crc = false, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
-    RetCode Erase(const std::string& part, std::string* response = nullptr,
+    RetCode Erase(const std::string& partition, std::string* response = nullptr,
                   std::vector<std::string>* info = nullptr);
-    RetCode Flash(const std::string& part, std::string* response = nullptr,
+    RetCode Flash(const std::string& partition, 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);
@@ -86,22 +100,24 @@
     RetCode Reboot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
     RetCode RebootTo(std::string target, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
+    RetCode ResizePartition(const std::string& partition, const std::string& size);
     RetCode SetActive(const std::string& slot, 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);
 
     /* 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 FlashPartition(const std::string& partition, const std::vector<char>& data);
+    RetCode FlashPartition(const std::string& partition, int fd, uint32_t sz);
+    RetCode FlashPartition(const std::string& partition, sparse_file* s, uint32_t sz,
+                           size_t current, size_t total);
 
-    RetCode Partitions(std::vector<std::tuple<std::string, uint64_t>>* parts);
+    RetCode Partitions(std::vector<std::tuple<std::string, uint64_t>>* partitions);
     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);
+    void SetInfoCallback(std::function<void(const std::string&)> info);
     static const std::string RCString(RetCode rc);
     std::string Error();
     RetCode WaitForDisconnect();
@@ -110,7 +126,10 @@
     Transport* set_transport(Transport* transport);
     Transport* transport() const { return transport_; }
 
-    // This is temporarily public for engine.cpp
+    RetCode RawCommand(const std::string& cmd, const std::string& message,
+                       std::string* response = nullptr, std::vector<std::string>* info = nullptr,
+                       int* dsize = nullptr);
+
     RetCode RawCommand(const std::string& cmd, std::string* response = nullptr,
                        std::vector<std::string>* info = nullptr, int* dsize = nullptr);
 
@@ -122,19 +141,6 @@
 
     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 REBOOT;
-        static const std::string SET_ACTIVE;
-        static const std::string UPLOAD;
-    };
-
     Transport* transport_;
 
   private:
@@ -145,10 +151,15 @@
     RetCode ReadBuffer(std::vector<char>& buf);
     RetCode ReadBuffer(void* buf, size_t size);
 
+    RetCode UploadInner(const std::string& outfile, std::string* response = nullptr,
+                        std::vector<std::string>* info = nullptr);
+
     int SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len);
 
     std::string error_;
-    std::function<void(std::string&)> info_cb_;
+    std::function<void(const std::string&)> prolog_;
+    std::function<void(int)> epilog_;
+    std::function<void(const std::string&)> info_;
     bool disable_checks_;
 };
 
diff --git a/fastboot/fastboot_test.cpp b/fastboot/fastboot_test.cpp
index e0bbd56..9c3ab6e 100644
--- a/fastboot/fastboot_test.cpp
+++ b/fastboot/fastboot_test.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "engine.h"
+#include "fastboot.h"
 
 #include <gtest/gtest.h>
 
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index 4da71ca..c18af1d 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -121,8 +121,7 @@
     } else {
         ASSERT_EQ(device_path, cb_scratch);  // The path can not change
     }
-    fb = std::unique_ptr<FastBootDriver>(
-            new FastBootDriver(transport.get(), [](std::string&) {}, true));
+    fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
 }
 
 void FastBootTest::TearDown() {
@@ -204,8 +203,7 @@
             putchar('.');
         }
         device_path = cb_scratch;
-        fb = std::unique_ptr<FastBootDriver>(
-                new FastBootDriver(transport.get(), [](std::string&) {}, true));
+        fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
         if (assert_change) {
             ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
             ASSERT_EQ(resp, unlock ? "yes" : "no")
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 8fb5a6a..90a2e74 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -441,9 +441,6 @@
         ASSERT_TRUE(*sparse) << "Sparse file creation failed on: " << bs;
         EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
         EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
-        EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
-                << "Download sparse with crc failed: " << sparse.Rep();
-        EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
     }
 }
 
@@ -462,9 +459,6 @@
                 << "Adding data failed to sparse file: " << sparse.Rep();
         EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
         EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
-        EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
-                << "Download sparse with crc failed: " << sparse.Rep();
-        EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
     }
 }
 
@@ -474,9 +468,6 @@
     ASSERT_TRUE(*sparse) << "Sparse image creation failed";
     EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
     EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
-            << "Download sparse with crc failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
 }
 
 TEST_F(Conformance, SparseDownload1) {
@@ -487,9 +478,6 @@
             << "Adding data failed to sparse file: " << sparse.Rep();
     EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
     EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
-            << "Download sparse with crc failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
 }
 
 TEST_F(Conformance, SparseDownload2) {
@@ -503,9 +491,6 @@
             << "Adding data failed to sparse file: " << sparse.Rep();
     EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
     EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
-            << "Download sparse with crc failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
 }
 
 TEST_F(Conformance, SparseDownload3) {
@@ -537,9 +522,6 @@
     }
     EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
     EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
-            << "Download sparse with crc failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
 }
 
 TEST_F(Conformance, SparseVersionCheck) {
@@ -559,24 +541,6 @@
     }
 }
 
-TEST_F(Conformance, SparseCRCCheck) {
-    SparseWrapper sparse(4096, 4096);
-    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
-    std::vector<char> buf = RandomBuf(4096);
-    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
-            << "Adding data failed to sparse file: " << sparse.Rep();
-    ASSERT_TRUE(SparseToBuf(*sparse, &buf, true)) << "Sparse buffer creation failed";
-    // Flip a bit in the crc
-    buf.back() = buf.back() ^ 0x01;
-    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
-    ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
-    // It can either reject this download or reject it during flash
-    if (HandleResponse() != DEVICE_FAIL) {
-        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
-                << "Flashing an invalid sparse version should fail " << sparse.Rep();
-    }
-}
-
 TEST_F(UnlockPermissions, Download) {
     std::vector<char> buf{'a', 'o', 's', 'p'};
     EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download 4-byte payload failed";
diff --git a/fastboot/main.cpp b/fastboot/main.cpp
index c3683f7..35f4218 100644
--- a/fastboot/main.cpp
+++ b/fastboot/main.cpp
@@ -26,7 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#include "engine.h"
+#include "fastboot.h"
 
 int main(int argc, char* argv[]) {
     FastBootTool fb;
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index 7d15047..d02b37f 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -69,8 +69,3 @@
     }
     fprintf(stderr, "\n");
 }
-
-void Status(const std::string& message) {
-    static constexpr char kStatusFormat[] = "%-50s ";
-    fprintf(stderr, kStatusFormat, message.c_str());
-}
diff --git a/fastboot/util.h b/fastboot/util.h
index 533d2c7..2535414 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -11,8 +11,6 @@
 double now();
 void set_verbose();
 
-void Status(const std::string& message);
-
 // 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__))
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index c0bef2c..07b2a7a 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -147,10 +147,8 @@
     auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
     if (candidate.empty()) return "";
 
-    auto context = fs_mgr_get_context(mount_point);
-    if (!context.empty()) context = ",rootcontext="s + context;
     return "override_creds=off,"s + kLowerdirOption + mount_point + "," + kUpperdirOption +
-           candidate + kUpperName + ",workdir=" + candidate + kWorkName + context;
+           candidate + kUpperName + ",workdir=" + candidate + kWorkName;
 }
 
 bool fs_mgr_system_root_image(const fstab* fstab) {
@@ -219,6 +217,15 @@
     return false;
 }
 
+bool fs_mgr_overlayfs_verity_enabled(const std::string& basename_mount_point) {
+    auto found = false;
+    fs_mgr_update_verity_state(
+            [&basename_mount_point, &found](fstab_rec*, const char* mount_point, int, int) {
+                if (mount_point && (basename_mount_point == mount_point)) found = true;
+            });
+    return found;
+}
+
 bool fs_mgr_wants_overlayfs(const fstab_rec* fsrec) {
     if (!fsrec) return false;
 
@@ -242,14 +249,7 @@
 
     if (!fs_mgr_overlayfs_enabled(fsrec)) return false;
 
-    // Verity enabled?
-    const auto basename_mount_point(android::base::Basename(fsrec_mount_point));
-    auto found = false;
-    fs_mgr_update_verity_state(
-            [&basename_mount_point, &found](fstab_rec*, const char* mount_point, int, int) {
-                if (mount_point && (basename_mount_point == mount_point)) found = true;
-            });
-    return !found;
+    return !fs_mgr_overlayfs_verity_enabled(android::base::Basename(fsrec_mount_point));
 }
 
 bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr) {
@@ -260,7 +260,7 @@
             errno = save_errno;
             return true;
         }
-        PERROR << "overlayfs open " << path;
+        PERROR << "opendir " << path;
         return false;
     }
     dirent* entry;
@@ -278,7 +278,7 @@
                 if (change) *change = true;
             } else {
                 ret = false;
-                PERROR << "overlayfs rmdir " << file;
+                PERROR << "rmdir " << file;
             }
             continue;
         }
@@ -286,7 +286,7 @@
             if (change) *change = true;
         } else {
             ret = false;
-            PERROR << "overlayfs rm " << file;
+            PERROR << "rm " << file;
         }
     }
     return ret;
@@ -301,14 +301,14 @@
 
     if (setfscreatecon(kOverlayfsFileContext)) {
         ret = false;
-        PERROR << "overlayfs setfscreatecon " << kOverlayfsFileContext;
+        PERROR << "setfscreatecon " << kOverlayfsFileContext;
     }
     auto save_errno = errno;
     if (!mkdir(fsrec_mount_point.c_str(), 0755)) {
         if (change) *change = true;
     } else if (errno != EEXIST) {
         ret = false;
-        PERROR << "overlayfs mkdir " << fsrec_mount_point;
+        PERROR << "mkdir " << fsrec_mount_point;
     } else {
         errno = save_errno;
     }
@@ -318,7 +318,7 @@
         if (change) *change = true;
     } else if (errno != EEXIST) {
         ret = false;
-        PERROR << "overlayfs mkdir " << fsrec_mount_point << kWorkName;
+        PERROR << "mkdir " << fsrec_mount_point << kWorkName;
     } else {
         errno = save_errno;
     }
@@ -327,7 +327,7 @@
     auto new_context = fs_mgr_get_context(mount_point);
     if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
         ret = false;
-        PERROR << "overlayfs setfscreatecon " << new_context;
+        PERROR << "setfscreatecon " << new_context;
     }
     auto upper = fsrec_mount_point + kUpperName;
     save_errno = errno;
@@ -335,7 +335,7 @@
         if (change) *change = true;
     } else if (errno != EEXIST) {
         ret = false;
-        PERROR << "overlayfs mkdir " << upper;
+        PERROR << "mkdir " << upper;
     } else {
         errno = save_errno;
     }
@@ -395,6 +395,15 @@
         }
         if (!duplicate_or_more_specific) mounts.emplace_back(new_mount_point);
     }
+    // if not itemized /system or /, system as root, fake up
+    // fs_mgr_wants_overlayfs evaluation of /system as candidate.
+
+    if ((std::find(mounts.begin(), mounts.end(), "/system") == mounts.end()) &&
+        !fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/") &&
+        !fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/system") &&
+        !fs_mgr_overlayfs_verity_enabled("system")) {
+        mounts.emplace_back("/system");
+    }
     return mounts;
 }
 
@@ -426,7 +435,7 @@
     if (!fs_mgr_wants_overlayfs()) return ret;
     if (!fs_mgr_boot_completed()) {
         errno = EBUSY;
-        PERROR << "overlayfs setup";
+        PERROR << "setup";
         return ret;
     }
 
@@ -437,14 +446,14 @@
     if (fstab && mounts.empty()) return ret;
 
     if (setfscreatecon(kOverlayfsFileContext)) {
-        PERROR << "overlayfs setfscreatecon " << kOverlayfsFileContext;
+        PERROR << "setfscreatecon " << kOverlayfsFileContext;
     }
     auto overlay = kOverlayMountPoint + kOverlayTopDir;
     auto save_errno = errno;
     if (!mkdir(overlay.c_str(), 0755)) {
         if (change) *change = true;
     } else if (errno != EEXIST) {
-        PERROR << "overlayfs mkdir " << overlay;
+        PERROR << "mkdir " << overlay;
     } else {
         errno = save_errno;
     }
@@ -476,7 +485,7 @@
         if (change) *change = true;
     } else if (errno != ENOENT) {
         ret = false;
-        PERROR << "overlayfs mv " << oldpath << " " << newpath;
+        PERROR << "mv " << oldpath << " " << newpath;
     } else {
         errno = save_errno;
     }
@@ -486,7 +495,7 @@
         if (change) *change = true;
     } else if (errno != ENOENT) {
         ret = false;
-        PERROR << "overlayfs rmdir " << newpath;
+        PERROR << "rmdir " << newpath;
     } else {
         errno = save_errno;
     }
@@ -496,7 +505,7 @@
             if (change) *change = true;
         } else if ((errno != ENOENT) && (errno != ENOTEMPTY)) {
             ret = false;
-            PERROR << "overlayfs rmdir " << overlay;
+            PERROR << "rmdir " << overlay;
         } else {
             errno = save_errno;
         }
@@ -511,7 +520,7 @@
     // caller that there may still be more to do.
     if (!fs_mgr_boot_completed()) {
         errno = EBUSY;
-        PERROR << "overlayfs teardown";
+        PERROR << "teardown";
         ret = false;
     }
     return ret;
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 9096f79..80bf84a 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -97,6 +97,7 @@
     android.hardware.health@2.0 \
     android.hardware.health@1.0 \
     android.hardware.health@1.0-convert \
+    libbinderthreadstate \
     libhidltransport \
     libhidlbase \
     libhwbinder_noltopgo \
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 1f4bec1..71a8e0d 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -120,14 +120,18 @@
     return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
 }
 
-static bool IsRecoveryMode() {
+static bool ForceNormalBoot() {
     static bool force_normal_boot = []() {
         std::string cmdline;
         android::base::ReadFileToString("/proc/cmdline", &cmdline);
         return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
     }();
 
-    return !force_normal_boot && access("/system/bin/recovery", F_OK) == 0;
+    return force_normal_boot;
+}
+
+static bool IsRecoveryMode() {
+    return !ForceNormalBoot() && access("/system/bin/recovery", F_OK) == 0;
 }
 
 static inline bool IsDmLinearEnabled() {
@@ -368,11 +372,15 @@
     // this case, we mount system first then pivot to it.  From that point on,
     // we are effectively identical to a system-as-root device.
     auto system_partition =
-            std::find_if(mount_fstab_recs_.begin(), mount_fstab_recs_.end(), [](const auto& rec) {
-                return rec->mount_point == "/system"s ||
-                       rec->mount_point == "/system_recovery_mount"s;
-            });
+            std::find_if(mount_fstab_recs_.begin(), mount_fstab_recs_.end(),
+                         [](const auto& rec) { return rec->mount_point == "/system"s; });
+
     if (system_partition != mount_fstab_recs_.end()) {
+        if (ForceNormalBoot()) {
+            free((*system_partition)->mount_point);
+            (*system_partition)->mount_point = strdup("/system_recovery_mount");
+        }
+
         if (!MountPartition(*system_partition)) {
             return false;
         }
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 0c4a110..40706a1 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -84,6 +84,9 @@
     CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
     CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
 
+    // These below mounts are done in first stage init so that first stage mount can mount
+    // subdirectories of /mnt/{vendor,product}/.  Other mounts, not required by first stage mount,
+    // should be done in rc files.
     // Mount staging areas for devices managed by vold
     // See storage config details at http://source.android.com/devices/storage/
     CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index e7b1728..53653de 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -56,15 +56,24 @@
 /*
  * Use __VA_ARGS__ if running a static analyzer,
  * to avoid warnings of unused variables in __VA_ARGS__.
- * __FAKE_USE_VA_ARGS is undefined at link time,
- * so don't link with __clang_analyzer__ defined.
+ * Use contexpr function in C++ mode, so these macros can be used
+ * in other constexpr functions without warning.
  */
 #ifdef __clang_analyzer__
-extern void __fake_use_va_args(int, ...);
-#define __FAKE_USE_VA_ARGS(...) __fake_use_va_args(0, ##__VA_ARGS__)
+#ifdef __cplusplus
+extern "C++" {
+template <typename... Ts>
+constexpr int __fake_use_va_args(Ts...) {
+  return 0;
+}
+}
+#else
+extern int __fake_use_va_args(int, ...);
+#endif /* __cplusplus */
+#define __FAKE_USE_VA_ARGS(...) ((void)__fake_use_va_args(0, ##__VA_ARGS__))
 #else
 #define __FAKE_USE_VA_ARGS(...) ((void)(0))
-#endif
+#endif /* __clang_analyzer__ */
 
 #ifndef __predict_false
 #define __predict_false(exp) __builtin_expect((exp) != 0, 0)
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index 5c7ec13..92a8325 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -52,7 +52,7 @@
         allocation_bytes_(0),
         roots_(allocator),
         root_vals_(allocator),
-        segv_handler_(allocator),
+        segv_handler_(),
         walking_ptr_(0) {
     valid_allocations_range_.end = 0;
     valid_allocations_range_.begin = ~valid_allocations_range_.end;
diff --git a/libmemunreachable/LeakFolding.cpp b/libmemunreachable/LeakFolding.cpp
index 69f320c..074dc48 100644
--- a/libmemunreachable/LeakFolding.cpp
+++ b/libmemunreachable/LeakFolding.cpp
@@ -57,7 +57,7 @@
 }
 
 void LeakFolding::AccumulateLeaks(SCCInfo* dominator) {
-  std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_, [&](SCCInfo* scc) {
+  std::function<void(SCCInfo*)> walk([&](SCCInfo* scc) {
     if (scc->accumulator != dominator) {
       scc->accumulator = dominator;
       dominator->cuumulative_size += scc->size;
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index ff53fad..9e08a8e 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -32,15 +32,14 @@
  public:
   using Fn = std::function<void(ScopedSignalHandler&, int, siginfo_t*, void*)>;
 
-  explicit ScopedSignalHandler(Allocator<Fn> allocator) : allocator_(allocator), signal_(-1) {}
+  explicit ScopedSignalHandler() : signal_(-1) {}
   ~ScopedSignalHandler() { reset(); }
 
   template <class F>
   void install(int signal, F&& f) {
     if (signal_ != -1) MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed");
 
-    handler_ = SignalFn(std::allocator_arg, allocator_,
-                        [=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
+    handler_ = SignalFn([=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
 
     struct sigaction act {};
     act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) { handler_(signal, si, uctx); };
@@ -68,7 +67,6 @@
  private:
   using SignalFn = std::function<void(int, siginfo_t*, void*)>;
   DISALLOW_COPY_AND_ASSIGN(ScopedSignalHandler);
-  Allocator<Fn> allocator_;
   int signal_;
   struct sigaction old_act_;
   // TODO(ccross): to support multiple ScopedSignalHandlers handler_ would need
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index e6e17ce..1551b5b 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -54,12 +54,12 @@
 // -----------------------------------------------------------------------------
 cc_test {
     name: "metricslogger_tests",
+    isolated: true,
     defaults: ["metricslogger_defaults"],
     shared_libs: [
         "libbase",
         "libmetricslogger_debug",
     ],
-    static_libs: ["libBionicGtestMain"],
     srcs: [
         "metrics_logger_test.cpp",
     ],
diff --git a/mkbootimg/Android.bp b/mkbootimg/Android.bp
index 576a677..c3cf746 100644
--- a/mkbootimg/Android.bp
+++ b/mkbootimg/Android.bp
@@ -31,3 +31,34 @@
     header_libs: ["libmkbootimg_abi_headers"],
     export_header_lib_headers: ["libmkbootimg_abi_headers"],
 }
+
+python_defaults {
+    name: "mkbootimg_defaults",
+
+    version: {
+        py2: {
+            enabled: true,
+            embedded_launcher: true,
+        },
+        py3: {
+            enabled: false,
+            embedded_launcher: false,
+        },
+    },
+}
+
+python_binary_host {
+    name: "mkbootimg",
+    defaults: ["mkbootimg_defaults"],
+    srcs: [
+        "mkbootimg.py",
+    ],
+}
+
+python_binary_host {
+    name: "unpack_bootimg",
+    defaults: ["mkbootimg_defaults"],
+    srcs: [
+        "unpack_bootimg.py",
+    ],
+}
diff --git a/mkbootimg/Android.mk b/mkbootimg/Android.mk
deleted file mode 100644
index 92e1e27..0000000
--- a/mkbootimg/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := mkbootimg
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_IS_HOST_MODULE := true
-
-LOCAL_MODULE := mkbootimg
-
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := unpack_bootimg
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_IS_HOST_MODULE := true
-
-LOCAL_MODULE := unpack_bootimg
-
-include $(BUILD_PREBUILT)
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg.py
old mode 100755
new mode 100644
similarity index 100%
rename from mkbootimg/mkbootimg
rename to mkbootimg/mkbootimg.py
diff --git a/mkbootimg/unpack_bootimg b/mkbootimg/unpack_bootimg.py
old mode 100755
new mode 100644
similarity index 100%
rename from mkbootimg/unpack_bootimg
rename to mkbootimg/unpack_bootimg.py
diff --git a/storaged/Android.bp b/storaged/Android.bp
index 7466728..733b60f 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -62,7 +62,7 @@
         "uid_info.cpp",
         "storaged.proto",
         ":storaged_aidl",
-        "binder/android/os/storaged/IStoragedPrivate.aidl",
+        ":storaged_aidl_private",
     ],
 
     static_libs: ["libhealthhalutils"],
@@ -116,4 +116,13 @@
     srcs: [
         "binder/android/os/IStoraged.aidl",
     ],
+    path: "binder",
+}
+
+filegroup {
+    name: "storaged_aidl_private",
+    srcs: [
+        "binder/android/os/storaged/IStoragedPrivate.aidl",
+    ],
+    path: "binder",
 }
diff --git a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
index 8e3b3b1..0849ee9 100644
--- a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
+++ b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
@@ -21,6 +21,7 @@
 #include <cutils/log.h>
 #include <keymaster/android_keymaster_messages.h>
 #include <trusty_keymaster/TrustyKeymaster3Device.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
 
 using ::keymaster::AbortOperationRequest;
 using ::keymaster::AbortOperationResponse;
@@ -393,20 +394,32 @@
                                             const hidl_vec<KeyParameter>& inParams,
                                             const hidl_vec<uint8_t>& input, update_cb _hidl_cb) {
     UpdateOperationRequest request;
-    request.op_handle = operationHandle;
-    request.input.Reinitialize(input.data(), input.size());
-    request.additional_params.Reinitialize(KmParamSet(inParams));
-
     UpdateOperationResponse response;
-    impl_->UpdateOperation(request, &response);
-
-    uint32_t resultConsumed = 0;
     hidl_vec<KeyParameter> resultParams;
     hidl_vec<uint8_t> resultBlob;
-    if (response.error == KM_ERROR_OK) {
-        resultConsumed = response.input_consumed;
-        resultParams = kmParamSet2Hidl(response.output_params);
-        resultBlob = kmBuffer2hidlVec(response.output);
+    uint32_t resultConsumed = 0;
+
+    request.op_handle = operationHandle;
+    request.additional_params.Reinitialize(KmParamSet(inParams));
+
+    size_t inp_size = input.size();
+    size_t ser_size = request.SerializedSize();
+
+    if (ser_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+        response.error = KM_ERROR_INVALID_INPUT_LENGTH;
+    } else {
+        if (ser_size + inp_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+            inp_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - ser_size;
+        }
+        request.input.Reinitialize(input.data(), inp_size);
+
+        impl_->UpdateOperation(request, &response);
+
+        if (response.error == KM_ERROR_OK) {
+            resultConsumed = response.input_consumed;
+            resultParams = kmParamSet2Hidl(response.output_params);
+            resultBlob = kmBuffer2hidlVec(response.output);
+        }
     }
     _hidl_cb(legacy_enum_conversion(response.error), resultConsumed, resultParams, resultBlob);
     return Void();