Merge "split first stage init into a separate executable"
diff --git a/adb/Android.bp b/adb/Android.bp
index 07f052f..906c5e3 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -277,26 +277,113 @@
     },
 }
 
+// libadbd_core contains the common sources to build libadbd and libadbd_services.
 cc_library_static {
+    name: "libadbd_core",
+    defaults: ["adb_defaults"],
+    recovery_available: true,
+
+    // libminadbd wants both, as it's used to build native tests.
+    compile_multilib: "both",
+
+    srcs: libadb_srcs + libadb_posix_srcs + [
+        "daemon/auth.cpp",
+        "daemon/jdwp_service.cpp",
+        "daemon/usb.cpp",
+    ],
+
+    local_include_dirs: [
+        "daemon/include",
+    ],
+
+    static_libs: [
+        "libdiagnose_usb",
+        "libqemu_pipe",
+    ],
+
+    shared_libs: [
+        "libasyncio",
+        "libbase",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "liblog",
+    ],
+}
+
+cc_library {
+    name: "libadbd_services",
+    defaults: ["adb_defaults"],
+    recovery_available: true,
+    compile_multilib: "both",
+
+    srcs: [
+        "daemon/file_sync_service.cpp",
+        "daemon/framebuffer_service.cpp",
+        "daemon/mdns.cpp",
+        "daemon/remount_service.cpp",
+        "daemon/services.cpp",
+        "daemon/set_verity_enable_state_service.cpp",
+        "daemon/shell_service.cpp",
+        "shell_service_protocol.cpp",
+    ],
+
+    cflags: [
+        "-D_GNU_SOURCE",
+        "-Wno-deprecated-declarations",
+    ],
+
+    static_libs: [
+        "libadbd_core",
+        "libavb_user",
+        "libdiagnose_usb",
+        "libqemu_pipe",
+
+        // `daemon/shell_service.cpp` uses selinux_android_setcon(), which is not exposed by
+        // libselinux.
+        "libselinux",
+    ],
+
+    shared_libs: [
+        "libasyncio",
+        "libbase",
+        "libbootloader_message",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "libext4_utils",
+        "libfec",
+        "libfec_rs",
+        "libfs_mgr",
+        "liblog",
+        "libmdnssd",
+    ],
+}
+
+cc_library {
     name: "libadbd",
     defaults: ["adb_defaults"],
     recovery_available: true,
 
-    // libminadbd wants both, for some reason.
+    // Avoid getting duplicate symbol of android::build::GetBuildNumber().
+    use_version_lib: false,
+
+    // libminadbd wants both, as it's used to build native tests.
     compile_multilib: "both",
-    srcs: libadb_srcs + libadb_posix_srcs + [
-        "daemon/auth.cpp",
-        "daemon/usb.cpp",
-        "daemon/jdwp_service.cpp",
+
+    // libadbd doesn't build any additional source, but to expose libadbd_core as a shared library.
+    whole_static_libs: [
+        "libadbd_core",
     ],
 
-    static_libs: [
+    shared_libs: [
+        "libadbd_services",
         "libasyncio",
-        "libcrypto_utils",
-        "libcrypto",
-        "libdiagnose_usb",
-        "libqemu_pipe",
         "libbase",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "liblog",
     ],
 
     export_include_dirs: [
@@ -307,19 +394,10 @@
 cc_binary {
     name: "adbd",
     defaults: ["adb_defaults"],
-
     recovery_available: true,
 
     srcs: [
         "daemon/main.cpp",
-        "daemon/mdns.cpp",
-        "daemon/file_sync_service.cpp",
-        "daemon/framebuffer_service.cpp",
-        "daemon/remount_service.cpp",
-        "daemon/set_verity_enable_state_service.cpp",
-        "daemon/services.cpp",
-        "daemon/shell_service.cpp",
-        "shell_service_protocol.cpp",
     ],
 
     cflags: [
@@ -331,27 +409,16 @@
         keep_symbols: true,
     },
 
-    static_libs: [
+    shared_libs: [
         "libadbd",
-        "libasyncio",
-        "libavb_user",
-        "libbootloader_message",
-        "libcrypto_utils",
+        "libadbd_services",
+        "libbase",
+        "libcap",
         "libcrypto",
-        "libdiagnose_usb",
-        "libfec",
-        "libfec_rs",
-        "libfs_mgr",
+        "libcutils",
         "liblog",
-        "libext4_utils",
-        "libmdnssd",
         "libminijail",
         "libselinux",
-        "libsquashfs_utils",
-        "libqemu_pipe",
-
-        "libbase",
-        "libcutils",
     ],
 }
 
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 38c6f62..a8fe736 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -891,6 +891,10 @@
         // child side of the fork
         pipe_read.reset();
 
+        // android::base::Pipe unconditionally opens the pipe with O_CLOEXEC.
+        // Undo this manually.
+        fcntl(pipe_write.get(), F_SETFD, 0);
+
         char reply_fd[30];
         snprintf(reply_fd, sizeof(reply_fd), "%d", pipe_write.get());
         // child process
diff --git a/adb/adb_unique_fd.cpp b/adb/adb_unique_fd.cpp
index 2079be1..dec73bc 100644
--- a/adb/adb_unique_fd.cpp
+++ b/adb/adb_unique_fd.cpp
@@ -21,45 +21,8 @@
 
 #include "sysdeps.h"
 
-#if !defined(_WIN32)
-bool Pipe(unique_fd* read, unique_fd* write, int flags) {
-    int pipefd[2];
-#if !defined(__APPLE__)
-    if (pipe2(pipefd, flags) != 0) {
-        return false;
-    }
-#else
-    // Darwin doesn't have pipe2. Implement it ourselves.
-    if (flags != 0 && (flags & ~(O_CLOEXEC | O_NONBLOCK)) != 0) {
-        errno = EINVAL;
-        return false;
-    }
-
-    if (pipe(pipefd) != 0) {
-        return false;
-    }
-
-    if (flags & O_CLOEXEC) {
-        if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 ||
-            fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
-            adb_close(pipefd[0]);
-            adb_close(pipefd[1]);
-            return false;
-        }
-    }
-
-    if (flags & O_NONBLOCK) {
-        if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 ||
-            fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
-            adb_close(pipefd[0]);
-            adb_close(pipefd[1]);
-            return false;
-        }
-    }
-#endif
-
-    read->reset(pipefd[0]);
-    write->reset(pipefd[1]);
-    return true;
+#if defined(_WIN32)
+void AdbCloser::Close(int fd) {
+    adb_close(fd);
 }
 #endif
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
index bad501a..d47213d 100644
--- a/adb/adb_unique_fd.h
+++ b/adb/adb_unique_fd.h
@@ -21,15 +21,15 @@
 
 #include <android-base/unique_fd.h>
 
+#if defined(_WIN32)
 // Helper to automatically close an FD when it goes out of scope.
 struct AdbCloser {
     static void Close(int fd);
 };
 
 using unique_fd = android::base::unique_fd_impl<AdbCloser>;
-
-#if !defined(_WIN32)
-bool Pipe(unique_fd* read, unique_fd* write, int flags = 0);
+#else
+using unique_fd = android::base::unique_fd;
 #endif
 
 template <typename T>
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 0c3327f..ffac315 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -274,10 +274,6 @@
     return android_dir;
 }
 
-void AdbCloser::Close(int fd) {
-    adb_close(fd);
-}
-
 int syntax_error(const char* fmt, ...) {
     fprintf(stderr, "adb: usage: ");
 
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 3b1552e..b20dee9 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -453,10 +453,6 @@
     return it->second;
 }
 
-std::string getEmulatorSerialString(int console_port) {
-    return android::base::StringPrintf("emulator-%d", console_port);
-}
-
 atransport* find_emulator_transport_by_adb_port(int adb_port) {
     std::lock_guard<std::mutex> lock(local_transports_lock);
     return find_emulator_transport_by_adb_port_locked(adb_port);
@@ -467,6 +463,10 @@
 }
 #endif
 
+std::string getEmulatorSerialString(int console_port) {
+    return android::base::StringPrintf("emulator-%d", console_port);
+}
+
 int init_socket_transport(atransport* t, unique_fd fd, int adb_port, int local) {
     int fail = 0;
 
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 1b20157..03b3287 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -43,7 +43,7 @@
     export_include_dirs: ["tombstoned/include"],
 }
 
-// Utility library to tombstoned and get an output fd.
+// Utility library to talk to tombstoned and get an output fd.
 cc_library_static {
     name: "libtombstoned_client_static",
     defaults: ["debuggerd_defaults"],
@@ -166,6 +166,9 @@
     local_include_dirs: ["libdebuggerd/include"],
     export_include_dirs: ["libdebuggerd/include"],
 
+    // Needed for private/bionic_fdsan.h
+    include_dirs: ["bionic/libc"],
+
     static_libs: [
         "libbacktrace",
         "libunwindstack",
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 40cf6c3..93f7572 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -249,24 +249,48 @@
 }
 
 static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
-                          std::unique_ptr<unwindstack::Regs>* regs, uintptr_t* abort_address) {
+                          std::unique_ptr<unwindstack::Regs>* regs, uintptr_t* abort_msg_address,
+                          uintptr_t* fdsan_table_address) {
   std::aligned_storage<sizeof(CrashInfo) + 1, alignof(CrashInfo)>::type buf;
+  CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
   ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf)));
   if (rc == -1) {
     PLOG(FATAL) << "failed to read target ucontext";
-  } else if (rc != sizeof(CrashInfo)) {
-    LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected "
-               << sizeof(CrashInfo);
+  } else {
+    ssize_t expected_size = 0;
+    switch (crash_info->header.version) {
+      case 1:
+        expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV1);
+        break;
+
+      case 2:
+        expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
+        break;
+
+      default:
+        LOG(FATAL) << "unexpected CrashInfo version: " << crash_info->header.version;
+        break;
+    };
+
+    if (rc != expected_size) {
+      LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected "
+                 << expected_size;
+    }
   }
 
-  CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
-  if (crash_info->version != 1) {
-    LOG(FATAL) << "version mismatch, expected 1, received " << crash_info->version;
-  }
+  *fdsan_table_address = 0;
+  switch (crash_info->header.version) {
+    case 2:
+      *fdsan_table_address = crash_info->data.v2.fdsan_table_address;
+    case 1:
+      *abort_msg_address = crash_info->data.v1.abort_msg_address;
+      *siginfo = crash_info->data.v1.siginfo;
+      regs->reset(Regs::CreateFromUcontext(Regs::CurrentArch(), &crash_info->data.v1.ucontext));
+      break;
 
-  *siginfo = crash_info->siginfo;
-  regs->reset(Regs::CreateFromUcontext(Regs::CurrentArch(), &crash_info->ucontext));
-  *abort_address = crash_info->abort_msg_address;
+    default:
+      __builtin_unreachable();
+  }
 }
 
 // Wait for a process to clone and return the child's pid.
@@ -369,7 +393,8 @@
   ATRACE_NAME("after reparent");
   pid_t pseudothread_tid;
   DebuggerdDumpType dump_type;
-  uintptr_t abort_address = 0;
+  uintptr_t abort_msg_address = 0;
+  uintptr_t fdsan_table_address = 0;
 
   Initialize(argv);
   ParseArgs(argc, argv, &pseudothread_tid, &dump_type);
@@ -387,7 +412,7 @@
   OpenFilesList open_files;
   {
     ATRACE_NAME("open files");
-    populate_open_files_list(g_target_thread, &open_files);
+    populate_open_files_list(&open_files, g_target_thread);
   }
 
   // In order to reduce the duration that we pause the process for, we ptrace
@@ -429,7 +454,8 @@
 
       if (thread == g_target_thread) {
         // Read the thread's registers along with the rest of the crash info out of the pipe.
-        ReadCrashInfo(input_pipe, &siginfo, &info.registers, &abort_address);
+        ReadCrashInfo(input_pipe, &siginfo, &info.registers, &abort_msg_address,
+                      &fdsan_table_address);
         info.siginfo = &siginfo;
         info.signo = info.siginfo->si_signo;
       } else {
@@ -504,8 +530,8 @@
     g_output_fd = std::move(devnull);
   }
 
-  LOG(INFO) << "performing dump of process " << target_process << " (target tid = " << g_target_thread
-            << ")";
+  LOG(INFO) << "performing dump of process " << target_process
+            << " (target tid = " << g_target_thread << ")";
 
   int signo = siginfo.si_signo;
   bool fatal_signal = signo != DEBUGGER_SIGNAL;
@@ -541,9 +567,16 @@
     ATRACE_NAME("dump_backtrace");
     dump_backtrace(std::move(g_output_fd), map.get(), thread_info, g_target_thread);
   } else {
-    ATRACE_NAME("engrave_tombstone");
-    engrave_tombstone(std::move(g_output_fd), map.get(), process_memory.get(), thread_info,
-                      g_target_thread, abort_address, &open_files, &amfd_data);
+    {
+      ATRACE_NAME("fdsan table dump");
+      populate_fdsan_table(&open_files, process_memory, fdsan_table_address);
+    }
+
+    {
+      ATRACE_NAME("engrave_tombstone");
+      engrave_tombstone(std::move(g_output_fd), map.get(), process_memory.get(), thread_info,
+                        g_target_thread, abort_msg_address, &open_files, &amfd_data);
+    }
   }
 
   if (fatal_signal) {
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 615fb46..91e6f71 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -108,6 +108,7 @@
   int saved_errno_;
 };
 
+extern "C" void* android_fdsan_get_fd_table();
 extern "C" void debuggerd_fallback_handler(siginfo_t*, ucontext_t*, void*);
 
 static debuggerd_callbacks_t g_callbacks;
@@ -286,6 +287,7 @@
   siginfo_t* siginfo;
   void* ucontext;
   uintptr_t abort_msg;
+  uintptr_t fdsan_table;
 };
 
 // Logging and contacting debuggerd requires free file descriptors, which we might not have.
@@ -330,23 +332,23 @@
   }
 
   // ucontext_t is absurdly large on AArch64, so piece it together manually with writev.
-  uint32_t version = 1;
-  constexpr size_t expected =
-      sizeof(version) + sizeof(siginfo_t) + sizeof(ucontext_t) + sizeof(uintptr_t);
+  uint32_t version = 2;
+  constexpr size_t expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
 
   errno = 0;
   if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) {
-    fatal_errno("failed to set pipe bufer size");
+    fatal_errno("failed to set pipe buffer size");
   }
 
-  struct iovec iovs[4] = {
+  struct iovec iovs[5] = {
       {.iov_base = &version, .iov_len = sizeof(version)},
       {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)},
       {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},
       {.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)},
+      {.iov_base = &thread_info->fdsan_table, .iov_len = sizeof(uintptr_t)},
   };
 
-  ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, 4));
+  ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, 5));
   if (rc == -1) {
     fatal_errno("failed to write crash info");
   } else if (rc != expected) {
@@ -504,6 +506,7 @@
       .siginfo = info,
       .ucontext = context,
       .abort_msg = reinterpret_cast<uintptr_t>(abort_message),
+      .fdsan_table = reinterpret_cast<uintptr_t>(android_fdsan_get_fd_table()),
   };
 
   // Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
index 4727ca4..d47f2dd 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
@@ -14,23 +14,31 @@
  * limitations under the License.
  */
 
-#ifndef _DEBUGGERD_OPEN_FILES_LIST_H
-#define _DEBUGGERD_OPEN_FILES_LIST_H
+#pragma once
 
+#include <stdint.h>
 #include <sys/types.h>
 
+#include <map>
+#include <optional>
 #include <string>
 #include <utility>
-#include <vector>
 
 #include "utility.h"
 
-typedef std::vector<std::pair<int, std::string>> OpenFilesList;
+struct FDInfo {
+  std::optional<std::string> path;
+  std::optional<uint64_t> fdsan_owner;
+};
 
-/* Populates the given list with open files for the given process. */
-void populate_open_files_list(pid_t pid, OpenFilesList* list);
+using OpenFilesList = std::map<int, FDInfo>;
 
-/* Dumps the open files list to the log. */
+// Populates the given list with open files for the given process.
+void populate_open_files_list(OpenFilesList* list, pid_t pid);
+
+// Populates the given list with the target process's fdsan table.
+void populate_fdsan_table(OpenFilesList* list, std::shared_ptr<unwindstack::Memory> memory,
+                          uint64_t fdsan_table_address);
+
+// Dumps the open files list to the log.
 void dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix);
-
-#endif // _DEBUGGERD_OPEN_FILES_LIST_H
diff --git a/debuggerd/libdebuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
index b12703e..1fdf236 100644
--- a/debuggerd/libdebuggerd/open_files_list.cpp
+++ b/debuggerd/libdebuggerd/open_files_list.cpp
@@ -32,10 +32,12 @@
 
 #include <android-base/file.h>
 #include <log/log.h>
+#include <unwindstack/Memory.h>
 
 #include "libdebuggerd/utility.h"
+#include "private/bionic_fdsan.h"
 
-void populate_open_files_list(pid_t pid, OpenFilesList* list) {
+void populate_open_files_list(OpenFilesList* list, pid_t pid) {
   std::string fd_dir_name = "/proc/" + std::to_string(pid) + "/fd";
   std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(fd_dir_name.c_str()), closedir);
   if (dir == nullptr) {
@@ -53,17 +55,84 @@
     std::string path = fd_dir_name + "/" + std::string(de->d_name);
     std::string target;
     if (android::base::Readlink(path, &target)) {
-      list->emplace_back(fd, target);
+      (*list)[fd].path = target;
     } else {
+      (*list)[fd].path = "???";
       ALOGE("failed to readlink %s: %s", path.c_str(), strerror(errno));
-      list->emplace_back(fd, "???");
     }
   }
 }
 
+void populate_fdsan_table(OpenFilesList* list, std::shared_ptr<unwindstack::Memory> memory,
+                          uint64_t fdsan_table_address) {
+  constexpr size_t inline_fds = sizeof(FdTable::entries) / sizeof(*FdTable::entries);
+  static_assert(inline_fds == 128);
+  size_t entry_offset = offsetof(FdTable, entries);
+  for (size_t i = 0; i < inline_fds; ++i) {
+    uint64_t address = fdsan_table_address + entry_offset + sizeof(FdEntry) * i;
+    FdEntry entry;
+    if (!memory->Read(address, &entry, sizeof(entry))) {
+      ALOGE("failed to read fdsan table entry %zu: %s", i, strerror(errno));
+      return;
+    }
+    ALOGE("fd %zu = %#" PRIx64, i, entry.close_tag.load());
+    if (entry.close_tag) {
+      (*list)[i].fdsan_owner = entry.close_tag.load();
+    }
+  }
+
+  size_t overflow_offset = offsetof(FdTable, overflow);
+  uintptr_t overflow = 0;
+  if (!memory->Read(fdsan_table_address + overflow_offset, &overflow, sizeof(overflow))) {
+    ALOGE("failed to read fdsan table overflow pointer: %s", strerror(errno));
+    return;
+  }
+
+  if (!overflow) {
+    return;
+  }
+
+  size_t overflow_length;
+  if (!memory->Read(overflow, &overflow_length, sizeof(overflow_length))) {
+    ALOGE("failed to read fdsan overflow table length: %s", strerror(errno));
+    return;
+  }
+
+  if (overflow_length > 131072) {
+    ALOGE("unreasonable large fdsan overflow table size %zu, bailing out", overflow_length);
+    return;
+  }
+
+  for (size_t i = 0; i < overflow_length; ++i) {
+    int fd = i + inline_fds;
+    uint64_t address = overflow + offsetof(FdTableOverflow, entries) + i * sizeof(FdEntry);
+    FdEntry entry;
+    if (!memory->Read(address, &entry, sizeof(entry))) {
+      ALOGE("failed to read fdsan overflow entry for fd %d: %s", fd, strerror(errno));
+      return;
+    }
+    if (entry.close_tag) {
+      (*list)[fd].fdsan_owner = entry.close_tag;
+    }
+  }
+  return;
+}
+
 void dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix) {
-  for (auto& file : files) {
-    _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s\n", prefix, file.first, file.second.c_str());
+  for (auto& [fd, entry] : files) {
+    const std::optional<std::string>& path = entry.path;
+    const std::optional<uint64_t>& fdsan_owner = entry.fdsan_owner;
+    if (path && fdsan_owner) {
+      _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s (owned by %#" PRIx64 ")\n", prefix, fd,
+           path->c_str(), *fdsan_owner);
+    } else if (path && !fdsan_owner) {
+      _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s (unowned)\n", prefix, fd, path->c_str());
+    } else if (!path && fdsan_owner) {
+      _LOG(log, logtype::OPEN_FILES, "%sfd %i: <MISSING> (owned by %#" PRIx64 ")\n", prefix, fd,
+           *fdsan_owner);
+    } else {
+      ALOGE("OpenFilesList contains an entry (fd %d) with no path or owner", fd);
+    }
   }
 }
 
diff --git a/debuggerd/libdebuggerd/test/open_files_list_test.cpp b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
index acac72c..d7036fd 100644
--- a/debuggerd/libdebuggerd/test/open_files_list_test.cpp
+++ b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
@@ -34,13 +34,13 @@
 
   // Get the list of open files for this process.
   OpenFilesList list;
-  populate_open_files_list(getpid(), &list);
+  populate_open_files_list(&list, getpid());
 
   // Verify our open file is in the list.
   bool found = false;
-  for (auto&  file : list) {
+  for (auto& file : list) {
     if (file.first == tf.fd) {
-      EXPECT_EQ(file.second, std::string(tf.path));
+      EXPECT_EQ(file.second.path.value_or(""), std::string(tf.path));
       found = true;
       break;
     }
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index 6903b0e..bfd0fbb 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -81,9 +81,24 @@
 };
 
 // Sent from handler to crash_dump via pipe.
-struct __attribute__((__packed__)) CrashInfo {
-  uint32_t version;  // must be 1.
+struct __attribute__((__packed__)) CrashInfoHeader {
+  uint32_t version;
+};
+
+struct __attribute__((__packed__)) CrashInfoDataV1 {
   siginfo_t siginfo;
   ucontext_t ucontext;
   uintptr_t abort_msg_address;
 };
+
+struct __attribute__((__packed__)) CrashInfoDataV2 : public CrashInfoDataV1 {
+  uintptr_t fdsan_table_address;
+};
+
+struct __attribute__((__packed__)) CrashInfo {
+  CrashInfoHeader header;
+  union {
+    CrashInfoDataV1 v1;
+    CrashInfoDataV2 v2;
+  } data;
+};
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 2b4a954..51cc62a 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -1,3 +1,17 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 cc_library_host_static {
     name: "libfastboot2",
 
@@ -54,3 +68,54 @@
     export_include_dirs: ["."],
 
 }
+
+cc_defaults {
+    name: "fastboot_defaults",
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wvla",
+    ],
+    rtti: true,
+
+    clang_cflags: [
+        "-Wthread-safety",
+    ],
+}
+
+cc_binary {
+    name: "fastbootd",
+    defaults: ["fastboot_defaults"],
+
+    recovery: true,
+
+    srcs: [
+        "device/commands.cpp",
+        "device/fastboot_device.cpp",
+        "device/main.cpp",
+        "device/usb_client.cpp",
+    ],
+
+    shared_libs: [
+        "libasyncio",
+        "libext4_utils",
+        "libsparse",
+        "liblog",
+        "libbootloader_message",
+        "libhidltransport",
+        "libhidlbase",
+        "libhwbinder",
+        "libbase",
+        "libutils",
+        "libcutils",
+        "libfs_mgr",
+    ],
+
+    static_libs: [
+        "libadbd",
+    ],
+
+    cpp_std: "c++17",
+}
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 5e7e955..7caca3d 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -27,6 +27,8 @@
 #define FB_CMD_REBOOT "reboot"
 #define FB_CMD_SHUTDOWN "shutdown"
 #define FB_CMD_REBOOT_BOOTLOADER "reboot-bootloader"
+#define FB_CMD_REBOOT_RECOVERY "reboot-recovery"
+#define FB_CMD_REBOOT_FASTBOOT "reboot-fastboot"
 #define FB_CMD_POWERDOWN "powerdown"
 
 #define RESPONSE_OKAY "OKAY"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
new file mode 100644
index 0000000..a3cbf96
--- /dev/null
+++ b/fastboot/device/commands.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "commands.h"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/android_reboot.h>
+
+#include "fastboot_device.h"
+
+bool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteStatus(FastbootResult::FAIL, "size argument unspecified");
+    }
+    // arg[0] is the command name, arg[1] contains size of data to be downloaded
+    unsigned int size;
+    if (!android::base::ParseUint("0x" + args[1], &size, UINT_MAX)) {
+        return device->WriteStatus(FastbootResult::FAIL, "Invalid size");
+    }
+    device->get_download_data().resize(size);
+    if (!device->WriteStatus(FastbootResult::DATA, android::base::StringPrintf("%08x", size))) {
+        return false;
+    }
+
+    if (device->HandleData(true, &device->get_download_data())) {
+        return device->WriteStatus(FastbootResult::OKAY, "");
+    }
+
+    PLOG(ERROR) << "Couldn't download data";
+    return device->WriteStatus(FastbootResult::FAIL, "Couldn't download data");
+}
+
+bool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    return device->WriteStatus(FastbootResult::OKAY, "");
+}
+
+bool ShutDownHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Shutting down");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,fastboot");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+bool RebootHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Rebooting");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,from_fastboot");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+bool RebootBootloaderHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Rebooting bootloader");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+bool RebootFastbootHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Rebooting fastboot");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+static bool EnterRecovery() {
+    const char msg_switch_to_recovery = 'r';
+
+    android::base::unique_fd sock(socket(AF_UNIX, SOCK_STREAM, 0));
+    if (sock < 0) {
+        PLOG(ERROR) << "Couldn't create sock";
+        return false;
+    }
+
+    struct sockaddr_un addr = {.sun_family = AF_UNIX};
+    strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
+    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+        PLOG(ERROR) << "Couldn't connect to recovery";
+        return false;
+    }
+    // Switch to recovery will not update the boot reason since it does not
+    // require a reboot.
+    auto ret = write(sock, &msg_switch_to_recovery, sizeof(msg_switch_to_recovery));
+    if (ret != sizeof(msg_switch_to_recovery)) {
+        PLOG(ERROR) << "Couldn't write message to switch to recovery";
+        return false;
+    }
+
+    return true;
+}
+
+bool RebootRecoveryHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto status = true;
+    if (EnterRecovery()) {
+        status = device->WriteStatus(FastbootResult::OKAY, "Rebooting to recovery");
+    } else {
+        status = device->WriteStatus(FastbootResult::FAIL, "Unable to reboot to recovery");
+    }
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return status;
+}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
new file mode 100644
index 0000000..cd984c8
--- /dev/null
+++ b/fastboot/device/commands.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <functional>
+#include <string>
+#include <vector>
+
+class FastbootDevice;
+
+enum class FastbootResult {
+    OKAY,
+    FAIL,
+    INFO,
+    DATA,
+};
+
+// Execute a command with the given arguments (possibly empty).
+using CommandHandler = std::function<bool(FastbootDevice*, const std::vector<std::string>&)>;
+
+bool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool ShutDownHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootBootloaderHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootFastbootHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootRecoveryHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
new file mode 100644
index 0000000..4c3d23f
--- /dev/null
+++ b/fastboot/device/fastboot_device.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fastboot_device.h"
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include <algorithm>
+
+#include "constants.h"
+#include "usb_client.h"
+
+FastbootDevice::FastbootDevice()
+    : kCommandMap({
+              {FB_CMD_SET_ACTIVE, SetActiveHandler},
+              {FB_CMD_DOWNLOAD, DownloadHandler},
+              {FB_CMD_SHUTDOWN, ShutDownHandler},
+              {FB_CMD_REBOOT, RebootHandler},
+              {FB_CMD_REBOOT_BOOTLOADER, RebootBootloaderHandler},
+              {FB_CMD_REBOOT_FASTBOOT, RebootFastbootHandler},
+              {FB_CMD_REBOOT_RECOVERY, RebootRecoveryHandler},
+      }),
+      transport_(std::make_unique<ClientUsbTransport>()) {}
+
+FastbootDevice::~FastbootDevice() {
+    CloseDevice();
+}
+
+void FastbootDevice::CloseDevice() {
+    transport_->Close();
+}
+
+bool FastbootDevice::WriteStatus(FastbootResult result, const std::string& message) {
+    constexpr size_t kResponseReasonSize = 4;
+    constexpr size_t kNumResponseTypes = 4;  // "FAIL", "OKAY", "INFO", "DATA"
+    char buf[FB_RESPONSE_SZ];
+    constexpr size_t kMaxMessageSize = sizeof(buf) - kResponseReasonSize;
+    size_t msg_len = std::min(kMaxMessageSize, message.size());
+
+    constexpr const char* kResultStrings[kNumResponseTypes] = {RESPONSE_OKAY, RESPONSE_FAIL,
+                                                               RESPONSE_INFO, RESPONSE_DATA};
+
+    if (static_cast<size_t>(result) >= kNumResponseTypes) {
+        return false;
+    }
+
+    memcpy(buf, kResultStrings[static_cast<size_t>(result)], kResponseReasonSize);
+    memcpy(buf + kResponseReasonSize, message.c_str(), msg_len);
+
+    size_t response_len = kResponseReasonSize + msg_len;
+    auto write_ret = this->get_transport()->Write(buf, response_len);
+    if (write_ret != static_cast<ssize_t>(response_len)) {
+        PLOG(ERROR) << "Failed to write " << message;
+        return false;
+    }
+
+    return true;
+}
+
+bool FastbootDevice::HandleData(bool read, std::vector<char>* data) {
+    auto read_write_data_size = read ? this->get_transport()->Read(data->data(), data->size())
+                                     : this->get_transport()->Write(data->data(), data->size());
+    if (read_write_data_size == -1 || static_cast<size_t>(read_write_data_size) != data->size()) {
+        return false;
+    }
+    return true;
+}
+
+void FastbootDevice::ExecuteCommands() {
+    char command[FB_RESPONSE_SZ + 1];
+    for (;;) {
+        auto bytes_read = transport_->Read(command, FB_RESPONSE_SZ);
+        if (bytes_read == -1) {
+            PLOG(ERROR) << "Couldn't read command";
+            return;
+        }
+        command[bytes_read] = '\0';
+
+        LOG(INFO) << "Fastboot command: " << command;
+        auto args = android::base::Split(command, ":");
+        auto found_command = kCommandMap.find(args[0]);
+        if (found_command == kCommandMap.end()) {
+            WriteStatus(FastbootResult::FAIL, "Unrecognized command");
+            continue;
+        }
+        if (!found_command->second(this, args)) {
+            return;
+        }
+    }
+}
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
new file mode 100644
index 0000000..fec42a7
--- /dev/null
+++ b/fastboot/device/fastboot_device.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "commands.h"
+#include "transport.h"
+
+class FastbootDevice;
+
+class FastbootDevice {
+  public:
+    FastbootDevice();
+    ~FastbootDevice();
+
+    void CloseDevice();
+    void ExecuteCommands();
+    bool WriteStatus(FastbootResult result, const std::string& message);
+    bool HandleData(bool read, std::vector<char>* data);
+
+    std::vector<char>& get_download_data() { return download_data_; }
+    void set_upload_data(const std::vector<char>& data) { upload_data_ = data; }
+    void set_upload_data(std::vector<char>&& data) { upload_data_ = std::move(data); }
+    Transport* get_transport() { return transport_.get(); }
+
+  private:
+    const std::unordered_map<std::string, CommandHandler> kCommandMap;
+
+    std::unique_ptr<Transport> transport_;
+
+    std::vector<char> download_data_;
+    std::vector<char> upload_data_;
+};
diff --git a/fastboot/device/main.cpp b/fastboot/device/main.cpp
new file mode 100644
index 0000000..df9c900
--- /dev/null
+++ b/fastboot/device/main.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+
+#include "fastboot_device.h"
+
+int main(int /*argc*/, char* argv[]) {
+    android::base::InitLogging(argv, &android::base::KernelLogger);
+
+    while (true) {
+        FastbootDevice device;
+        device.ExecuteCommands();
+    }
+}
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
new file mode 100644
index 0000000..215f99a
--- /dev/null
+++ b/fastboot/device/usb_client.cpp
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "usb_client.h"
+
+#include <endian.h>
+#include <fcntl.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+constexpr int kMaxPacketSizeFs = 64;
+constexpr int kMaxPacketSizeHs = 512;
+constexpr int kMaxPacketsizeSs = 1024;
+
+constexpr size_t kFbFfsNumBufs = 16;
+constexpr size_t kFbFfsBufSize = 32768;
+
+constexpr const char* kUsbFfsFastbootEp0 = "/dev/usb-ffs/fastboot/ep0";
+constexpr const char* kUsbFfsFastbootOut = "/dev/usb-ffs/fastboot/ep1";
+constexpr const char* kUsbFfsFastbootIn = "/dev/usb-ffs/fastboot/ep2";
+
+struct FuncDesc {
+    struct usb_interface_descriptor intf;
+    struct usb_endpoint_descriptor_no_audio source;
+    struct usb_endpoint_descriptor_no_audio sink;
+} __attribute__((packed));
+
+struct SsFuncDesc {
+    struct usb_interface_descriptor intf;
+    struct usb_endpoint_descriptor_no_audio source;
+    struct usb_ss_ep_comp_descriptor source_comp;
+    struct usb_endpoint_descriptor_no_audio sink;
+    struct usb_ss_ep_comp_descriptor sink_comp;
+} __attribute__((packed));
+
+struct DescV2 {
+    struct usb_functionfs_descs_head_v2 header;
+    // The rest of the structure depends on the flags in the header.
+    __le32 fs_count;
+    __le32 hs_count;
+    __le32 ss_count;
+    struct FuncDesc fs_descs, hs_descs;
+    struct SsFuncDesc ss_descs;
+} __attribute__((packed));
+
+struct usb_interface_descriptor fastboot_interface = {
+        .bLength = USB_DT_INTERFACE_SIZE,
+        .bDescriptorType = USB_DT_INTERFACE,
+        .bInterfaceNumber = 0,
+        .bNumEndpoints = 2,
+        .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+        .bInterfaceSubClass = 66,
+        .bInterfaceProtocol = 3,
+        .iInterface = 1, /* first string from the provided table */
+};
+
+static struct FuncDesc fs_descriptors = {
+        .intf = fastboot_interface,
+        .source =
+                {
+                        .bLength = sizeof(fs_descriptors.source),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_OUT,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeFs,
+                },
+        .sink =
+                {
+                        .bLength = sizeof(fs_descriptors.sink),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_IN,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeFs,
+                },
+};
+
+static struct FuncDesc hs_descriptors = {
+        .intf = fastboot_interface,
+        .source =
+                {
+                        .bLength = sizeof(hs_descriptors.source),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_OUT,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeHs,
+                },
+        .sink =
+                {
+                        .bLength = sizeof(hs_descriptors.sink),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_IN,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeHs,
+                },
+};
+
+static struct SsFuncDesc ss_descriptors = {
+        .intf = fastboot_interface,
+        .source =
+                {
+                        .bLength = sizeof(ss_descriptors.source),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_OUT,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketsizeSs,
+                },
+        .source_comp =
+                {
+                        .bLength = sizeof(ss_descriptors.source_comp),
+                        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+                        .bMaxBurst = 15,
+                },
+        .sink =
+                {
+                        .bLength = sizeof(ss_descriptors.sink),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_IN,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketsizeSs,
+                },
+        .sink_comp =
+                {
+                        .bLength = sizeof(ss_descriptors.sink_comp),
+                        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+                        .bMaxBurst = 15,
+                },
+};
+
+#define STR_INTERFACE_ "fastboot"
+
+static const struct {
+    struct usb_functionfs_strings_head header;
+    struct {
+        __le16 code;
+        const char str1[sizeof(STR_INTERFACE_)];
+    } __attribute__((packed)) lang0;
+} __attribute__((packed)) strings = {
+        .header =
+                {
+                        .magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
+                        .length = htole32(sizeof(strings)),
+                        .str_count = htole32(1),
+                        .lang_count = htole32(1),
+                },
+        .lang0 =
+                {
+                        htole16(0x0409), /* en-us */
+                        STR_INTERFACE_,
+                },
+};
+
+static struct DescV2 v2_descriptor = {
+        .header =
+                {
+                        .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+                        .length = htole32(sizeof(v2_descriptor)),
+                        .flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
+                                 FUNCTIONFS_HAS_SS_DESC,
+                },
+        .fs_count = 3,
+        .hs_count = 3,
+        .ss_count = 5,
+        .fs_descs = fs_descriptors,
+        .hs_descs = hs_descriptors,
+        .ss_descs = ss_descriptors,
+};
+
+// Reimplementing since usb_ffs_close() does not close the control FD.
+static void CloseFunctionFs(usb_handle* h) {
+    if (h->bulk_in > 0) {
+        close(h->bulk_in);
+        h->bulk_in = -1;
+    }
+    if (h->bulk_out > 0) {
+        close(h->bulk_out);
+        h->bulk_out = -1;
+    }
+    if (h->control > 0) {
+        close(h->control);
+        h->control = -1;
+    }
+}
+
+static bool InitFunctionFs(usb_handle* h) {
+    LOG(INFO) << "initializing functionfs";
+
+    if (h->control < 0) {  // might have already done this before
+        LOG(INFO) << "opening control endpoint " << kUsbFfsFastbootEp0;
+        h->control = open(kUsbFfsFastbootEp0, O_RDWR);
+        if (h->control < 0) {
+            PLOG(ERROR) << "cannot open control endpoint " << kUsbFfsFastbootEp0;
+            goto err;
+        }
+
+        auto ret = write(h->control, &v2_descriptor, sizeof(v2_descriptor));
+        if (ret < 0) {
+            PLOG(ERROR) << "cannot write descriptors " << kUsbFfsFastbootEp0;
+            goto err;
+        }
+
+        ret = write(h->control, &strings, sizeof(strings));
+        if (ret < 0) {
+            PLOG(ERROR) << "cannot write strings " << kUsbFfsFastbootEp0;
+            goto err;
+        }
+        // Signal only when writing the descriptors to ffs
+        android::base::SetProperty("sys.usb.ffs.ready", "1");
+    }
+
+    h->bulk_out = open(kUsbFfsFastbootOut, O_RDONLY);
+    if (h->bulk_out < 0) {
+        PLOG(ERROR) << "cannot open bulk-out endpoint " << kUsbFfsFastbootOut;
+        goto err;
+    }
+
+    h->bulk_in = open(kUsbFfsFastbootIn, O_WRONLY);
+    if (h->bulk_in < 0) {
+        PLOG(ERROR) << "cannot open bulk-in endpoint " << kUsbFfsFastbootIn;
+        goto err;
+    }
+
+    h->read_aiob.fd = h->bulk_out;
+    h->write_aiob.fd = h->bulk_in;
+    h->reads_zero_packets = false;
+    return true;
+
+err:
+    CloseFunctionFs(h);
+    return false;
+}
+
+ClientUsbTransport::ClientUsbTransport()
+    : handle_(std::unique_ptr<usb_handle>(create_usb_handle(kFbFfsNumBufs, kFbFfsBufSize))) {
+    if (!InitFunctionFs(handle_.get())) {
+        handle_.reset(nullptr);
+    }
+}
+
+ssize_t ClientUsbTransport::Read(void* data, size_t len) {
+    if (handle_ == nullptr || len > SSIZE_MAX) {
+        return -1;
+    }
+    char* char_data = static_cast<char*>(data);
+    size_t bytes_read_total = 0;
+    while (bytes_read_total < len) {
+        auto bytes_to_read = std::min(len - bytes_read_total, kFbFfsNumBufs * kFbFfsBufSize);
+        auto bytes_read_now = handle_->read(handle_.get(), char_data, bytes_to_read);
+        if (bytes_read_now < 0) {
+            return bytes_read_total;
+        }
+        bytes_read_total += bytes_read_now;
+        char_data += bytes_read_now;
+        if (static_cast<size_t>(bytes_read_now) < bytes_to_read) {
+            break;
+        }
+    }
+    return bytes_read_total;
+}
+
+ssize_t ClientUsbTransport::Write(const void* data, size_t len) {
+    if (handle_ == nullptr || len > SSIZE_MAX) {
+        return -1;
+    }
+    const char* char_data = reinterpret_cast<const char*>(data);
+    size_t bytes_written_total = 0;
+    while (bytes_written_total < len) {
+        auto bytes_to_write = std::min(len - bytes_written_total, kFbFfsNumBufs * kFbFfsBufSize);
+        auto bytes_written_now = handle_->write(handle_.get(), data, bytes_to_write);
+        if (bytes_written_now < 0) {
+            return bytes_written_total;
+        }
+        bytes_written_total += bytes_written_now;
+        char_data += bytes_written_now;
+        if (static_cast<size_t>(bytes_written_now) < bytes_to_write) {
+            break;
+        }
+    }
+    return bytes_written_total;
+}
+
+int ClientUsbTransport::Close() {
+    if (handle_ == nullptr) {
+        return -1;
+    }
+    CloseFunctionFs(handle_.get());
+    return 0;
+}
diff --git a/fastboot/device/usb_client.h b/fastboot/device/usb_client.h
new file mode 100644
index 0000000..3694f9a
--- /dev/null
+++ b/fastboot/device/usb_client.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include <adbd/usb.h>
+
+#include "transport.h"
+
+class ClientUsbTransport : public Transport {
+  public:
+    ClientUsbTransport();
+    ~ClientUsbTransport() override = default;
+
+    ssize_t Read(void* data, size_t len) override;
+    ssize_t Write(const void* data, size_t len) override;
+    int Close() override;
+
+  private:
+    std::unique_ptr<usb_handle> handle_;
+
+    DISALLOW_COPY_AND_ASSIGN(ClientUsbTransport);
+};
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 321e6ba..d787d09 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -111,12 +111,13 @@
     const char* img_name;
     const char* sig_name;
     const char* part_name;
-    bool is_optional;
-    bool is_secondary;
+    bool optional_if_no_image;
+    bool optional_if_no_partition;
+    bool IsSecondary() const { return nickname == nullptr; }
 } images[] = {
         // clang-format off
     { "boot",     "boot.img",         "boot.sig",     "boot",     false, false },
-    { nullptr,    "boot_other.img",   "boot.sig",     "boot",     true,  true  },
+    { nullptr,    "boot_other.img",   "boot.sig",     "boot",     true,  false },
     { "dtbo",     "dtbo.img",         "dtbo.sig",     "dtbo",     true,  false },
     { "dts",      "dt.img",           "dt.sig",       "dts",      true,  false },
     { "odm",      "odm.img",          "odm.sig",      "odm",      true,  false },
@@ -125,14 +126,14 @@
                   "product-services.img",
                                       "product-services.sig",
                                                       "product-services",
-                                                                  true,  false },
+                                                                  true,  true  },
     { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  false },
-    { "super",    "super.img",        "super.sig",    "super",    true,  false },
-    { "system",   "system.img",       "system.sig",   "system",   false, false },
-    { nullptr,    "system_other.img", "system.sig",   "system",   true,  true  },
+    { "super",    "super.img",        "super.sig",    "super",    true,  true  },
+    { "system",   "system.img",       "system.sig",   "system",   false, true  },
+    { nullptr,    "system_other.img", "system.sig",   "system",   true,  false },
     { "vbmeta",   "vbmeta.img",       "vbmeta.sig",   "vbmeta",   true,  false },
-    { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  false },
-    { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  true  },
+    { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  true  },
+    { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  false },
         // clang-format on
 };
 
@@ -637,7 +638,8 @@
     // "require partition-exists=x" is a special case, added because of the trouble we had when
     // Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
     // missing out new partitions. A device with new partitions can use "partition-exists" to
-    // override the `is_optional` field in the `images` array.
+    // override the fields `optional_if_no_image` and 'optional_if_no_partition' in the `images`
+    // array.
     if (!strcmp(name, "partition-exists")) {
         const char* partition_name = val[0];
         std::string has_slot;
@@ -648,7 +650,8 @@
         bool known_partition = false;
         for (size_t i = 0; i < arraysize(images); ++i) {
             if (images[i].nickname && !strcmp(images[i].nickname, partition_name)) {
-                images[i].is_optional = false;
+                images[i].optional_if_no_image = false;
+                images[i].optional_if_no_partition = false;
                 known_partition = true;
             }
         }
@@ -1041,6 +1044,25 @@
     }
 }
 
+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 (slot == "") {
+            std::string current_slot = get_current_slot();
+            if (current_slot == "") {
+                die("Failed to identify current slot");
+            }
+            partition_name += "_" + current_slot;
+        } else {
+            partition_name += "_" + slot;
+        }
+    }
+    std::string partition_size;
+    return fb_getvar("partition-size:" + partition_name, &partition_size);
+}
+
 static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
     queue_info_dump();
 
@@ -1076,7 +1098,7 @@
     }
     for (size_t i = 0; i < arraysize(images); ++i) {
         const char* slot = slot_override.c_str();
-        if (images[i].is_secondary) {
+        if (images[i].IsSecondary()) {
             if (!skip_secondary) {
                 slot = secondary.c_str();
             } else {
@@ -1086,12 +1108,17 @@
 
         int fd = unzip_to_file(zip, images[i].img_name);
         if (fd == -1) {
-            if (images[i].is_optional) {
+            if (images[i].optional_if_no_image) {
                 continue; // An optional file is missing, so ignore it.
             }
             die("non-optional file %s missing", images[i].img_name);
         }
 
+        if (images[i].optional_if_no_partition &&
+            !if_partition_exists(images[i].part_name, slot)) {
+            continue;
+        }
+
         fastboot_buffer buf;
         if (!load_buf_fd(fd, &buf)) {
             die("cannot load %s from flash: %s", images[i].img_name, strerror(errno));
@@ -1163,7 +1190,7 @@
 
     for (size_t i = 0; i < arraysize(images); i++) {
         const char* slot = NULL;
-        if (images[i].is_secondary) {
+        if (images[i].IsSecondary()) {
             if (!skip_secondary) slot = secondary.c_str();
         } else {
             slot = slot_override.c_str();
@@ -1172,10 +1199,13 @@
         fname = find_item_given_name(images[i].img_name);
         fastboot_buffer buf;
         if (!load_buf(fname.c_str(), &buf)) {
-            if (images[i].is_optional) continue;
+            if (images[i].optional_if_no_image) continue;
             die("could not load '%s': %s", images[i].img_name, strerror(errno));
         }
-
+        if (images[i].optional_if_no_partition &&
+            !if_partition_exists(images[i].part_name, slot)) {
+            continue;
+        }
         auto flashall = [&](const std::string &partition) {
             do_send_signature(fname.c_str());
             flash_buf(partition.c_str(), &buf);
@@ -1333,6 +1363,8 @@
     bool wants_wipe = false;
     bool wants_reboot = false;
     bool wants_reboot_bootloader = false;
+    bool wants_reboot_recovery = false;
+    bool wants_reboot_fastboot = false;
     bool skip_reboot = false;
     bool wants_set_active = false;
     bool skip_secondary = false;
@@ -1555,6 +1587,12 @@
                 if (what == "bootloader") {
                     wants_reboot = false;
                     wants_reboot_bootloader = true;
+                } else if (what == "recovery") {
+                    wants_reboot = false;
+                    wants_reboot_recovery = true;
+                } else if (what == "fastboot") {
+                    wants_reboot = false;
+                    wants_reboot_fastboot = true;
                 } else {
                     syntax_error("unknown reboot target %s", what.c_str());
                 }
@@ -1563,6 +1601,10 @@
             if (!args.empty()) syntax_error("junk after reboot command");
         } else if (command == "reboot-bootloader") {
             wants_reboot_bootloader = true;
+        } else if (command == "reboot-recovery") {
+            wants_reboot_recovery = true;
+        } else if (command == "reboot-fastboot") {
+            wants_reboot_fastboot = true;
         } else if (command == "continue") {
             fb_queue_command("continue", "resuming boot");
         } else if (command == "boot") {
@@ -1680,6 +1722,12 @@
     } else if (wants_reboot_bootloader) {
         fb_queue_command("reboot-bootloader", "rebooting into bootloader");
         fb_queue_wait_for_disconnect();
+    } else if (wants_reboot_recovery) {
+        fb_queue_command("reboot-recovery", "rebooting into recovery");
+        fb_queue_wait_for_disconnect();
+    } else if (wants_reboot_fastboot) {
+        fb_queue_command("reboot-fastboot", "rebooting into fastboot");
+        fb_queue_wait_for_disconnect();
     }
 
     int status = fb_execute_queue() ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index aa68ceb..a91e92e 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -80,13 +80,17 @@
 }
 
 static bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
-                                   const LpMetadataPartition& partition, std::string* path) {
+                                   const LpMetadataPartition& partition, bool force_writable,
+                                   std::string* path) {
     DeviceMapper& dm = DeviceMapper::Instance();
 
     DmTable table;
     if (!CreateDmTable(block_device, metadata, partition, &table)) {
         return false;
     }
+    if (force_writable) {
+        table.set_readonly(false);
+    }
     std::string name = GetPartitionName(partition);
     if (!dm.CreateDevice(name, table)) {
         return false;
@@ -106,8 +110,12 @@
         return true;
     }
     for (const auto& partition : metadata->partitions) {
+        if (!partition.num_extents) {
+            LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
+            continue;
+        }
         std::string path;
-        if (!CreateLogicalPartition(block_device, *metadata.get(), partition, &path)) {
+        if (!CreateLogicalPartition(block_device, *metadata.get(), partition, false, &path)) {
             LERROR << "Could not create logical partition: " << GetPartitionName(partition);
             return false;
         }
@@ -116,7 +124,8 @@
 }
 
 bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
-                            const std::string& partition_name, std::string* path) {
+                            const std::string& partition_name, bool force_writable,
+                            std::string* path) {
     auto metadata = ReadMetadata(block_device.c_str(), metadata_slot);
     if (!metadata) {
         LOG(ERROR) << "Could not read partition table.";
@@ -124,7 +133,8 @@
     }
     for (const auto& partition : metadata->partitions) {
         if (GetPartitionName(partition) == partition_name) {
-            return CreateLogicalPartition(block_device, *metadata.get(), partition, path);
+            return CreateLogicalPartition(block_device, *metadata.get(), partition, force_writable,
+                                          path);
         }
     }
     LERROR << "Could not find any partition with name: " << partition_name;
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index cac475c..f15c450 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -41,9 +41,11 @@
 
 // Create a block device for a single logical partition, given metadata and
 // the partition name. On success, a path to the partition's block device is
-// returned.
+// returned. If |force_writable| is true, the "readonly" flag will be ignored
+// so the partition can be flashed.
 bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
-                            const std::string& partition_name, std::string* path);
+                            const std::string& partition_name, bool force_writable,
+                            std::string* path);
 
 // Destroy the block device for a logical partition, by name.
 bool DestroyLogicalPartition(const std::string& name);
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index c564271..a9cfce4 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -31,7 +31,6 @@
 
 #include <deque>
 #include <iterator>
-#include <memory>
 #include <string>
 #include <vector>
 
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index 2a83bd9..bd6015e 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -16,15 +16,16 @@
 
 #define LOG_TAG "CallStack"
 
+#include <utils/CallStack.h>
+
+#include <memory>
+
 #include <utils/Printer.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 
 #include <backtrace/Backtrace.h>
 
-#define CALLSTACK_WEAK  // Don't generate weak definitions.
-#include <utils/CallStack.h>
-
 namespace android {
 
 CallStack::CallStack() {
@@ -75,26 +76,4 @@
     }
 }
 
-// The following four functions may be used via weak symbol references from libutils.
-// Clients assume that if any of these symbols are available, then deleteStack() is.
-
-CallStack::CallStackUPtr CallStack::getCurrentInternal(int ignoreDepth) {
-    CallStack::CallStackUPtr stack(new CallStack());
-    stack->update(ignoreDepth + 1);
-    return stack;
-}
-
-void CallStack::logStackInternal(const char* logtag, const CallStack* stack,
-                                 android_LogPriority priority) {
-    stack->log(logtag, priority);
-}
-
-String8 CallStack::stackToStringInternal(const char* prefix, const CallStack* stack) {
-    return stack->toString(prefix);
-}
-
-void CallStack::deleteStack(CallStack* stack) {
-    delete stack;
-}
-
 }; // namespace android
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 3f1e79a..9074850 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -17,41 +17,30 @@
 #define LOG_TAG "RefBase"
 // #define LOG_NDEBUG 0
 
-#include <memory>
-
 #include <utils/RefBase.h>
 
 #include <utils/CallStack.h>
 
-#include <utils/Mutex.h>
-
 #ifndef __unused
 #define __unused __attribute__((__unused__))
 #endif
 
-// Compile with refcounting debugging enabled.
-#define DEBUG_REFS 0
-
-// The following three are ignored unless DEBUG_REFS is set.
+// compile with refcounting debugging enabled
+#define DEBUG_REFS                      0
 
 // whether ref-tracking is enabled by default, if not, trackMe(true, false)
 // needs to be called explicitly
-#define DEBUG_REFS_ENABLED_BY_DEFAULT 0
+#define DEBUG_REFS_ENABLED_BY_DEFAULT   0
 
 // whether callstack are collected (significantly slows things down)
-#define DEBUG_REFS_CALLSTACK_ENABLED 1
+#define DEBUG_REFS_CALLSTACK_ENABLED    1
 
 // folder where stack traces are saved when DEBUG_REFS is enabled
 // this folder needs to exist and be writable
-#define DEBUG_REFS_CALLSTACK_PATH "/data/debug"
+#define DEBUG_REFS_CALLSTACK_PATH       "/data/debug"
 
 // log all reference counting operations
-#define PRINT_REFS 0
-
-// Continue after logging a stack trace if ~RefBase discovers that reference
-// count has never been incremented. Normally we conspicuously crash in that
-// case.
-#define DEBUG_REFBASE_DESTRUCTION 1
+#define PRINT_REFS                      0
 
 // ---------------------------------------------------------------------------
 
@@ -195,7 +184,7 @@
                 char inc = refs->ref >= 0 ? '+' : '-';
                 ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
 #if DEBUG_REFS_CALLSTACK_ENABLED
-                CallStack::logStack(LOG_TAG, refs->stack.get());
+                refs->stack.log(LOG_TAG);
 #endif
                 refs = refs->next;
             }
@@ -209,14 +198,14 @@
                 char inc = refs->ref >= 0 ? '+' : '-';
                 ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
 #if DEBUG_REFS_CALLSTACK_ENABLED
-                CallStack::logStack(LOG_TAG, refs->stack.get());
+                refs->stack.log(LOG_TAG);
 #endif
                 refs = refs->next;
             }
         }
         if (dumpStack) {
             ALOGE("above errors at:");
-            CallStack::logStack(LOG_TAG);
+            CallStack stack(LOG_TAG);
         }
     }
 
@@ -290,7 +279,7 @@
                      this);
             int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
             if (rc >= 0) {
-                (void)write(rc, text.string(), text.length());
+                write(rc, text.string(), text.length());
                 close(rc);
                 ALOGD("STACK TRACE for %p saved in %s", this, name);
             }
@@ -305,7 +294,7 @@
         ref_entry* next;
         const void* id;
 #if DEBUG_REFS_CALLSTACK_ENABLED
-        CallStack::CallStackUPtr stack;
+        CallStack stack;
 #endif
         int32_t ref;
     };
@@ -322,7 +311,7 @@
             ref->ref = mRef;
             ref->id = id;
 #if DEBUG_REFS_CALLSTACK_ENABLED
-            ref->stack = CallStack::getCurrent(2);
+            ref->stack.update(2);
 #endif
             ref->next = *refs;
             *refs = ref;
@@ -357,7 +346,7 @@
                 ref = ref->next;
             }
 
-            CallStack::logStack(LOG_TAG);
+            CallStack stack(LOG_TAG);
         }
     }
 
@@ -384,7 +373,7 @@
                      inc, refs->id, refs->ref);
             out->append(buf);
 #if DEBUG_REFS_CALLSTACK_ENABLED
-            out->append(CallStack::stackToString("\t\t", refs->stack.get()));
+            out->append(refs->stack.toString("\t\t"));
 #else
             out->append("\t\t(call stacks disabled)");
 #endif
@@ -711,16 +700,16 @@
         if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {
             delete mRefs;
         }
-    } else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
+    } else if (mRefs->mStrong.load(std::memory_order_relaxed)
+            == INITIAL_STRONG_VALUE) {
         // We never acquired a strong reference on this object.
-#if DEBUG_REFBASE_DESTRUCTION
-        // Treating this as fatal is prone to causing boot loops. For debugging, it's
-        // better to treat as non-fatal.
-        ALOGD("RefBase: Explicit destruction, weak count = %d (in %p)", mRefs->mWeak.load(), this);
-        CallStack::logStack(LOG_TAG);
-#else
-        LOG_ALWAYS_FATAL("RefBase: Explicit destruction, weak count = %d", mRefs->mWeak.load());
-#endif
+        LOG_ALWAYS_FATAL_IF(mRefs->mWeak.load() != 0,
+                "RefBase: Explicit destruction with non-zero weak "
+                "reference count");
+        // TODO: Always report if we get here. Currently MediaMetadataRetriever
+        // C++ objects are inconsistently managed and sometimes get here.
+        // There may be other cases, but we believe they should all be fixed.
+        delete mRefs;
     }
     // For debugging purposes, clear mRefs.  Ineffective against outstanding wp's.
     const_cast<weakref_impl*&>(mRefs) = nullptr;
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index a7b1d10..0c1b875 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -17,8 +17,6 @@
 #ifndef ANDROID_CALLSTACK_H
 #define ANDROID_CALLSTACK_H
 
-#include <memory>
-
 #include <android/log.h>
 #include <backtrace/backtrace_constants.h>
 #include <utils/String8.h>
@@ -27,11 +25,6 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#ifndef CALLSTACK_WEAK
-#define CALLSTACK_WEAK __attribute__((weak))
-#endif
-#define ALWAYS_INLINE __attribute__((always_inline))
-
 namespace android {
 
 class Printer;
@@ -43,7 +36,7 @@
     CallStack();
     // Create a callstack with the current thread's stack trace.
     // Immediately dump it to logcat using the given logtag.
-    CallStack(const char* logtag, int32_t ignoreDepth = 1);
+    CallStack(const char* logtag, int32_t ignoreDepth=1);
     ~CallStack();
 
     // Reset the stack frames (same as creating an empty call stack).
@@ -51,7 +44,7 @@
 
     // Immediately collect the stack traces for the specified thread.
     // The default is to dump the stack of the current call.
-    void update(int32_t ignoreDepth = 1, pid_t tid = BACKTRACE_CURRENT_THREAD);
+    void update(int32_t ignoreDepth=1, pid_t tid=BACKTRACE_CURRENT_THREAD);
 
     // Dump a stack trace to the log using the supplied logtag.
     void log(const char* logtag,
@@ -70,58 +63,7 @@
     // Get the count of stack frames that are in this call stack.
     size_t size() const { return mFrameLines.size(); }
 
-    // DO NOT USE ANYTHING BELOW HERE. The following public members are expected
-    // to disappear again shortly, once a better replacement facility exists.
-    // The replacement facility will be incompatible!
-
-    // Debugging accesses to some basic functionality. These use weak symbols to
-    // avoid introducing a dependency on libutilscallstack. Such a dependency from
-    // libutils results in a cyclic build dependency. These routines can be called
-    // from within libutils. But if the actual library is unavailable, they do
-    // nothing.
-    //
-    // DO NOT USE THESE. They will disappear.
-    struct StackDeleter {
-        void operator()(CallStack* stack) { deleteStack(stack); }
-    };
-
-    typedef std::unique_ptr<CallStack, StackDeleter> CallStackUPtr;
-
-    // Return current call stack if possible, nullptr otherwise.
-    static CallStackUPtr ALWAYS_INLINE getCurrent(int32_t ignoreDepth = 1) {
-        if (reinterpret_cast<uintptr_t>(getCurrentInternal) == 0) {
-            ALOGW("CallStack::getCurrentInternal not linked, returning null");
-            return CallStackUPtr(nullptr);
-        } else {
-            return getCurrentInternal(ignoreDepth);
-        }
-    }
-    static void ALWAYS_INLINE logStack(const char* logtag, CallStack* stack = getCurrent().get(),
-                                       android_LogPriority priority = ANDROID_LOG_DEBUG) {
-        if (reinterpret_cast<uintptr_t>(logStackInternal) != 0 && stack != nullptr) {
-            logStackInternal(logtag, stack, priority);
-        } else {
-            ALOGW("CallStack::logStackInternal not linked");
-        }
-    }
-    static String8 ALWAYS_INLINE stackToString(const char* prefix = nullptr,
-                                               const CallStack* stack = getCurrent().get()) {
-        if (reinterpret_cast<uintptr_t>(stackToStringInternal) != 0 && stack != nullptr) {
-            return stackToStringInternal(prefix, stack);
-        } else {
-            return String8("<CallStack package not linked>");
-        }
-    }
-
-  private:
-    static CallStackUPtr CALLSTACK_WEAK getCurrentInternal(int32_t ignoreDepth);
-    static void CALLSTACK_WEAK logStackInternal(const char* logtag, const CallStack* stack,
-                                                android_LogPriority priority);
-    static String8 CALLSTACK_WEAK stackToStringInternal(const char* prefix, const CallStack* stack);
-    // The deleter is only invoked on non-null pointers. Hence it will never be
-    // invoked if CallStack is not linked.
-    static void CALLSTACK_WEAK deleteStack(CallStack* stack);
-
+private:
     Vector<String8> mFrameLines;
 };