Merge "trusty: tipc_test: Read output and test result from ta2ta_ipc_test"
diff --git a/.clang-format-2 b/.clang-format-2
index 41591ce..ede5d7e 100644
--- a/.clang-format-2
+++ b/.clang-format-2
@@ -7,4 +7,3 @@
 PointerAlignment: Left
 TabWidth: 2
 UseTab: Never
-PenaltyExcessCharacter: 32
diff --git a/.clang-format-4 b/.clang-format-4
index 9127163..55773a2 100644
--- a/.clang-format-4
+++ b/.clang-format-4
@@ -9,4 +9,3 @@
 PointerAlignment: Left
 TabWidth: 4
 UseTab: Never
-PenaltyExcessCharacter: 32
diff --git a/adb/Android.bp b/adb/Android.bp
index 97c9762..2a9a579 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -19,17 +19,13 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-Wexit-time-destructors",
         "-Wno-unused-parameter",
         "-Wno-missing-field-initializers",
         "-Wvla",
     ],
     rtti: true,
 
-    clang_cflags: [
-        "-Wexit-time-destructors",
-        "-Wthread-safety",
-    ],
-
     use_version_lib: true,
 
     compile_multilib: "first",
@@ -85,6 +81,12 @@
                 "-luserenv",
             ],
         },
+
+        not_windows: {
+            cflags: [
+                "-Wthread-safety",
+            ],
+        },
     },
 }
 
@@ -297,6 +299,10 @@
         "libqemu_pipe",
         "libbase",
     ],
+
+    export_include_dirs: [
+        "daemon/include",
+    ],
 }
 
 cc_binary {
diff --git a/adb/adb.bash b/adb/adb.bash
index d36bec3..b1b3957 100644
--- a/adb/adb.bash
+++ b/adb/adb.bash
@@ -16,11 +16,11 @@
 #
 
 _adb() {
-    if ! type -t "$1" >/dev/null; then
+    if ! check_type "$1" >/dev/null; then
         return
     fi
 
-    if type -t _init_completion >/dev/null; then
+    if check_type _init_completion >/dev/null; then
         _init_completion || return
     fi
 
@@ -435,7 +435,7 @@
     fi
 
     # Since we're probably doing file completion here, don't add a space after.
-    if [[ $(type -t compopt) = "builtin" ]]; then
+    if [[ $(check_type compopt) == "builtin" ]]; then
         compopt -o nospace
     fi
 
@@ -451,7 +451,7 @@
     xspec=$2
 
     # Since we're probably doing file completion here, don't add a space after.
-    if [[ $(type -t compopt) = "builtin" ]]; then
+    if [[ $(check_type compopt) == "builtin" ]]; then
         compopt -o plusdirs
         if [[ "${xspec}" == "" ]]; then
             COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -f -- "${cur}") )
@@ -492,7 +492,7 @@
 }
 
 
-if [[ $(type -t compopt) = "builtin" ]]; then
+if [[ $(check_type compopt) == "builtin" ]]; then
     complete -F _adb adb
 else
     complete -o nospace -F _adb adb
diff --git a/adb/adb.cpp b/adb/adb.cpp
index f8a54c6..19300f6 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -264,15 +264,6 @@
     send_packet(cp, t);
 }
 
-// qual_overwrite is used to overwrite a qualifier string.  dst is a
-// pointer to a char pointer.  It is assumed that if *dst is non-NULL, it
-// was malloc'ed and needs to freed.  *dst will be set to a dup of src.
-// TODO: switch to std::string for these atransport fields instead.
-static void qual_overwrite(char** dst, const std::string& src) {
-    free(*dst);
-    *dst = strdup(src.c_str());
-}
-
 void parse_banner(const std::string& banner, atransport* t) {
     D("parse_banner: %s", banner.c_str());
 
@@ -296,11 +287,11 @@
             const std::string& key = key_value[0];
             const std::string& value = key_value[1];
             if (key == "ro.product.name") {
-                qual_overwrite(&t->product, value);
+                t->product = value;
             } else if (key == "ro.product.model") {
-                qual_overwrite(&t->model, value);
+                t->model = value;
             } else if (key == "ro.product.device") {
-                qual_overwrite(&t->device, value);
+                t->device = value;
             } else if (key == "features") {
                 t->SetFeatures(value);
             }
@@ -415,7 +406,7 @@
         if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) {
             asocket* s = find_local_socket(p->msg.arg1, 0);
             if (s) {
-                if(s->peer == 0) {
+                if(s->peer == nullptr) {
                     /* On first READY message, create the connection. */
                     s->peer = create_remote_socket(p->msg.arg0, t);
                     s->peer->peer = s;
@@ -424,8 +415,8 @@
                     /* Other READY messages must use the same local-id */
                     s->ready(s);
                 } else {
-                    D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s",
-                      p->msg.arg0, p->msg.arg1, s->peer->id, p->msg.arg1, t->serial);
+                    D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s", p->msg.arg0,
+                      p->msg.arg1, s->peer->id, p->msg.arg1, t->serial.c_str());
                 }
             } else {
                 // When receiving A_OKAY from device for A_OPEN request, the host server may
@@ -451,8 +442,8 @@
                  * socket has a peer on the same transport.
                  */
                 if (p->msg.arg0 == 0 && s->peer && s->peer->transport != t) {
-                    D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s",
-                      p->msg.arg1, t->serial, s->peer->transport->serial);
+                    D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s", p->msg.arg1,
+                      t->serial.c_str(), s->peer->transport->serial.c_str());
                 } else {
                     s->close(s);
                 }
@@ -933,25 +924,6 @@
 // This returns 1 on success, 0 on failure, and -1 to indicate this is not
 // a forwarding-related request.
 int handle_forward_request(const char* service, atransport* transport, int reply_fd) {
-    if (!strcmp(service, "list-forward")) {
-        // Create the list of forward redirections.
-        std::string listeners = format_listeners();
-#if ADB_HOST
-        SendOkay(reply_fd);
-#endif
-        return SendProtocolString(reply_fd, listeners);
-    }
-
-    if (!strcmp(service, "killforward-all")) {
-        remove_all_listeners();
-#if ADB_HOST
-        /* On the host: 1st OKAY is connect, 2nd OKAY is status */
-        SendOkay(reply_fd);
-#endif
-        SendOkay(reply_fd);
-        return 1;
-    }
-
     if (!strncmp(service, "forward:", 8) || !strncmp(service, "killforward:", 12)) {
         // killforward:local
         // forward:(norebind:)?local;remote
@@ -1171,7 +1143,7 @@
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
-            return SendOkay(reply_fd, t->serial ? t->serial : "unknown");
+            return SendOkay(reply_fd, !t->serial.empty() ? t->serial : "unknown");
         } else {
             return SendFail(reply_fd, error);
         }
@@ -1180,7 +1152,7 @@
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
-            return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown");
+            return SendOkay(reply_fd, !t->devpath.empty() ? t->devpath : "unknown");
         } else {
             return SendFail(reply_fd, error);
         }
@@ -1214,10 +1186,30 @@
         return SendOkay(reply_fd, response);
     }
 
+    if (!strcmp(service, "list-forward")) {
+        // Create the list of forward redirections.
+        std::string listeners = format_listeners();
+#if ADB_HOST
+        SendOkay(reply_fd);
+#endif
+        return SendProtocolString(reply_fd, listeners);
+    }
+
+    if (!strcmp(service, "killforward-all")) {
+        remove_all_listeners();
+#if ADB_HOST
+        /* On the host: 1st OKAY is connect, 2nd OKAY is status */
+        SendOkay(reply_fd);
+#endif
+        SendOkay(reply_fd);
+        return 1;
+    }
+
     std::string error;
     atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
     if (!t) {
-        return -1;
+        SendFail(reply_fd, error);
+        return 1;
     }
 
     int ret = handle_forward_request(service, t, reply_fd);
diff --git a/adb/adb.h b/adb/adb.h
index ede55da..7e9af9e 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -124,8 +124,6 @@
 
 void print_packet(const char* label, apacket* p);
 
-// These use the system (v)fprintf, not the adb prefixed ones defined in sysdeps.h, so they
-// shouldn't be tagged with ADB_FORMAT_ARCHETYPE.
 void fatal(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
 void fatal_errno(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
 
@@ -158,11 +156,6 @@
 
 int handle_forward_request(const char* service, atransport* transport, int reply_fd);
 
-#if !ADB_HOST
-void framebuffer_service(int fd, void* cookie);
-void set_verity_enabled_state_service(int fd, void* cookie);
-#endif
-
 /* packet allocator */
 apacket* get_apacket(void);
 void put_apacket(apacket* p);
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 38e3116..6cc274b 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -49,7 +49,7 @@
     }
     buf[4] = 0;
 
-    unsigned long len = strtoul(buf, 0, 16);
+    unsigned long len = strtoul(buf, nullptr, 16);
     s->resize(len, '\0');
     if (!ReadFdExactly(fd, &(*s)[0], len)) {
         *error = perror_str("protocol fault (couldn't read status message)");
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index ea5a44e..f4a92e3 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -136,9 +136,10 @@
         }
         //  <device-serial> " " <local-name> " " <remote-name> "\n"
         // Entries from "adb reverse" have no serial.
-        android::base::StringAppendF(&result, "%s %s %s\n",
-                                     l->transport->serial ? l->transport->serial : "(reverse)",
-                                     l->local_name.c_str(), l->connect_to.c_str());
+        android::base::StringAppendF(
+                &result, "%s %s %s\n",
+                !l->transport->serial.empty() ? l->transport->serial.c_str() : "(reverse)",
+                l->local_name.c_str(), l->connect_to.c_str());
     }
     return result;
 }
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index b236fb3..0c3327f 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -79,22 +79,24 @@
 }
 
 std::string escape_arg(const std::string& s) {
-  std::string result = s;
-
   // Escape any ' in the string (before we single-quote the whole thing).
   // The correct way to do this for the shell is to replace ' with '\'' --- that is,
   // close the existing single-quoted string, escape a single single-quote, and start
   // a new single-quoted string. Like the C preprocessor, the shell will concatenate
   // these pieces into one string.
-  for (size_t i = 0; i < s.size(); ++i) {
-    if (s[i] == '\'') {
-      result.insert(i, "'\\'");
-      i += 2;
-    }
+
+  std::string result;
+  result.push_back('\'');
+
+  size_t base = 0;
+  while (true) {
+    size_t found = s.find('\'', base);
+    result.append(s, base, found - base);
+    if (found == s.npos) break;
+    result.append("'\\''");
+    base = found + 1;
   }
 
-  // Prefix and suffix the whole string with '.
-  result.insert(result.begin(), '\'');
   result.push_back('\'');
   return result;
 }
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index e1b6287..341323f 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -82,30 +82,38 @@
 #endif
 
 TEST(adb_utils, escape_arg) {
-  ASSERT_EQ(R"('')", escape_arg(""));
+  EXPECT_EQ(R"('')", escape_arg(""));
 
-  ASSERT_EQ(R"('abc')", escape_arg("abc"));
+  EXPECT_EQ(R"('abc')", escape_arg("abc"));
 
-  ASSERT_EQ(R"(' abc')", escape_arg(" abc"));
-  ASSERT_EQ(R"(''\''abc')", escape_arg("'abc"));
-  ASSERT_EQ(R"('"abc')", escape_arg("\"abc"));
-  ASSERT_EQ(R"('\abc')", escape_arg("\\abc"));
-  ASSERT_EQ(R"('(abc')", escape_arg("(abc"));
-  ASSERT_EQ(R"(')abc')", escape_arg(")abc"));
+  auto wrap = [](const std::string& x) { return '\'' + x + '\''; };
+  const std::string q = R"('\'')";
+  EXPECT_EQ(wrap(q), escape_arg("'"));
+  EXPECT_EQ(wrap(q + q), escape_arg("''"));
+  EXPECT_EQ(wrap(q + "abc" + q), escape_arg("'abc'"));
+  EXPECT_EQ(wrap(q + "abc"), escape_arg("'abc"));
+  EXPECT_EQ(wrap("abc" + q), escape_arg("abc'"));
+  EXPECT_EQ(wrap("abc" + q + "def"), escape_arg("abc'def"));
+  EXPECT_EQ(wrap("a" + q + "b" + q + "c"), escape_arg("a'b'c"));
+  EXPECT_EQ(wrap("a" + q + "bcde" + q + "f"), escape_arg("a'bcde'f"));
 
-  ASSERT_EQ(R"('abc abc')", escape_arg("abc abc"));
-  ASSERT_EQ(R"('abc'\''abc')", escape_arg("abc'abc"));
-  ASSERT_EQ(R"('abc"abc')", escape_arg("abc\"abc"));
-  ASSERT_EQ(R"('abc\abc')", escape_arg("abc\\abc"));
-  ASSERT_EQ(R"('abc(abc')", escape_arg("abc(abc"));
-  ASSERT_EQ(R"('abc)abc')", escape_arg("abc)abc"));
+  EXPECT_EQ(R"(' abc')", escape_arg(" abc"));
+  EXPECT_EQ(R"('"abc')", escape_arg("\"abc"));
+  EXPECT_EQ(R"('\abc')", escape_arg("\\abc"));
+  EXPECT_EQ(R"('(abc')", escape_arg("(abc"));
+  EXPECT_EQ(R"(')abc')", escape_arg(")abc"));
 
-  ASSERT_EQ(R"('abc ')", escape_arg("abc "));
-  ASSERT_EQ(R"('abc'\''')", escape_arg("abc'"));
-  ASSERT_EQ(R"('abc"')", escape_arg("abc\""));
-  ASSERT_EQ(R"('abc\')", escape_arg("abc\\"));
-  ASSERT_EQ(R"('abc(')", escape_arg("abc("));
-  ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
+  EXPECT_EQ(R"('abc abc')", escape_arg("abc abc"));
+  EXPECT_EQ(R"('abc"abc')", escape_arg("abc\"abc"));
+  EXPECT_EQ(R"('abc\abc')", escape_arg("abc\\abc"));
+  EXPECT_EQ(R"('abc(abc')", escape_arg("abc(abc"));
+  EXPECT_EQ(R"('abc)abc')", escape_arg("abc)abc"));
+
+  EXPECT_EQ(R"('abc ')", escape_arg("abc "));
+  EXPECT_EQ(R"('abc"')", escape_arg("abc\""));
+  EXPECT_EQ(R"('abc\')", escape_arg("abc\\"));
+  EXPECT_EQ(R"('abc(')", escape_arg("abc("));
+  EXPECT_EQ(R"('abc)')", escape_arg("abc)"));
 }
 
 void test_mkdirs(const std::string& basepath) {
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index 849a6e7..1959258 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -46,7 +46,7 @@
 #include "sysdeps/chrono.h"
 
 static TransportType __adb_transport = kTransportAny;
-static const char* __adb_serial = NULL;
+static const char* __adb_serial = nullptr;
 static TransportId __adb_transport_id = 0;
 
 static const char* __adb_server_socket_spec;
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index 0f4dd33..5fbef09 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -109,7 +109,7 @@
     LOG(INFO) << "generate_key(" << file << ")...";
 
     mode_t old_mask;
-    FILE *f = NULL;
+    FILE *f = nullptr;
     int ret = 0;
 
     EVP_PKEY* pkey = EVP_PKEY_new();
@@ -121,7 +121,7 @@
     }
 
     BN_set_word(exponent, RSA_F4);
-    RSA_generate_key_ex(rsa, 2048, exponent, NULL);
+    RSA_generate_key_ex(rsa, 2048, exponent, nullptr);
     EVP_PKEY_set1_RSA(pkey, rsa);
 
     old_mask = umask(077);
@@ -135,7 +135,7 @@
 
     umask(old_mask);
 
-    if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
+    if (!PEM_write_PrivateKey(f, pkey, nullptr, nullptr, 0, nullptr, nullptr)) {
         D("Failed to write key");
         goto out;
     }
@@ -302,7 +302,7 @@
 static std::string adb_auth_sign(RSA* key, const char* token, size_t token_size) {
     if (token_size != TOKEN_SIZE) {
         D("Unexpected token size %zd", token_size);
-        return 0;
+        return nullptr;
     }
 
     std::string result;
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index e07dba7..7791895 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -362,9 +362,8 @@
 }
 
 static void copy_to_file(int inFd, int outFd) {
-    const size_t BUFSIZE = 32 * 1024;
-    char* buf = (char*) malloc(BUFSIZE);
-    if (buf == nullptr) fatal("couldn't allocate buffer for copy_to_file");
+    constexpr size_t BUFSIZE = 32 * 1024;
+    std::vector<char> buf(BUFSIZE);
     int len;
     long total = 0;
     int old_stdin_mode = -1;
@@ -376,9 +375,9 @@
 
     while (true) {
         if (inFd == STDIN_FILENO) {
-            len = unix_read(inFd, buf, BUFSIZE);
+            len = unix_read(inFd, buf.data(), BUFSIZE);
         } else {
-            len = adb_read(inFd, buf, BUFSIZE);
+            len = adb_read(inFd, buf.data(), BUFSIZE);
         }
         if (len == 0) {
             D("copy_to_file() : read 0 bytes; exiting");
@@ -389,10 +388,10 @@
             break;
         }
         if (outFd == STDOUT_FILENO) {
-            fwrite(buf, 1, len, stdout);
+            fwrite(buf.data(), 1, len, stdout);
             fflush(stdout);
         } else {
-            adb_write(outFd, buf, len);
+            adb_write(outFd, buf.data(), len);
         }
         total += len;
     }
@@ -400,7 +399,6 @@
     stdinout_raw_epilogue(inFd, outFd, old_stdin_mode, old_stdout_mode);
 
     D("copy_to_file() finished after %lu bytes", total);
-    free(buf);
 }
 
 static void send_window_size_change(int fd, std::unique_ptr<ShellProtocol>& shell) {
@@ -885,7 +883,7 @@
             return 0;
         }
 
-        int block = strtol(buf, NULL, 10);
+        int block = strtol(buf, nullptr, 10);
 
         size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
         if (offset >= static_cast<size_t>(sb.st_size)) {
@@ -968,7 +966,7 @@
             //argv[2] and beyond become ppp_args[1] and beyond
             ppp_args[i - 1] = argv[i];
         }
-        ppp_args[i-1] = NULL;
+        ppp_args[i-1] = nullptr;
 
         // child side
 
@@ -1142,24 +1140,22 @@
 static void write_zeros(int bytes, int fd) {
     int old_stdin_mode = -1;
     int old_stdout_mode = -1;
-    char* buf = (char*) calloc(1, bytes);
-    if (buf == nullptr) fatal("couldn't allocate buffer for write_zeros");
+    std::vector<char> buf(bytes);
 
     D("write_zeros(%d) -> %d", bytes, fd);
 
     stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
 
     if (fd == STDOUT_FILENO) {
-        fwrite(buf, 1, bytes, stdout);
+        fwrite(buf.data(), 1, bytes, stdout);
         fflush(stdout);
     } else {
-        adb_write(fd, buf, bytes);
+        adb_write(fd, buf.data(), bytes);
     }
 
     stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
 
     D("write_zeros() finished");
-    free(buf);
 }
 
 static int backup(int argc, const char** argv) {
@@ -1174,7 +1170,7 @@
                 argv[i++] = argv[j++];
             }
             argc -= 2;
-            argv[argc] = NULL;
+            argv[argc] = nullptr;
         }
     }
 
@@ -1969,7 +1965,7 @@
         char* end = strrchr(buf, ']');
         if (start && end) {
             *end = '\0';
-            session_id = strtol(start + 1, NULL, 10);
+            session_id = strtol(start + 1, nullptr, 10);
         }
     }
     if (session_id < 0) {
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
index 3d10030..3aa03a7 100644
--- a/adb/client/commandline.h
+++ b/adb/client/commandline.h
@@ -83,6 +83,14 @@
     DISALLOW_COPY_AND_ASSIGN(DefaultStandardStreamsCallback);
 };
 
+class SilentStandardStreamsCallbackInterface : public StandardStreamsCallbackInterface {
+  public:
+    SilentStandardStreamsCallbackInterface() = default;
+    void OnStdout(const char*, int) override final {}
+    void OnStderr(const char*, int) override final {}
+    int Done(int status) override final { return status; }
+};
+
 // Singleton.
 extern DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK;
 
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index 26f8d83..a438dbb 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -44,6 +44,8 @@
 #include "sysdeps/errno.h"
 #include "sysdeps/stat.h"
 
+#include "client/commandline.h"
+
 #include <android-base/file.h>
 #include <android-base/strings.h>
 #include <android-base/stringprintf.h>
@@ -202,12 +204,11 @@
         max = SYNC_DATA_MAX; // TODO: decide at runtime.
 
         std::string error;
-        FeatureSet features;
-        if (!adb_get_feature_set(&features, &error)) {
+        if (!adb_get_feature_set(&features_, &error)) {
             fd = -1;
             Error("failed to get feature set: %s", error.c_str());
         } else {
-            have_stat_v2_ = CanUseFeature(features, kFeatureStat2);
+            have_stat_v2_ = CanUseFeature(features_, kFeatureStat2);
             fd = adb_connect("sync:", &error);
             if (fd < 0) {
                 Error("connect failed: %s", error.c_str());
@@ -232,6 +233,8 @@
         line_printer_.KeepInfoLine();
     }
 
+    const FeatureSet& Features() const { return features_; }
+
     bool IsValid() { return fd >= 0; }
 
     bool ReceivedError(const char* from, const char* to) {
@@ -510,8 +513,7 @@
         return false;
     }
 
-
-    void Printf(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+    void Printf(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
         std::string s;
 
         va_list ap;
@@ -522,7 +524,7 @@
         line_printer_.Print(s, LinePrinter::INFO);
     }
 
-    void Println(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+    void Println(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
         std::string s;
 
         va_list ap;
@@ -534,7 +536,7 @@
         line_printer_.KeepInfoLine();
     }
 
-    void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+    void Error(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
         std::string s = "adb: error: ";
 
         va_list ap;
@@ -545,7 +547,7 @@
         line_printer_.Print(s, LinePrinter::ERROR);
     }
 
-    void Warning(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+    void Warning(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
         std::string s = "adb: warning: ";
 
         va_list ap;
@@ -577,6 +579,7 @@
 
   private:
     bool expect_done_;
+    FeatureSet features_;
     bool have_stat_v2_;
 
     TransferLedger global_ledger_;
@@ -806,7 +809,7 @@
 }
 
 static bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list,
-                             const std::string& lpath,
+                             std::vector<std::string>* directory_list, const std::string& lpath,
                              const std::string& rpath) {
     std::vector<copyinfo> dirlist;
     std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(lpath.c_str()), closedir);
@@ -849,21 +852,9 @@
     // Close this directory and recurse.
     dir.reset();
 
-    // Add the current directory to the list if it was empty, to ensure that
-    // it gets created.
-    if (empty_dir) {
-        // TODO(b/25566053): Make pushing empty directories work.
-        // TODO(b/25457350): We don't preserve permissions on directories.
-        sc.Warning("skipping empty directory '%s'", lpath.c_str());
-        copyinfo ci(android::base::Dirname(lpath), android::base::Dirname(rpath),
-                    android::base::Basename(lpath), S_IFDIR);
-        ci.skip = true;
-        file_list->push_back(ci);
-        return true;
-    }
-
     for (const copyinfo& ci : dirlist) {
-        local_build_list(sc, file_list, ci.lpath, ci.rpath);
+        directory_list->push_back(ci.rpath);
+        local_build_list(sc, file_list, directory_list, ci.lpath, ci.rpath);
     }
 
     return true;
@@ -880,11 +871,54 @@
 
     // Recursively build the list of files to copy.
     std::vector<copyinfo> file_list;
+    std::vector<std::string> directory_list;
+
+    for (std::string dirpath = rpath; dirpath != "/"; dirpath = android::base::Dirname(dirpath)) {
+        directory_list.push_back(dirpath);
+    }
+    std::reverse(directory_list.begin(), directory_list.end());
+
     int skipped = 0;
-    if (!local_build_list(sc, &file_list, lpath, rpath)) {
+    if (!local_build_list(sc, &file_list, &directory_list, lpath, rpath)) {
         return false;
     }
 
+    // b/110953234:
+    // P shipped with a bug that causes directory creation as a side-effect of a push to fail.
+    // Work around this by explicitly doing a mkdir via shell.
+    //
+    // Devices that don't support shell_v2 are unhappy if we try to send a too-long packet to them,
+    // but they're not affected by this bug, so only apply the workaround if we have shell_v2.
+    //
+    // TODO(b/25457350): We don't preserve permissions on directories.
+    // TODO: Find all of the leaves and `mkdir -p` them instead?
+    if (CanUseFeature(sc.Features(), kFeatureShell2)) {
+        SilentStandardStreamsCallbackInterface cb;
+        std::string cmd = "mkdir";
+        for (const auto& dir : directory_list) {
+            std::string escaped_path = escape_arg(dir);
+            if (escaped_path.size() > 16384) {
+                // Somewhat arbitrarily limit that probably won't ever happen.
+                sc.Error("path too long: %s", escaped_path.c_str());
+                return false;
+            }
+
+            // The maximum should be 64kiB, but that's not including other stuff that gets tacked
+            // onto the command line, so let's be a bit conservative.
+            if (cmd.size() + escaped_path.size() > 32768) {
+                // Dispatch the command, ignoring failure (since the directory might already exist).
+                send_shell_command(cmd, false, &cb);
+                cmd = "mkdir";
+            }
+            cmd += " ";
+            cmd += escaped_path;
+        }
+
+        if (cmd != "mkdir") {
+            send_shell_command(cmd, false, &cb);
+        }
+    }
+
     if (check_timestamps) {
         for (const copyinfo& ci : file_list) {
             if (!sc.SendLstat(ci.rpath.c_str())) {
diff --git a/adb/client/line_printer.cpp b/adb/client/line_printer.cpp
index 64d10b6..9758526 100644
--- a/adb/client/line_printer.cpp
+++ b/adb/client/line_printer.cpp
@@ -52,7 +52,7 @@
   // MSDN says: "For some systems, [_IOLBF] provides line
   // buffering. However, for Win32, the behavior is the same as _IOFBF
   // - Full Buffering."
-  setvbuf(stdout, NULL, _IONBF, 0);
+  setvbuf(stdout, nullptr, _IONBF, 0);
   console_ = GetStdHandle(STD_OUTPUT_HANDLE);
   CONSOLE_SCREEN_BUFFER_INFO csbi;
   smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 44ed3a2..de6c723 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -89,10 +89,10 @@
     // unbuffer stdout and stderr just like if we were run at the console.
     // This also keeps stderr unbuffered when it is redirected to adb.log.
     if (is_daemon) {
-        if (setvbuf(stdout, NULL, _IONBF, 0) == -1) {
+        if (setvbuf(stdout, nullptr, _IONBF, 0) == -1) {
             fatal("cannot make stdout unbuffered: %s", strerror(errno));
         }
-        if (setvbuf(stderr, NULL, _IONBF, 0) == -1) {
+        if (setvbuf(stderr, nullptr, _IONBF, 0) == -1) {
             fatal("cannot make stderr unbuffered: %s", strerror(errno));
         }
     }
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 46c3f58..10b6090 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -589,7 +589,7 @@
 
     int rc = perform_usb_transfer(h, info, std::move(lock));
     LOG(DEBUG) << "usb_write(" << len << ") = " << rc;
-    return rc;
+    return info->transfer->actual_length;
 }
 
 int usb_read(usb_handle* h, void* d, int len) {
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 1f376a4..869e858 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -128,7 +128,7 @@
     if (!bus_dir) return;
 
     dirent* de;
-    while ((de = readdir(bus_dir.get())) != 0) {
+    while ((de = readdir(bus_dir.get())) != nullptr) {
         if (contains_non_digit(de->d_name)) continue;
 
         std::string bus_name = base + "/" + de->d_name;
@@ -418,11 +418,11 @@
     if (h->zero_mask && !(len & h->zero_mask)) {
         // If we need 0-markers and our transfer is an even multiple of the packet size,
         // then send a zero marker.
-        return usb_bulk_write(h, _data, 0);
+        return usb_bulk_write(h, _data, 0) == 0 ? n : -1;
     }
 
     D("-- usb_write --");
-    return 0;
+    return n;
 }
 
 int usb_read(usb_handle *h, void *_data, int len)
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index 8a95a19..49baf36 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -497,8 +497,8 @@
         }
     }
 
-    if (0 == result)
-        return 0;
+    if (!result)
+        return len;
 
     LOG(ERROR) << "usb_write failed with status: " << std::hex << result;
     return -1;
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index f529e8f..e928377 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -126,11 +126,11 @@
 int usb_close(usb_handle* handle);
 
 int known_device_locked(const wchar_t* dev_name) {
-    if (NULL != dev_name) {
+    if (nullptr != dev_name) {
         // Iterate through the list looking for the name match.
         for (usb_handle* usb : handle_list) {
             // In Windows names are not case sensetive!
-            if ((NULL != usb->interface_name) && (0 == wcsicmp(usb->interface_name, dev_name))) {
+            if ((nullptr != usb->interface_name) && (0 == wcsicmp(usb->interface_name, dev_name))) {
                 return 1;
             }
         }
@@ -142,7 +142,7 @@
 int known_device(const wchar_t* dev_name) {
     int ret = 0;
 
-    if (NULL != dev_name) {
+    if (nullptr != dev_name) {
         std::lock_guard<std::mutex> lock(usb_lock);
         ret = known_device_locked(dev_name);
     }
@@ -151,7 +151,7 @@
 }
 
 int register_new_device(usb_handle* handle) {
-    if (NULL == handle) return 0;
+    if (nullptr == handle) return 0;
 
     std::lock_guard<std::mutex> lock(usb_lock);
 
@@ -209,7 +209,7 @@
 
     // Get the HINSTANCE corresponding to the module that _power_window_proc
     // is in (the main module).
-    const HINSTANCE instance = GetModuleHandleW(NULL);
+    const HINSTANCE instance = GetModuleHandleW(nullptr);
     if (!instance) {
         // This is such a common API call that this should never fail.
         fatal("GetModuleHandleW failed: %s",
@@ -228,14 +228,14 @@
     }
 
     if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
-                         L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0, NULL, NULL,
-                         instance, NULL)) {
+                         L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
+                         instance, nullptr)) {
         fatal("CreateWindowExW failed: %s",
               android::base::SystemErrorCodeToString(GetLastError()).c_str());
     }
 
     MSG msg;
-    while (GetMessageW(&msg, NULL, 0, 0)) {
+    while (GetMessageW(&msg, nullptr, 0, 0)) {
         TranslateMessage(&msg);
         DispatchMessageW(&msg);
     }
@@ -259,14 +259,14 @@
 
     // Allocate our handle
     usb_handle* ret = (usb_handle*)calloc(1, sizeof(usb_handle));
-    if (NULL == ret) {
+    if (nullptr == ret) {
         D("Could not allocate %u bytes for usb_handle: %s", sizeof(usb_handle), strerror(errno));
         goto fail;
     }
 
     // Create interface.
     ret->adb_interface = AdbCreateInterfaceByName(interface_name);
-    if (NULL == ret->adb_interface) {
+    if (nullptr == ret->adb_interface) {
         D("AdbCreateInterfaceByName failed: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
         goto fail;
@@ -275,7 +275,7 @@
     // Open read pipe (endpoint)
     ret->adb_read_pipe = AdbOpenDefaultBulkReadEndpoint(
         ret->adb_interface, AdbOpenAccessTypeReadWrite, AdbOpenSharingModeReadWrite);
-    if (NULL == ret->adb_read_pipe) {
+    if (nullptr == ret->adb_read_pipe) {
         D("AdbOpenDefaultBulkReadEndpoint failed: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
         goto fail;
@@ -284,7 +284,7 @@
     // Open write pipe (endpoint)
     ret->adb_write_pipe = AdbOpenDefaultBulkWriteEndpoint(
         ret->adb_interface, AdbOpenAccessTypeReadWrite, AdbOpenSharingModeReadWrite);
-    if (NULL == ret->adb_write_pipe) {
+    if (nullptr == ret->adb_write_pipe) {
         D("AdbOpenDefaultBulkWriteEndpoint failed: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
         goto fail;
@@ -292,7 +292,7 @@
 
     // Save interface name
     // First get expected name length
-    AdbGetInterfaceName(ret->adb_interface, NULL, &name_len, false);
+    AdbGetInterfaceName(ret->adb_interface, nullptr, &name_len, false);
     if (0 == name_len) {
         D("AdbGetInterfaceName returned name length of zero: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
@@ -300,7 +300,7 @@
     }
 
     ret->interface_name = (wchar_t*)malloc(name_len * sizeof(ret->interface_name[0]));
-    if (NULL == ret->interface_name) {
+    if (nullptr == ret->interface_name) {
         D("Could not allocate %lu characters for interface_name: %s", name_len, strerror(errno));
         goto fail;
     }
@@ -316,12 +316,12 @@
     return ret;
 
 fail:
-    if (NULL != ret) {
+    if (nullptr != ret) {
         usb_cleanup_handle(ret);
         free(ret);
     }
 
-    return NULL;
+    return nullptr;
 }
 
 int usb_write(usb_handle* handle, const void* data, int len) {
@@ -330,7 +330,7 @@
     int err = 0;
 
     D("usb_write %d", len);
-    if (NULL == handle) {
+    if (nullptr == handle) {
         D("usb_write was passed NULL handle");
         err = EINVAL;
         goto fail;
@@ -365,12 +365,12 @@
         }
     }
 
-    return 0;
+    return written;
 
 fail:
     // Any failure should cause us to kick the device instead of leaving it a
     // zombie state with potential to hang.
-    if (NULL != handle) {
+    if (nullptr != handle) {
         D("Kicking device due to error in usb_write");
         usb_kick(handle);
     }
@@ -387,7 +387,7 @@
     int orig_len = len;
 
     D("usb_read %d", len);
-    if (NULL == handle) {
+    if (nullptr == handle) {
         D("usb_read was passed NULL handle");
         err = EINVAL;
         goto fail;
@@ -411,7 +411,7 @@
 fail:
     // Any failure should cause us to kick the device instead of leaving it a
     // zombie state with potential to hang.
-    if (NULL != handle) {
+    if (nullptr != handle) {
         D("Kicking device due to error in usb_read");
         usb_kick(handle);
     }
@@ -431,19 +431,19 @@
 
 void usb_cleanup_handle(usb_handle* handle) {
     D("usb_cleanup_handle");
-    if (NULL != handle) {
-        if (NULL != handle->interface_name) free(handle->interface_name);
+    if (nullptr != handle) {
+        if (nullptr != handle->interface_name) free(handle->interface_name);
         // AdbCloseHandle(pipe) will break any threads out of pending IO calls and
         // wait until the pipe no longer uses the interface. Then we can
         // AdbCloseHandle() the interface.
-        if (NULL != handle->adb_write_pipe) _adb_close_handle(handle->adb_write_pipe);
-        if (NULL != handle->adb_read_pipe) _adb_close_handle(handle->adb_read_pipe);
-        if (NULL != handle->adb_interface) _adb_close_handle(handle->adb_interface);
+        if (nullptr != handle->adb_write_pipe) _adb_close_handle(handle->adb_write_pipe);
+        if (nullptr != handle->adb_read_pipe) _adb_close_handle(handle->adb_read_pipe);
+        if (nullptr != handle->adb_interface) _adb_close_handle(handle->adb_interface);
 
-        handle->interface_name = NULL;
-        handle->adb_write_pipe = NULL;
-        handle->adb_read_pipe = NULL;
-        handle->adb_interface = NULL;
+        handle->interface_name = nullptr;
+        handle->adb_write_pipe = nullptr;
+        handle->adb_read_pipe = nullptr;
+        handle->adb_interface = nullptr;
     }
 }
 
@@ -455,7 +455,7 @@
 
 void usb_kick(usb_handle* handle) {
     D("usb_kick");
-    if (NULL != handle) {
+    if (nullptr != handle) {
         std::lock_guard<std::mutex> lock(usb_lock);
         usb_kick_locked(handle);
     } else {
@@ -466,7 +466,7 @@
 int usb_close(usb_handle* handle) {
     D("usb_close");
 
-    if (NULL != handle) {
+    if (nullptr != handle) {
         // Remove handle from the list
         {
             std::lock_guard<std::mutex> lock(usb_lock);
@@ -487,7 +487,7 @@
 }
 
 int recognized_device(usb_handle* handle) {
-    if (NULL == handle) return 0;
+    if (nullptr == handle) return 0;
 
     // Check vendor and product id first
     USB_DEVICE_DESCRIPTOR device_desc;
@@ -532,7 +532,7 @@
 }
 
 void find_devices() {
-    usb_handle* handle = NULL;
+    usb_handle* handle = nullptr;
     char entry_buffer[2048];
     AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
     unsigned long entry_buffer_size = sizeof(entry_buffer);
@@ -540,7 +540,7 @@
     // Enumerate all present and active interfaces.
     ADBAPIHANDLE enum_handle = AdbEnumInterfaces(usb_class_id, true, true, true);
 
-    if (NULL == enum_handle) {
+    if (nullptr == enum_handle) {
         D("AdbEnumInterfaces failed: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return;
@@ -551,7 +551,7 @@
         if (!known_device(next_interface->device_name)) {
             // This seems to be a new device. Open it!
             handle = do_usb_open(next_interface->device_name);
-            if (NULL != handle) {
+            if (nullptr != handle) {
                 // Lets see if this interface (device) belongs to us
                 if (recognized_device(handle)) {
                     D("adding a new device %ls", next_interface->device_name);
@@ -569,7 +569,7 @@
                                            true)) {
                         // Lets make sure that we don't duplicate this device
                         if (register_new_device(handle)) {
-                            register_usb_transport(handle, serial_number, NULL, 1);
+                            register_usb_transport(handle, serial_number, nullptr, 1);
                         } else {
                             D("register_new_device failed for %ls", next_interface->device_name);
                             usb_cleanup_handle(handle);
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
index f0c3629..180df8f 100644
--- a/adb/daemon/auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -100,7 +100,7 @@
 
 static void usb_disconnected(void* unused, atransport* t) {
     LOG(INFO) << "USB disconnect";
-    usb_transport = NULL;
+    usb_transport = nullptr;
     needs_retry = false;
 }
 
@@ -200,7 +200,7 @@
         return;
     }
 
-    listener_fde = fdevent_create(fd, adbd_auth_listener, NULL);
+    listener_fde = fdevent_create(fd, adbd_auth_listener, nullptr);
     fdevent_add(listener_fde, FDE_READ);
 }
 
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index 1128993..0b71363 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -69,17 +69,23 @@
 }
 
 static bool secure_mkdirs(const std::string& path) {
-    uid_t uid = -1;
-    gid_t gid = -1;
-    unsigned int mode = 0775;
-    uint64_t capabilities = 0;
-
     if (path[0] != '/') return false;
 
     std::vector<std::string> path_components = android::base::Split(path, "/");
     std::string partial_path;
     for (const auto& path_component : path_components) {
-        if (partial_path.back() != OS_PATH_SEPARATOR) partial_path += OS_PATH_SEPARATOR;
+        uid_t uid = -1;
+        gid_t gid = -1;
+        unsigned int mode = 0775;
+        uint64_t capabilities = 0;
+
+        if (path_component.empty()) {
+            continue;
+        }
+
+        if (partial_path.empty() || partial_path.back() != OS_PATH_SEPARATOR) {
+            partial_path += OS_PATH_SEPARATOR;
+        }
         partial_path += path_component;
 
         if (should_use_fs_config(partial_path)) {
@@ -519,12 +525,11 @@
     return true;
 }
 
-void file_sync_service(int fd, void*) {
+void file_sync_service(android::base::unique_fd fd) {
     std::vector<char> buffer(SYNC_DATA_MAX);
 
-    while (handle_sync_command(fd, buffer)) {
+    while (handle_sync_command(fd.get(), buffer)) {
     }
 
     D("sync: done");
-    adb_close(fd);
 }
diff --git a/adb/daemon/framebuffer_service.cpp b/adb/daemon/framebuffer_service.cpp
index 6c3a225..9a620ab 100644
--- a/adb/daemon/framebuffer_service.cpp
+++ b/adb/daemon/framebuffer_service.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "framebuffer_service.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/fb.h>
@@ -55,8 +57,7 @@
     unsigned int alpha_length;
 } __attribute__((packed));
 
-void framebuffer_service(int fd, void *cookie)
-{
+void framebuffer_service(android::base::unique_fd fd) {
     struct fbinfo fbinfo;
     unsigned int i, bsize;
     char buf[640];
@@ -65,7 +66,7 @@
     int fds[2];
     pid_t pid;
 
-    if (pipe2(fds, O_CLOEXEC) < 0) goto pipefail;
+    if (pipe2(fds, O_CLOEXEC) < 0) return;
 
     pid = fork();
     if (pid < 0) goto done;
@@ -75,7 +76,7 @@
         adb_close(fds[0]);
         adb_close(fds[1]);
         const char* command = "screencap";
-        const char *args[2] = {command, NULL};
+        const char *args[2] = {command, nullptr};
         execvp(command, (char**)args);
         exit(1);
     }
@@ -168,7 +169,7 @@
     }
 
     /* write header */
-    if(!WriteFdExactly(fd, &fbinfo, sizeof(fbinfo))) goto done;
+    if (!WriteFdExactly(fd.get(), &fbinfo, sizeof(fbinfo))) goto done;
 
     /* write data */
     for(i = 0; i < fbinfo.size; i += bsize) {
@@ -176,13 +177,11 @@
       if (i + bsize > fbinfo.size)
         bsize = fbinfo.size - i;
       if(!ReadFdExactly(fd_screencap, buf, bsize)) goto done;
-      if(!WriteFdExactly(fd, buf, bsize)) goto done;
+      if (!WriteFdExactly(fd.get(), buf, bsize)) goto done;
     }
 
 done:
     adb_close(fds[0]);
 
-    TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
-pipefail:
-    adb_close(fd);
+    TEMP_FAILURE_RETRY(waitpid(pid, nullptr, 0));
 }
diff --git a/init/init_first_stage.h b/adb/daemon/framebuffer_service.h
similarity index 67%
copy from init/init_first_stage.h
copy to adb/daemon/framebuffer_service.h
index c7a3867..d99c6fe 100644
--- a/init/init_first_stage.h
+++ b/adb/daemon/framebuffer_service.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,16 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_FIRST_STAGE_H
-#define _INIT_FIRST_STAGE_H
+#ifndef _DAEMON_FRAMEBUFFER_SERVICE_H_
+#define _DAEMON_FRAMEBUFFER_SERVICE_H_
 
-namespace android {
-namespace init {
+#include <android-base/unique_fd.h>
 
-bool DoFirstStageMount();
-void SetInitAvbVersionInRecovery();
+void framebuffer_service(android::base::unique_fd fd);
 
-}  // namespace init
-}  // namespace android
-
-#endif
+#endif  // _DAEMON_FRAMEBUFFER_SERVICE_H_
diff --git a/adb/daemon/usb.h b/adb/daemon/include/adbd/usb.h
similarity index 92%
rename from adb/daemon/usb.h
rename to adb/daemon/include/adbd/usb.h
index 15a7f65..7905d9d 100644
--- a/adb/daemon/usb.h
+++ b/adb/daemon/include/adbd/usb.h
@@ -19,6 +19,7 @@
 #include <atomic>
 #include <condition_variable>
 #include <mutex>
+#include <vector>
 
 #include <asyncio/AsyncIO.h>
 
@@ -54,5 +55,9 @@
     // read and write threads.
     struct aio_block read_aiob;
     struct aio_block write_aiob;
+
+    bool reads_zero_packets;
+    size_t io_size;
 };
 
+usb_handle *create_usb_handle(unsigned num_bufs, unsigned io_size);
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index 367695d..175e82e 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -264,7 +264,7 @@
 
             iov.iov_base = &dummy;
             iov.iov_len = 1;
-            msg.msg_name = NULL;
+            msg.msg_name = nullptr;
             msg.msg_namelen = 0;
             msg.msg_iov = &iov;
             msg.msg_iovlen = 1;
@@ -393,7 +393,7 @@
     control->listen_socket = s;
 
     control->fde = fdevent_create(s, jdwp_control_event, control);
-    if (control->fde == NULL) {
+    if (control->fde == nullptr) {
         D("could not create fdevent for jdwp control socket");
         adb_close(s);
         return -1;
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 830b35d..1bb2fbb 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mount.h>
+#include <sys/statvfs.h>
 #include <sys/vfs.h>
 #include <unistd.h>
 
@@ -43,6 +44,7 @@
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
 #include "fs_mgr.h"
+#include "set_verity_enable_state_service.h"
 
 // Returns the device used to mount a directory in /proc/mounts.
 static std::string find_proc_mount(const char* dir) {
@@ -145,6 +147,17 @@
     return true;
 }
 
+static unsigned long get_mount_flags(int fd, const char* dir) {
+    struct statvfs st_vfs;
+    if (statvfs(dir, &st_vfs) == -1) {
+        // Even though we could not get the original mount flags, assume that
+        // the mount was originally read-only.
+        WriteFdFmt(fd, "statvfs of the %s mount failed: %s.\n", dir, strerror(errno));
+        return MS_RDONLY;
+    }
+    return st_vfs.f_flag;
+}
+
 static bool remount_partition(int fd, const char* dir) {
     if (!directory_exists(dir)) {
         return true;
@@ -163,7 +176,12 @@
                    dir, dev.c_str(), strerror(errno));
         return false;
     }
-    if (mount(dev.c_str(), dir, "none", MS_REMOUNT | MS_BIND, nullptr) == -1) {
+
+    unsigned long remount_flags = get_mount_flags(fd, dir);
+    remount_flags &= ~MS_RDONLY;
+    remount_flags |= MS_REMOUNT;
+
+    if (mount(dev.c_str(), dir, "none", remount_flags | MS_BIND, nullptr) == -1) {
         // This is useful for cases where the superblock is already marked as
         // read-write, but the mount itself is read-only, such as containers
         // where the remount with just MS_REMOUNT is forbidden by the kernel.
@@ -197,14 +215,11 @@
     android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd.c_str());
 }
 
-void remount_service(int fd, void* cookie) {
-    unique_fd close_fd(fd);
-
-    const char* cmd = reinterpret_cast<const char*>(cookie);
-    bool user_requested_reboot = cmd && !strcmp(cmd, "-R");
+void remount_service(android::base::unique_fd fd, const std::string& cmd) {
+    bool user_requested_reboot = cmd != "-R";
 
     if (getuid() != 0) {
-        WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
+        WriteFdExactly(fd.get(), "Not running as root. Try \"adb root\" first.\n");
         return;
     }
 
@@ -225,7 +240,7 @@
         if (dev.empty() || !fs_has_shared_blocks(dev.c_str())) {
             continue;
         }
-        if (can_unshare_blocks(fd, dev.c_str())) {
+        if (can_unshare_blocks(fd.get(), dev.c_str())) {
             dedup.emplace(partition);
         }
     }
@@ -236,12 +251,12 @@
     if (user_requested_reboot) {
         if (!dedup.empty() || verity_enabled) {
             if (verity_enabled) {
-                set_verity_enabled_state_service(fd, nullptr);
+                set_verity_enabled_state_service(android::base::unique_fd(dup(fd.get())), false);
             }
-            reboot_for_remount(fd, !dedup.empty());
+            reboot_for_remount(fd.get(), !dedup.empty());
             return;
         }
-        WriteFdExactly(fd, "No reboot needed, skipping -R.\n");
+        WriteFdExactly(fd.get(), "No reboot needed, skipping -R.\n");
     }
 
     // If we need to disable-verity, but we also need to perform a recovery
@@ -250,17 +265,14 @@
     if (verity_enabled && dedup.empty()) {
         // Allow remount but warn of likely bad effects
         bool both = system_verified && vendor_verified;
-        WriteFdFmt(fd,
-                   "dm_verity is enabled on the %s%s%s partition%s.\n",
-                   system_verified ? "system" : "",
-                   both ? " and " : "",
-                   vendor_verified ? "vendor" : "",
-                   both ? "s" : "");
-        WriteFdExactly(fd,
+        WriteFdFmt(fd.get(), "dm_verity is enabled on the %s%s%s partition%s.\n",
+                   system_verified ? "system" : "", both ? " and " : "",
+                   vendor_verified ? "vendor" : "", both ? "s" : "");
+        WriteFdExactly(fd.get(),
                        "Use \"adb disable-verity\" to disable verity.\n"
                        "If you do not, remount may succeed, however, you will still "
                        "not be able to write to these volumes.\n");
-        WriteFdExactly(fd,
+        WriteFdExactly(fd.get(),
                        "Alternately, use \"adb remount -R\" to disable verity "
                        "and automatically reboot.\n");
     }
@@ -271,29 +283,29 @@
         if (dedup.count(partition)) {
             continue;
         }
-        success &= remount_partition(fd, partition.c_str());
+        success &= remount_partition(fd.get(), partition.c_str());
     }
 
     if (!dedup.empty()) {
-        WriteFdExactly(fd,
+        WriteFdExactly(fd.get(),
                        "The following partitions are deduplicated and cannot "
                        "yet be remounted:\n");
         for (const std::string& name : dedup) {
-            WriteFdFmt(fd, "  %s\n", name.c_str());
+            WriteFdFmt(fd.get(), "  %s\n", name.c_str());
         }
 
-        WriteFdExactly(fd,
+        WriteFdExactly(fd.get(),
                        "To reboot and un-deduplicate the listed partitions, "
                        "please retry with adb remount -R.\n");
         if (system_verified || vendor_verified) {
-            WriteFdExactly(fd, "Note: verity will be automatically disabled after reboot.\n");
+            WriteFdExactly(fd.get(), "Note: verity will be automatically disabled after reboot.\n");
         }
         return;
     }
 
     if (!success) {
-        WriteFdExactly(fd, "remount failed\n");
+        WriteFdExactly(fd.get(), "remount failed\n");
     } else {
-        WriteFdExactly(fd, "remount succeeded\n");
+        WriteFdExactly(fd.get(), "remount succeeded\n");
     }
 }
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
index 0fcf89b..0f804e9 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -16,6 +16,7 @@
 
 #define TRACE_TAG ADB
 
+#include "set_verity_enable_state_service.h"
 #include "sysdeps.h"
 
 #include <fcntl.h>
@@ -25,8 +26,8 @@
 #include <stdio.h>
 #include <sys/stat.h>
 
-#include "android-base/properties.h"
-#include "android-base/stringprintf.h"
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <log/log_properties.h>
 
 #include "adb.h"
@@ -131,12 +132,9 @@
     return true;
 }
 
-void set_verity_enabled_state_service(int fd, void* cookie) {
-    unique_fd closer(fd);
+void set_verity_enabled_state_service(android::base::unique_fd fd, bool enable) {
     bool any_changed = false;
 
-    bool enable = (cookie != NULL);
-
     // Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
     // contract, androidboot.vbmeta.digest is set by the bootloader
     // when using AVB).
@@ -147,12 +145,12 @@
     // VB1.0 dm-verity is only enabled on certain builds.
     if (!using_avb) {
         if (!kAllowDisableVerity) {
-            WriteFdFmt(fd, "%s-verity only works for userdebug builds\n",
+            WriteFdFmt(fd.get(), "%s-verity only works for userdebug builds\n",
                        enable ? "enable" : "disable");
         }
 
         if (!android::base::GetBoolProperty("ro.secure", false)) {
-            WriteFdFmt(fd, "verity not enabled - ENG build\n");
+            WriteFdFmt(fd.get(), "verity not enabled - ENG build\n");
             return;
         }
     }
@@ -160,7 +158,7 @@
     // Should never be possible to disable dm-verity on a USER build
     // regardless of using AVB or VB1.0.
     if (!__android_log_is_debuggable()) {
-        WriteFdFmt(fd, "verity cannot be disabled/enabled - USER build\n");
+        WriteFdFmt(fd.get(), "verity cannot be disabled/enabled - USER build\n");
         return;
     }
 
@@ -168,10 +166,10 @@
         // Yep, the system is using AVB.
         AvbOps* ops = avb_ops_user_new();
         if (ops == nullptr) {
-            WriteFdFmt(fd, "Error getting AVB ops\n");
+            WriteFdFmt(fd.get(), "Error getting AVB ops\n");
             return;
         }
-        if (set_avb_verity_enabled_state(fd, ops, enable)) {
+        if (set_avb_verity_enabled_state(fd.get(), ops, enable)) {
             any_changed = true;
         }
         avb_ops_user_free(ops);
@@ -181,14 +179,14 @@
         // read all fstab entries at once from all sources
         fstab = fs_mgr_read_fstab_default();
         if (!fstab) {
-            WriteFdFmt(fd, "Failed to read fstab\nMaybe run adb root?\n");
+            WriteFdFmt(fd.get(), "Failed to read fstab\nMaybe run adb root?\n");
             return;
         }
 
         // Loop through entries looking for ones that vold manages.
         for (int i = 0; i < fstab->num_entries; i++) {
             if (fs_mgr_is_verified(&fstab->recs[i])) {
-                if (set_verity_enabled_state(fd, fstab->recs[i].blk_device,
+                if (set_verity_enabled_state(fd.get(), fstab->recs[i].blk_device,
                                              fstab->recs[i].mount_point, enable)) {
                     any_changed = true;
                 }
@@ -197,6 +195,6 @@
     }
 
     if (any_changed) {
-        WriteFdFmt(fd, "Now reboot your device for settings to take effect\n");
+        WriteFdFmt(fd.get(), "Now reboot your device for settings to take effect\n");
     }
 }
diff --git a/init/log.h b/adb/daemon/set_verity_enable_state_service.h
similarity index 62%
rename from init/log.h
rename to adb/daemon/set_verity_enable_state_service.h
index 5a4eba6..9f84f35 100644
--- a/init/log.h
+++ b/adb/daemon/set_verity_enable_state_service.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * 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.
@@ -14,19 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_LOG_H_
-#define _INIT_LOG_H_
+#ifndef _DAEMON_SET_VERITY_ENABLED_STATE_SERVICE_H_
+#define _DAEMON_SET_VERITY_ENABLED_STATE_SERVICE_H_
 
-#include <sys/cdefs.h>
+#include <android-base/unique_fd.h>
 
-namespace android {
-namespace init {
+void set_verity_enabled_state_service(android::base::unique_fd fd, bool enable);
 
-void InitKernelLogging(char* argv[]);
-
-int selinux_klog_callback(int level, const char* fmt, ...) __printflike(2, 3);
-
-}  // namespace init
-}  // namespace android
-
-#endif
+#endif  // _DAEMON_SET_VERITY_ENABLED_STATE_SERVICE_H_
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index c724b11..c79d6fc 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -41,7 +41,7 @@
 #include <android-base/properties.h>
 
 #include "adb.h"
-#include "daemon/usb.h"
+#include "adbd/usb.h"
 #include "transport.h"
 
 using namespace std::chrono_literals;
@@ -53,7 +53,7 @@
 #define USB_FFS_BULK_SIZE 16384
 
 // Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.
-#define USB_FFS_NUM_BUFS ((MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
+#define USB_FFS_NUM_BUFS ((4 * MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
 
 #define cpu_to_le16(x) htole16(x)
 #define cpu_to_le32(x) htole32(x)
@@ -226,16 +226,16 @@
     },
 };
 
-static void aio_block_init(aio_block* aiob) {
-    aiob->iocb.resize(USB_FFS_NUM_BUFS);
-    aiob->iocbs.resize(USB_FFS_NUM_BUFS);
-    aiob->events.resize(USB_FFS_NUM_BUFS);
+static void aio_block_init(aio_block* aiob, unsigned num_bufs) {
+    aiob->iocb.resize(num_bufs);
+    aiob->iocbs.resize(num_bufs);
+    aiob->events.resize(num_bufs);
     aiob->num_submitted = 0;
-    for (unsigned i = 0; i < USB_FFS_NUM_BUFS; i++) {
+    for (unsigned i = 0; i < num_bufs; i++) {
         aiob->iocbs[i] = &aiob->iocb[i];
     }
     memset(&aiob->ctx, 0, sizeof(aiob->ctx));
-    if (io_setup(USB_FFS_NUM_BUFS, &aiob->ctx)) {
+    if (io_setup(num_bufs, &aiob->ctx)) {
         D("[ aio: got error on io_setup (%d) ]", errno);
     }
 }
@@ -250,7 +250,7 @@
     }
 }
 
-bool init_functionfs(struct usb_handle* h) {
+static bool init_functionfs(struct usb_handle* h) {
     LOG(INFO) << "initializing functionfs";
 
     ssize_t ret;
@@ -318,6 +318,7 @@
 
     h->read_aiob.fd = h->bulk_out;
     h->write_aiob.fd = h->bulk_in;
+    h->reads_zero_packets = true;
     return true;
 
 err:
@@ -336,9 +337,7 @@
     return false;
 }
 
-static void usb_ffs_open_thread(void* x) {
-    struct usb_handle* usb = (struct usb_handle*)x;
-
+static void usb_ffs_open_thread(usb_handle *usb) {
     adb_thread_setname("usb ffs open");
 
     while (true) {
@@ -359,7 +358,7 @@
         }
 
         LOG(INFO) << "registering usb transport";
-        register_usb_transport(usb, 0, 0, 1);
+        register_usb_transport(usb, nullptr, nullptr, 1);
     }
 
     // never gets here
@@ -370,6 +369,7 @@
     D("about to write (fd=%d, len=%d)", h->bulk_in, len);
 
     const char* buf = static_cast<const char*>(data);
+    int orig_len = len;
     while (len > 0) {
         int write_len = std::min(USB_FFS_BULK_SIZE, len);
         int n = adb_write(h->bulk_in, buf, write_len);
@@ -382,13 +382,14 @@
     }
 
     D("[ done fd=%d ]", h->bulk_in);
-    return 0;
+    return orig_len;
 }
 
 static int usb_ffs_read(usb_handle* h, void* data, int len) {
     D("about to read (fd=%d, len=%d)", h->bulk_out, len);
 
     char* buf = static_cast<char*>(data);
+    int orig_len = len;
     while (len > 0) {
         int read_len = std::min(USB_FFS_BULK_SIZE, len);
         int n = adb_read(h->bulk_out, buf, read_len);
@@ -401,14 +402,14 @@
     }
 
     D("[ done fd=%d ]", h->bulk_out);
-    return 0;
+    return orig_len;
 }
 
 static int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) {
     aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;
     bool zero_packet = false;
 
-    int num_bufs = len / USB_FFS_BULK_SIZE + (len % USB_FFS_BULK_SIZE == 0 ? 0 : 1);
+    int num_bufs = len / h->io_size + (len % h->io_size == 0 ? 0 : 1);
     const char* cur_data = reinterpret_cast<const char*>(data);
     int packet_size = getMaxPacketSize(aiob->fd);
 
@@ -418,7 +419,7 @@
     }
 
     for (int i = 0; i < num_bufs; i++) {
-        int buf_len = std::min(len, USB_FFS_BULK_SIZE);
+        int buf_len = std::min(len, static_cast<int>(h->io_size));
         io_prep(&aiob->iocb[i], aiob->fd, cur_data, buf_len, 0, read);
 
         len -= buf_len;
@@ -427,7 +428,7 @@
         if (len == 0 && buf_len % packet_size == 0 && read) {
             // adb does not expect the device to send a zero packet after data transfer,
             // but the host *does* send a zero packet for the device to read.
-            zero_packet = true;
+            zero_packet = h->reads_zero_packets;
         }
     }
     if (zero_packet) {
@@ -449,6 +450,7 @@
         if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
             continue;
         }
+        int ret = 0;
         for (int i = 0; i < num_bufs; i++) {
             if (aiob->events[i].res < 0) {
                 errno = -aiob->events[i].res;
@@ -456,8 +458,9 @@
                             << " total bufs " << num_bufs;
                 return -1;
             }
+            ret += aiob->events[i].res;
         }
-        return 0;
+        return ret;
     }
 }
 
@@ -505,9 +508,7 @@
     h->notify.notify_one();
 }
 
-static void usb_ffs_init() {
-    D("[ usb_init - using FunctionFS ]");
-
+usb_handle *create_usb_handle(unsigned num_bufs, unsigned io_size) {
     usb_handle* h = new usb_handle();
 
     if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
@@ -518,20 +519,21 @@
     } else {
         h->write = usb_ffs_aio_write;
         h->read = usb_ffs_aio_read;
-        aio_block_init(&h->read_aiob);
-        aio_block_init(&h->write_aiob);
+        aio_block_init(&h->read_aiob, num_bufs);
+        aio_block_init(&h->write_aiob, num_bufs);
     }
+    h->io_size = io_size;
     h->kick = usb_ffs_kick;
     h->close = usb_ffs_close;
-
-    D("[ usb_init - starting thread ]");
-    std::thread(usb_ffs_open_thread, h).detach();
+    return h;
 }
 
 void usb_init() {
+    D("[ usb_init - using FunctionFS ]");
     dummy_fd = adb_open("/dev/null", O_WRONLY);
     CHECK_NE(dummy_fd, -1);
-    usb_ffs_init();
+
+    std::thread(usb_ffs_open_thread, create_usb_handle(USB_FFS_NUM_BUFS, USB_FFS_BULK_SIZE)).detach();
 }
 
 int usb_write(usb_handle* h, const void* data, int len) {
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 098a39d..dee87bd 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -149,7 +149,7 @@
 
 void fdevent_destroy(fdevent* fde) {
     check_main_thread();
-    if (fde == 0) return;
+    if (fde == nullptr) return;
     if (!(fde->state & FDE_CREATED)) {
         LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
     }
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 6606efd..608b3ad 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -20,6 +20,8 @@
 #include <string>
 #include <vector>
 
+#include <android-base/unique_fd.h>
+
 #define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
 
 #define ID_LSTAT_V1 MKID('S','T','A','T')
@@ -79,7 +81,7 @@
     } status;
 };
 
-void file_sync_service(int fd, void* cookie);
+void file_sync_service(android::base::unique_fd fd);
 bool do_sync_ls(const char* path);
 bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync);
 bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
diff --git a/adb/remount_service.h b/adb/remount_service.h
index 7bda1be..45821ee 100644
--- a/adb/remount_service.h
+++ b/adb/remount_service.h
@@ -19,7 +19,9 @@
 
 #include <string>
 
+#include <android-base/unique_fd.h>
+
 bool make_block_device_writable(const std::string&);
-void remount_service(int, void*);
+void remount_service(android::base::unique_fd, const std::string&);
 
 #endif
diff --git a/adb/services.cpp b/adb/services.cpp
index a757d90..3d418cb 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -37,6 +37,7 @@
 #include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 
 #if !ADB_HOST
@@ -49,6 +50,10 @@
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_utils.h"
+#if !ADB_HOST
+#include "daemon/framebuffer_service.h"
+#include "daemon/set_verity_enable_state_service.h"
+#endif
 #include "file_sync_service.h"
 #include "remount_service.h"
 #include "services.h"
@@ -57,81 +62,67 @@
 #include "sysdeps.h"
 #include "transport.h"
 
-struct stinfo {
-    const char* service_name;
-    void (*func)(int fd, void *cookie);
-    int fd;
-    void *cookie;
-};
+namespace {
 
-static void service_bootstrap_func(void* x) {
-    stinfo* sti = reinterpret_cast<stinfo*>(x);
-    adb_thread_setname(android::base::StringPrintf("%s svc %d", sti->service_name, sti->fd));
-    sti->func(sti->fd, sti->cookie);
-    free(sti);
+void service_bootstrap_func(std::string service_name,
+                            std::function<void(android::base::unique_fd)> func,
+                            android::base::unique_fd fd) {
+    adb_thread_setname(android::base::StringPrintf("%s svc %d", service_name.c_str(), fd.get()));
+    func(std::move(fd));
 }
 
 #if !ADB_HOST
 
-void restart_root_service(int fd, void *cookie) {
+void restart_root_service(android::base::unique_fd fd) {
     if (getuid() == 0) {
-        WriteFdExactly(fd, "adbd is already running as root\n");
-        adb_close(fd);
-    } else {
-        if (!__android_log_is_debuggable()) {
-            WriteFdExactly(fd, "adbd cannot run as root in production builds\n");
-            adb_close(fd);
-            return;
-        }
-
-        android::base::SetProperty("service.adb.root", "1");
-        WriteFdExactly(fd, "restarting adbd as root\n");
-        adb_close(fd);
+        WriteFdExactly(fd.get(), "adbd is already running as root\n");
+        return;
     }
+    if (!__android_log_is_debuggable()) {
+        WriteFdExactly(fd.get(), "adbd cannot run as root in production builds\n");
+        return;
+    }
+
+    android::base::SetProperty("service.adb.root", "1");
+    WriteFdExactly(fd.get(), "restarting adbd as root\n");
 }
 
-void restart_unroot_service(int fd, void *cookie) {
+void restart_unroot_service(android::base::unique_fd fd) {
     if (getuid() != 0) {
-        WriteFdExactly(fd, "adbd not running as root\n");
-        adb_close(fd);
-    } else {
-        android::base::SetProperty("service.adb.root", "0");
-        WriteFdExactly(fd, "restarting adbd as non root\n");
-        adb_close(fd);
+        WriteFdExactly(fd.get(), "adbd not running as root\n");
+        return;
     }
+    android::base::SetProperty("service.adb.root", "0");
+    WriteFdExactly(fd.get(), "restarting adbd as non root\n");
 }
 
-void restart_tcp_service(int fd, void *cookie) {
-    int port = (int) (uintptr_t) cookie;
+void restart_tcp_service(android::base::unique_fd fd, int port) {
     if (port <= 0) {
-        WriteFdFmt(fd, "invalid port %d\n", port);
-        adb_close(fd);
+        WriteFdFmt(fd.get(), "invalid port %d\n", port);
         return;
     }
 
     android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
-    WriteFdFmt(fd, "restarting in TCP mode port: %d\n", port);
-    adb_close(fd);
+    WriteFdFmt(fd.get(), "restarting in TCP mode port: %d\n", port);
 }
 
-void restart_usb_service(int fd, void *cookie) {
+void restart_usb_service(android::base::unique_fd fd) {
     android::base::SetProperty("service.adb.tcp.port", "0");
-    WriteFdExactly(fd, "restarting in USB mode\n");
-    adb_close(fd);
+    WriteFdExactly(fd.get(), "restarting in USB mode\n");
 }
 
-static bool reboot_service_impl(int fd, const char* arg) {
-    const char* reboot_arg = arg;
+bool reboot_service_impl(android::base::unique_fd fd, const std::string& arg) {
+    std::string reboot_arg = arg;
     bool auto_reboot = false;
 
-    if (strcmp(reboot_arg, "sideload-auto-reboot") == 0) {
+    if (reboot_arg == "sideload-auto-reboot") {
         auto_reboot = true;
         reboot_arg = "sideload";
     }
 
     // It reboots into sideload mode by setting "--sideload" or "--sideload_auto_reboot"
     // in the command file.
-    if (strcmp(reboot_arg, "sideload") == 0) {
+    if (reboot_arg == "sideload") {
         if (getuid() != 0) {
             WriteFdExactly(fd, "'adb root' is required for 'adb reboot sideload'.\n");
             return false;
@@ -151,8 +142,8 @@
 
     sync();
 
-    if (!reboot_arg || !reboot_arg[0]) reboot_arg = "adb";
-    std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg);
+    if (reboot_arg.empty()) reboot_arg = "adb";
+    std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
     if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
         WriteFdFmt(fd, "reboot (%s) failed\n", reboot_string.c_str());
         return false;
@@ -161,23 +152,19 @@
     return true;
 }
 
-void reboot_service(int fd, void* arg) {
-    if (reboot_service_impl(fd, static_cast<const char*>(arg))) {
-        // Don't return early. Give the reboot command time to take effect
-        // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
-        while (true) {
-            pause();
-        }
+void reboot_service(android::base::unique_fd fd, const std::string& arg) {
+    if (!reboot_service_impl(std::move(fd), arg)) {
+        return;
     }
-
-    free(arg);
-    adb_close(fd);
+    // Don't return early. Give the reboot command time to take effect
+    // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
+    while (true) {
+        pause();
+    }
 }
 
-static void reconnect_service(int fd, void* arg) {
+void reconnect_service(android::base::unique_fd fd, atransport* t) {
     WriteFdExactly(fd, "done");
-    adb_close(fd);
-    atransport* t = static_cast<atransport*>(arg);
     kick_transport(t);
 }
 
@@ -197,7 +184,7 @@
 
 // Shell service string can look like:
 //   shell[,arg1,arg2,...]:[command]
-static int ShellService(const std::string& args, const atransport* transport) {
+int ShellService(const std::string& args, const atransport* transport) {
     size_t delimiter_index = args.find(':');
     if (delimiter_index == std::string::npos) {
         LOG(ERROR) << "No ':' found in shell service arguments: " << args;
@@ -236,16 +223,17 @@
 
 #endif  // !ADB_HOST
 
-static int create_service_thread(const char* service_name, void (*func)(int, void*), void* cookie) {
+android::base::unique_fd create_service_thread(const char* service_name,
+                                               std::function<void(android::base::unique_fd)> func) {
     int s[2];
     if (adb_socketpair(s)) {
         printf("cannot create service socket pair\n");
-        return -1;
+        return android::base::unique_fd();
     }
     D("socketpair: (%d,%d)", s[0], s[1]);
 
 #if !ADB_HOST
-    if (func == &file_sync_service) {
+    if (strcmp(service_name, "sync") == 0) {
         // Set file sync service socket to maximum size
         int max_buf = LINUX_MAX_SOCKET_SIZE;
         adb_setsockopt(s[0], SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
@@ -253,21 +241,14 @@
     }
 #endif // !ADB_HOST
 
-    stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
-    if (sti == nullptr) {
-        fatal("cannot allocate stinfo");
-    }
-    sti->service_name = service_name;
-    sti->func = func;
-    sti->cookie = cookie;
-    sti->fd = s[1];
-
-    std::thread(service_bootstrap_func, sti).detach();
+    std::thread(service_bootstrap_func, service_name, func, android::base::unique_fd(s[1])).detach();
 
     D("service thread started, %d:%d",s[0], s[1]);
-    return s[0];
+    return android::base::unique_fd(s[0]);
 }
 
+}  // namespace
+
 int service_to_fd(const char* name, atransport* transport) {
     int ret = -1;
 
@@ -280,54 +261,60 @@
 #if !ADB_HOST
     } else if(!strncmp("dev:", name, 4)) {
         ret = unix_open(name + 4, O_RDWR | O_CLOEXEC);
-    } else if(!strncmp(name, "framebuffer:", 12)) {
-        ret = create_service_thread("fb", framebuffer_service, nullptr);
+    } else if (!strncmp(name, "framebuffer:", 12)) {
+        ret = create_service_thread("fb", framebuffer_service).release();
     } else if (!strncmp(name, "jdwp:", 5)) {
-        ret = create_jdwp_connection_fd(atoi(name+5));
-    } else if(!strncmp(name, "shell", 5)) {
+        ret = create_jdwp_connection_fd(atoi(name + 5));
+    } else if (!strncmp(name, "shell", 5)) {
         ret = ShellService(name + 5, transport);
-    } else if(!strncmp(name, "exec:", 5)) {
+    } else if (!strncmp(name, "exec:", 5)) {
         ret = StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
-    } else if(!strncmp(name, "sync:", 5)) {
-        ret = create_service_thread("sync", file_sync_service, nullptr);
-    } else if(!strncmp(name, "remount:", 8)) {
-        const char* options = name + strlen("remount:");
-        void* cookie = const_cast<void*>(reinterpret_cast<const void*>(options));
-        ret = create_service_thread("remount", remount_service, cookie);
-    } else if(!strncmp(name, "reboot:", 7)) {
-        void* arg = strdup(name + 7);
-        if (arg == NULL) return -1;
-        ret = create_service_thread("reboot", reboot_service, arg);
-        if (ret < 0) free(arg);
-    } else if(!strncmp(name, "root:", 5)) {
-        ret = create_service_thread("root", restart_root_service, nullptr);
-    } else if(!strncmp(name, "unroot:", 7)) {
-        ret = create_service_thread("unroot", restart_unroot_service, nullptr);
-    } else if(!strncmp(name, "backup:", 7)) {
-        ret = StartSubprocess(android::base::StringPrintf("/system/bin/bu backup %s",
-                                                          (name + 7)).c_str(),
-                              nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
-    } else if(!strncmp(name, "restore:", 8)) {
+    } else if (!strncmp(name, "sync:", 5)) {
+        ret = create_service_thread("sync", file_sync_service).release();
+    } else if (!strncmp(name, "remount:", 8)) {
+        std::string options(name + strlen("remount:"));
+        ret = create_service_thread("remount",
+                                    std::bind(remount_service, std::placeholders::_1, options))
+                      .release();
+    } else if (!strncmp(name, "reboot:", 7)) {
+        std::string arg(name + strlen("reboot:"));
+        ret = create_service_thread("reboot", std::bind(reboot_service, std::placeholders::_1, arg))
+                      .release();
+    } else if (!strncmp(name, "root:", 5)) {
+        ret = create_service_thread("root", restart_root_service).release();
+    } else if (!strncmp(name, "unroot:", 7)) {
+        ret = create_service_thread("unroot", restart_unroot_service).release();
+    } else if (!strncmp(name, "backup:", 7)) {
+        ret = StartSubprocess(
+                android::base::StringPrintf("/system/bin/bu backup %s", (name + 7)).c_str(),
+                nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+    } else if (!strncmp(name, "restore:", 8)) {
         ret = StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
                               SubprocessProtocol::kNone);
-    } else if(!strncmp(name, "tcpip:", 6)) {
+    } else if (!strncmp(name, "tcpip:", 6)) {
         int port;
         if (sscanf(name + 6, "%d", &port) != 1) {
             return -1;
         }
-        ret = create_service_thread("tcp", restart_tcp_service, reinterpret_cast<void*>(port));
-    } else if(!strncmp(name, "usb:", 4)) {
-        ret = create_service_thread("usb", restart_usb_service, nullptr);
+        ret = create_service_thread("tcp",
+                                    std::bind(restart_tcp_service, std::placeholders::_1, port))
+                      .release();
+    } else if (!strncmp(name, "usb:", 4)) {
+        ret = create_service_thread("usb", restart_usb_service).release();
     } else if (!strncmp(name, "reverse:", 8)) {
         ret = reverse_service(name + 8, transport);
-    } else if(!strncmp(name, "disable-verity:", 15)) {
-        ret = create_service_thread("verity-on", set_verity_enabled_state_service,
-                                    reinterpret_cast<void*>(0));
-    } else if(!strncmp(name, "enable-verity:", 15)) {
-        ret = create_service_thread("verity-off", set_verity_enabled_state_service,
-                                    reinterpret_cast<void*>(1));
+    } else if (!strncmp(name, "disable-verity:", 15)) {
+        ret = create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
+                                                           std::placeholders::_1, false))
+                      .release();
+    } else if (!strncmp(name, "enable-verity:", 15)) {
+        ret = create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
+                                                            std::placeholders::_1, true))
+                      .release();
     } else if (!strcmp(name, "reconnect")) {
-        ret = create_service_thread("reconnect", reconnect_service, transport);
+        ret = create_service_thread("reconnect",
+                                    std::bind(reconnect_service, std::placeholders::_1, transport))
+                      .release();
 #endif
     }
     if (ret >= 0) {
@@ -352,7 +339,7 @@
     while (true) {
         bool is_ambiguous = false;
         std::string error = "unknown error";
-        const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL;
+        const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : nullptr;
         atransport* t = acquire_one_transport(sinfo->transport_type, serial, sinfo->transport_id,
                                               &is_ambiguous, &error);
         if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
@@ -389,8 +376,8 @@
         return;
     }
 
-    int console_port = strtol(pieces[0].c_str(), NULL, 0);
-    int adb_port = strtol(pieces[1].c_str(), NULL, 0);
+    int console_port = strtol(pieces[0].c_str(), nullptr, 0);
+    int adb_port = strtol(pieces[1].c_str(), nullptr, 0);
     if (console_port <= 0 || adb_port <= 0) {
         *response = android::base::StringPrintf("Invalid port numbers: %s", port_spec.c_str());
         return;
@@ -420,19 +407,16 @@
     }
 }
 
-static void connect_service(int fd, void* data) {
-    char* host = reinterpret_cast<char*>(data);
+static void connect_service(android::base::unique_fd fd, std::string host) {
     std::string response;
-    if (!strncmp(host, "emu:", 4)) {
-        connect_emulator(host + 4, &response);
+    if (!strncmp(host.c_str(), "emu:", 4)) {
+        connect_emulator(host.c_str() + 4, &response);
     } else {
-        connect_device(host, &response);
+        connect_device(host.c_str(), &response);
     }
-    free(host);
 
     // Send response for emulator and device
-    SendProtocolString(fd, response);
-    adb_close(fd);
+    SendProtocolString(fd.get(), response);
 }
 #endif
 
@@ -445,7 +429,7 @@
     } else if (android::base::StartsWith(name, "wait-for-")) {
         name += strlen("wait-for-");
 
-        std::unique_ptr<state_info> sinfo(new state_info);
+        std::unique_ptr<state_info> sinfo = std::make_unique<state_info>();
         if (sinfo == nullptr) {
             fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
             return nullptr;
@@ -481,19 +465,20 @@
             return nullptr;
         }
 
-        int fd = create_service_thread("wait", wait_for_state, sinfo.get());
+        int fd = create_service_thread(
+                         "wait", std::bind(wait_for_state, std::placeholders::_1, sinfo.get()))
+                         .release();
         if (fd != -1) {
             sinfo.release();
         }
         return create_local_socket(fd);
     } else if (!strncmp(name, "connect:", 8)) {
-        char* host = strdup(name + 8);
-        int fd = create_service_thread("connect", connect_service, host);
-        if (fd == -1) {
-            free(host);
-        }
+        std::string host(name + strlen("connect:"));
+        int fd = create_service_thread("connect",
+                                       std::bind(connect_service, std::placeholders::_1, host))
+                         .release();
         return create_local_socket(fd);
     }
-    return NULL;
+    return nullptr;
 }
 #endif /* ADB_HOST */
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index de3215d..69b5180 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -337,7 +337,7 @@
     s->fd = fd;
     s->enqueue = local_socket_enqueue;
     s->ready = local_socket_ready;
-    s->shutdown = NULL;
+    s->shutdown = nullptr;
     s->close = local_socket_close;
     install_local_socket(s);
 
@@ -383,7 +383,7 @@
 
     s = host_service_to_socket(name, serial, transport_id);
 
-    if (s != NULL) {
+    if (s != nullptr) {
         D("LS(%d) bound to '%s'", s->id, name);
         return s;
     }
@@ -435,7 +435,7 @@
 
 static void remote_socket_close(asocket* s) {
     if (s->peer) {
-        s->peer->peer = 0;
+        s->peer->peer = nullptr;
         D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd);
         s->peer->close(s->peer);
     }
@@ -488,7 +488,7 @@
    send the go-ahead message when they connect */
 static void local_socket_ready_notify(asocket* s) {
     s->ready = local_socket_ready;
-    s->shutdown = NULL;
+    s->shutdown = nullptr;
     s->close = local_socket_close;
     SendOkay(s->fd);
     s->ready(s);
@@ -499,7 +499,7 @@
    connected (to avoid closing them without a status message) */
 static void local_socket_close_notify(asocket* s) {
     s->ready = local_socket_ready;
-    s->shutdown = NULL;
+    s->shutdown = nullptr;
     s->close = local_socket_close;
     SendFail(s->fd, "closed");
     s->close(s);
@@ -706,7 +706,7 @@
         ** and tear down here.
         */
         s2 = create_host_service_socket(service, serial, transport_id);
-        if (s2 == 0) {
+        if (s2 == nullptr) {
             D("SS(%d): couldn't create host service '%s'", s->id, service);
             SendFail(s->peer->fd, "unknown host service");
             goto fail;
@@ -726,7 +726,7 @@
         s->peer->close = local_socket_close;
         s->peer->peer = s2;
         s2->peer = s->peer;
-        s->peer = 0;
+        s->peer = nullptr;
         D("SS(%d): okay", s->id);
         s->close(s);
 
@@ -764,12 +764,12 @@
     s->peer->ready = local_socket_ready_notify;
     s->peer->shutdown = nullptr;
     s->peer->close = local_socket_close_notify;
-    s->peer->peer = 0;
+    s->peer->peer = nullptr;
     /* give him our transport and upref it */
     s->peer->transport = s->transport;
 
     connect_to_remote(s->peer, s->smart_socket_data.data() + 4);
-    s->peer = 0;
+    s->peer = nullptr;
     s->close(s);
     return 1;
 
@@ -789,9 +789,9 @@
 static void smart_socket_close(asocket* s) {
     D("SS(%d): closed", s->id);
     if (s->peer) {
-        s->peer->peer = 0;
+        s->peer->peer = nullptr;
         s->peer->close(s->peer);
-        s->peer = 0;
+        s->peer = nullptr;
     }
     delete s;
 }
@@ -801,7 +801,7 @@
     asocket* s = new asocket();
     s->enqueue = smart_socket_enqueue;
     s->ready = smart_socket_ready;
-    s->shutdown = NULL;
+    s->shutdown = nullptr;
     s->close = smart_socket_close;
 
     D("SS(%d)", s->id);
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 3be99f6..f2911e0 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -39,19 +39,6 @@
 #include "sysdeps/network.h"
 #include "sysdeps/stat.h"
 
-// Some printf-like functions are implemented in terms of
-// android::base::StringAppendV, so they should use the same attribute for
-// compile-time format string checking. On Windows, if the mingw version of
-// vsnprintf is used in StringAppendV, use `gnu_printf' which allows z in %zd
-// and PRIu64 (and related) to be recognized by the compile-time checking.
-#define ADB_FORMAT_ARCHETYPE __printf__
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-#undef ADB_FORMAT_ARCHETYPE
-#define ADB_FORMAT_ARCHETYPE gnu_printf
-#endif
-#endif
-
 #ifdef _WIN32
 
 // Clang-only nullability specifiers
@@ -212,14 +199,12 @@
 extern int adb_utime(const char *, struct utimbuf *);
 extern int adb_chmod(const char *, int);
 
-extern int adb_vfprintf(FILE *stream, const char *format, va_list ap)
-    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 0)));
-extern int adb_vprintf(const char *format, va_list ap)
-    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 0)));
-extern int adb_fprintf(FILE *stream, const char *format, ...)
-    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3)));
-extern int adb_printf(const char *format, ...)
-    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 2)));
+extern int adb_vfprintf(FILE* stream, const char* format, va_list ap)
+        __attribute__((__format__(__printf__, 2, 0)));
+extern int adb_vprintf(const char* format, va_list ap) __attribute__((__format__(__printf__, 1, 0)));
+extern int adb_fprintf(FILE* stream, const char* format, ...)
+        __attribute__((__format__(__printf__, 2, 3)));
+extern int adb_printf(const char* format, ...) __attribute__((__format__(__printf__, 1, 2)));
 
 extern int adb_fputs(const char* buf, FILE* stream);
 extern int adb_fputc(int ch, FILE* stream);
@@ -329,7 +314,6 @@
 #else /* !_WIN32 a.k.a. Unix */
 
 #include <cutils/sockets.h>
-#include <cutils/threads.h>
 #include <fcntl.h>
 #include <poll.h>
 #include <signal.h>
diff --git a/adb/sysdeps/memory.h b/adb/sysdeps/memory.h
index 0e4c509..4108aff 100644
--- a/adb/sysdeps/memory.h
+++ b/adb/sysdeps/memory.h
@@ -23,6 +23,23 @@
 // We don't have C++14 on Windows yet.
 // Reimplement std::make_unique ourselves until we do.
 
+namespace internal {
+
+template <typename T>
+struct array_known_bounds;
+
+template <typename T>
+struct array_known_bounds<T[]> {
+    constexpr static bool value = false;
+};
+
+template <typename T, size_t N>
+struct array_known_bounds<T[N]> {
+    constexpr static bool value = true;
+};
+
+}  // namespace internal
+
 namespace std {
 
 template <typename T, typename... Args>
@@ -31,6 +48,18 @@
     return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
 }
 
+template <typename T>
+typename std::enable_if<std::is_array<T>::value && !internal::array_known_bounds<T>::value,
+                        std::unique_ptr<T>>::type
+make_unique(std::size_t size) {
+    return std::unique_ptr<T>(new typename std::remove_extent<T>::type[size]());
+}
+
+template <typename T, typename... Args>
+typename std::enable_if<std::is_array<T>::value && internal::array_known_bounds<T>::value,
+                        std::unique_ptr<T>>::type
+make_unique(Args&&... args) = delete;
+
 }  // namespace std
 
 #endif
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index bfac342..c94d13f 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -158,7 +158,7 @@
         D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
            func );
         errno = EBADF;
-        return NULL;
+        return nullptr;
     }
 
     f = &_win32_fhs[fd];
@@ -167,7 +167,7 @@
         D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
            func );
         errno = EBADF;
-        return NULL;
+        return nullptr;
     }
 
     return f;
@@ -186,12 +186,12 @@
 static FH
 _fh_alloc( FHClass  clazz )
 {
-    FH   f = NULL;
+    FH   f = nullptr;
 
     std::lock_guard<std::mutex> lock(_win32_lock);
 
     for (int i = _win32_fh_next; i < WIN32_MAX_FHS; ++i) {
-        if (_win32_fhs[i].clazz == NULL) {
+        if (_win32_fhs[i].clazz == nullptr) {
             f = &_win32_fhs[i];
             _win32_fh_next = i + 1;
             f->clazz = clazz;
@@ -226,7 +226,7 @@
         f->name[0] = '\0';
         f->eof     = 0;
         f->used    = 0;
-        f->clazz   = NULL;
+        f->clazz   = nullptr;
     }
     return 0;
 }
@@ -269,7 +269,7 @@
 static int _fh_file_read(FH f, void* buf, int len) {
     DWORD read_bytes;
 
-    if (!ReadFile(f->fh_handle, buf, (DWORD)len, &read_bytes, NULL)) {
+    if (!ReadFile(f->fh_handle, buf, (DWORD)len, &read_bytes, nullptr)) {
         D("adb_read: could not read %d bytes from %s", len, f->name);
         errno = EIO;
         return -1;
@@ -282,7 +282,7 @@
 static int _fh_file_write(FH f, const void* buf, int len) {
     DWORD wrote_bytes;
 
-    if (!WriteFile(f->fh_handle, buf, (DWORD)len, &wrote_bytes, NULL)) {
+    if (!WriteFile(f->fh_handle, buf, (DWORD)len, &wrote_bytes, nullptr)) {
         D("adb_file_write: could not write %d bytes from %s", len, f->name);
         errno = EIO;
         return -1;
@@ -337,7 +337,7 @@
             return -1;
     }
 
-    result = SetFilePointer(f->fh_handle, pos, NULL, method);
+    result = SetFilePointer(f->fh_handle, pos, nullptr, method);
     if (result == INVALID_SET_FILE_POINTER) {
         errno = EIO;
         return -1;
@@ -387,7 +387,7 @@
         return -1;
     }
     f->fh_handle =
-        CreateFileW(path_wide.c_str(), desiredAccess, shareMode, NULL, OPEN_EXISTING, 0, NULL);
+        CreateFileW(path_wide.c_str(), desiredAccess, shareMode, nullptr, OPEN_EXISTING, 0, nullptr);
 
     if (f->fh_handle == INVALID_HANDLE_VALUE) {
         const DWORD err = GetLastError();
@@ -430,7 +430,7 @@
         return -1;
     }
     f->fh_handle = CreateFileW(path_wide.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
-                               NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+                               nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
 
     if (f->fh_handle == INVALID_HANDLE_VALUE) {
         const DWORD err = GetLastError();
@@ -461,7 +461,7 @@
 int adb_read(int fd, void* buf, int len) {
     FH f = _fh_from_int(fd, __func__);
 
-    if (f == NULL) {
+    if (f == nullptr) {
         errno = EBADF;
         return -1;
     }
@@ -472,7 +472,7 @@
 int adb_write(int fd, const void* buf, int len) {
     FH f = _fh_from_int(fd, __func__);
 
-    if (f == NULL) {
+    if (f == nullptr) {
         errno = EBADF;
         return -1;
     }
@@ -483,7 +483,7 @@
 ssize_t adb_writev(int fd, const adb_iovec* iov, int iovcnt) {
     FH f = _fh_from_int(fd, __func__);
 
-    if (f == NULL) {
+    if (f == nullptr) {
         errno = EBADF;
         return -1;
     }
@@ -1700,7 +1700,7 @@
 
         // The following emulation code should write the output sequence to
         // either seqstr or to seqbuf and seqbuflen.
-        const char* seqstr = NULL;  // NULL terminated C-string
+        const char* seqstr = nullptr;  // NULL terminated C-string
         // Enough space for max sequence string below, plus modifiers and/or
         // escape prefix.
         char seqbuf[16];
@@ -1998,7 +1998,7 @@
         // * seqstr is set (and strlen can be used to determine the length).
         // * seqbuf and seqbuflen are set
         // Fallback to ch from Windows.
-        if (seqstr != NULL) {
+        if (seqstr != nullptr) {
             out = seqstr;
             outlen = strlen(seqstr);
         } else if (seqbuflen > 0) {
@@ -2068,9 +2068,9 @@
 }
 
 void stdin_raw_restore() {
-    if (_console_handle != NULL) {
+    if (_console_handle != nullptr) {
         const HANDLE in = _console_handle;
-        _console_handle = NULL;  // clear state
+        _console_handle = nullptr;  // clear state
 
         if (!SetConsoleMode(in, _old_console_mode)) {
             // This really should not fail.
@@ -2082,7 +2082,7 @@
 
 // Called by 'adb shell' and 'adb exec-in' (via unix_read()) to read from stdin.
 int unix_read_interruptible(int fd, void* buf, size_t len) {
-    if ((fd == STDIN_FILENO) && (_console_handle != NULL)) {
+    if ((fd == STDIN_FILENO) && (_console_handle != nullptr)) {
         // If it is a request to read from stdin, and stdin_raw_init() has been
         // called, and it successfully configured the console, then read from
         // the console using Win32 console APIs and partially emulate a unix
@@ -2442,7 +2442,7 @@
 
     // Write UTF-16 to the console.
     DWORD written = 0;
-    if (!WriteConsoleW(console, utf16.c_str(), utf16.length(), &written, NULL)) {
+    if (!WriteConsoleW(console, utf16.c_str(), utf16.length(), &written, nullptr)) {
         errno = EIO;
         return -1;
     }
@@ -2455,9 +2455,8 @@
 }
 
 // Function prototype because attributes cannot be placed on func definitions.
-static int _console_vfprintf(const HANDLE console, FILE* stream,
-                             const char *format, va_list ap)
-    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 3, 0)));
+static int _console_vfprintf(const HANDLE console, FILE* stream, const char* format, va_list ap)
+        __attribute__((__format__(__printf__, 3, 0)));
 
 // Internal function to format a UTF-8 string and write it to a Win32 console.
 // Returns -1 on error.
@@ -2487,7 +2486,7 @@
 
     // If there is an associated Win32 console, write to it specially,
     // otherwise defer to the regular C Runtime, passing it UTF-8.
-    if (console != NULL) {
+    if (console != nullptr) {
         return _console_vfprintf(console, stream, format, ap);
     } else {
         // If vfprintf is a macro, undefine it, so we can call the real
@@ -2578,7 +2577,7 @@
 
     // If there is an associated Win32 console, write to it specially,
     // otherwise defer to the regular C Runtime, passing it UTF-8.
-    if (console != NULL) {
+    if (console != nullptr) {
         return _console_fwrite(ptr, size, nmemb, stream, console);
     } else {
         // If fwrite is a macro, undefine it, so we can call the real
diff --git a/adb/test_device.py b/adb/test_device.py
index f995be2..4abe7a7 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -750,7 +750,6 @@
             if host_dir is not None:
                 shutil.rmtree(host_dir)
 
-    @unittest.expectedFailure # b/25566053
     def test_push_empty(self):
         """Push a directory containing an empty directory to the device."""
         self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
@@ -866,6 +865,21 @@
             self.assertTrue('Permission denied' in output or
                             'Read-only file system' in output)
 
+    @requires_non_root
+    def test_push_directory_creation(self):
+        """Regression test for directory creation.
+
+        Bug: http://b/110953234
+        """
+        with tempfile.NamedTemporaryFile() as tmp_file:
+            tmp_file.write('\0' * 1024 * 1024)
+            tmp_file.flush()
+            remote_path = self.DEVICE_TEMP_DIR + '/test_push_directory_creation'
+            self.device.shell(['rm', '-rf', remote_path])
+
+            remote_path += '/filename'
+            self.device.push(local=tmp_file.name, remote=remote_path)
+
     def _test_pull(self, remote_file, checksum):
         tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
         tmp_write.close()
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 7db9bf2..638940c 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -173,18 +173,18 @@
             attempt = reconnect_queue_.front();
             reconnect_queue_.pop();
             if (attempt.transport->kicked()) {
-                D("transport %s was kicked. giving up on it.", attempt.transport->serial);
+                D("transport %s was kicked. giving up on it.", attempt.transport->serial.c_str());
                 remove_transport(attempt.transport);
                 continue;
             }
         }
-        D("attempting to reconnect %s", attempt.transport->serial);
+        D("attempting to reconnect %s", attempt.transport->serial.c_str());
 
         if (!attempt.transport->Reconnect()) {
-            D("attempting to reconnect %s failed.", attempt.transport->serial);
+            D("attempting to reconnect %s failed.", attempt.transport->serial.c_str());
             if (attempt.attempts_left == 0) {
                 D("transport %s exceeded the number of retry attempts. giving up on it.",
-                  attempt.transport->serial);
+                  attempt.transport->serial.c_str());
                 remove_transport(attempt.transport);
                 continue;
             }
@@ -197,7 +197,7 @@
             continue;
         }
 
-        D("reconnection to %s succeeded.", attempt.transport->serial);
+        D("reconnection to %s succeeded.", attempt.transport->serial.c_str());
         register_transport(attempt.transport);
     }
 }
@@ -402,14 +402,14 @@
         p->msg.data_check = calculate_apacket_checksum(p);
     }
 
-    VLOG(TRANSPORT) << dump_packet(t->serial, "to remote", p);
+    VLOG(TRANSPORT) << dump_packet(t->serial.c_str(), "to remote", p);
 
-    if (t == NULL) {
+    if (t == nullptr) {
         fatal("Transport is null");
     }
 
     if (t->Write(p) != 0) {
-        D("%s: failed to enqueue packet, closing transport", t->serial);
+        D("%s: failed to enqueue packet, closing transport", t->serial.c_str());
         t->Kick();
     }
 }
@@ -467,7 +467,7 @@
 
     D("device tracker %p removed", tracker);
     if (peer) {
-        peer->peer = NULL;
+        peer->peer = nullptr;
         peer->close(peer);
     }
     device_tracker_remove(tracker);
@@ -619,19 +619,13 @@
     t = m.transport;
 
     if (m.action == 0) {
-        D("transport: %s deleting", t->serial);
+        D("transport: %s deleting", t->serial.c_str());
 
         {
             std::lock_guard<std::recursive_mutex> lock(transport_lock);
             transport_list.remove(t);
         }
 
-        if (t->product) free(t->product);
-        if (t->serial) free(t->serial);
-        if (t->model) free(t->model);
-        if (t->device) free(t->device);
-        if (t->devpath) free(t->devpath);
-
         delete t;
 
         update_transports();
@@ -646,11 +640,11 @@
         t->connection()->SetTransportName(t->serial_name());
         t->connection()->SetReadCallback([t](Connection*, std::unique_ptr<apacket> p) {
             if (!check_header(p.get(), t)) {
-                D("%s: remote read: bad header", t->serial);
+                D("%s: remote read: bad header", t->serial.c_str());
                 return false;
             }
 
-            VLOG(TRANSPORT) << dump_packet(t->serial, "from remote", p.get());
+            VLOG(TRANSPORT) << dump_packet(t->serial.c_str(), "from remote", p.get());
             apacket* packet = p.release();
 
             // TODO: Does this need to run on the main thread?
@@ -658,7 +652,7 @@
             return true;
         });
         t->connection()->SetErrorCallback([t](Connection*, const std::string& error) {
-            D("%s: connection terminated: %s", t->serial, error.c_str());
+            D("%s: connection terminated: %s", t->serial.c_str(), error.c_str());
             fdevent_run_on_main_thread([t]() {
                 handle_offline(t);
                 transport_unref(t);
@@ -699,7 +693,7 @@
     transport_registration_recv = s[1];
 
     transport_registration_fde =
-        fdevent_create(transport_registration_recv, transport_registration_func, 0);
+        fdevent_create(transport_registration_recv, transport_registration_func, nullptr);
     fdevent_set(transport_registration_fde, FDE_READ);
 }
 
@@ -717,7 +711,7 @@
     tmsg m;
     m.transport = transport;
     m.action = 1;
-    D("transport: %s registered", transport->serial);
+    D("transport: %s registered", transport->serial.c_str());
     if (transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
@@ -727,7 +721,7 @@
     tmsg m;
     m.transport = transport;
     m.action = 0;
-    D("transport: %s removed", transport->serial);
+    D("transport: %s removed", transport->serial.c_str());
     if (transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
@@ -743,38 +737,38 @@
     if (t->ref_count == 0) {
         t->connection()->Stop();
         if (t->IsTcpDevice() && !t->kicked()) {
-            D("transport: %s unref (attempting reconnection) %d", t->serial, t->kicked());
+            D("transport: %s unref (attempting reconnection) %d", t->serial.c_str(), t->kicked());
             reconnect_handler.TrackTransport(t);
         } else {
-            D("transport: %s unref (kicking and closing)", t->serial);
+            D("transport: %s unref (kicking and closing)", t->serial.c_str());
             remove_transport(t);
         }
     } else {
-        D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
+        D("transport: %s unref (count=%zu)", t->serial.c_str(), t->ref_count);
     }
 }
 
-static int qual_match(const char* to_test, const char* prefix, const char* qual,
+static int qual_match(const std::string& to_test, const char* prefix, const std::string& qual,
                       bool sanitize_qual) {
-    if (!to_test || !*to_test) /* Return true if both the qual and to_test are null strings. */
-        return !qual || !*qual;
+    if (to_test.empty()) /* Return true if both the qual and to_test are empty strings. */
+        return qual.empty();
 
-    if (!qual) return 0;
+    if (qual.empty()) return 0;
 
+    const char* ptr = to_test.c_str();
     if (prefix) {
         while (*prefix) {
-            if (*prefix++ != *to_test++) return 0;
+            if (*prefix++ != *ptr++) return 0;
         }
     }
 
-    while (*qual) {
-        char ch = *qual++;
+    for (char ch : qual) {
         if (sanitize_qual && !isalnum(ch)) ch = '_';
-        if (ch != *to_test++) return 0;
+        if (ch != *ptr++) return 0;
     }
 
-    /* Everything matched so far.  Return true if *to_test is a NUL. */
-    return !*to_test;
+    /* Everything matched so far.  Return true if *ptr is a NUL. */
+    return !*ptr;
 }
 
 atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id,
@@ -921,7 +915,7 @@
 
 void atransport::Kick() {
     if (!kicked_.exchange(true)) {
-        D("kicking transport %p %s", this, this->serial);
+        D("kicking transport %p %s", this, this->serial.c_str());
         this->connection()->Stop();
     }
 }
@@ -1040,7 +1034,7 @@
 }
 
 bool atransport::MatchesTarget(const std::string& target) const {
-    if (serial) {
+    if (!serial.empty()) {
         if (target == serial) {
             return true;
         } else if (type == kTransportLocal) {
@@ -1069,10 +1063,9 @@
         }
     }
 
-    return (devpath && target == devpath) ||
-           qual_match(target.c_str(), "product:", product, false) ||
-           qual_match(target.c_str(), "model:", model, true) ||
-           qual_match(target.c_str(), "device:", device, false);
+    return (target == devpath) || qual_match(target, "product:", product, false) ||
+           qual_match(target, "model:", model, true) ||
+           qual_match(target, "device:", device, false);
 }
 
 void atransport::SetConnectionEstablished(bool success) {
@@ -1093,9 +1086,9 @@
     return str;
 }
 
-static void append_transport_info(std::string* result, const char* key, const char* value,
+static void append_transport_info(std::string* result, const char* key, const std::string& value,
                                   bool alphanumeric) {
-    if (value == nullptr || *value == '\0') {
+    if (value.empty()) {
         return;
     }
 
@@ -1105,8 +1098,8 @@
 }
 
 static void append_transport(const atransport* t, std::string* result, bool long_listing) {
-    const char* serial = t->serial;
-    if (!serial || !serial[0]) {
+    std::string serial = t->serial;
+    if (serial.empty()) {
         serial = "(no serial number)";
     }
 
@@ -1115,7 +1108,8 @@
         *result += '\t';
         *result += t->connection_state_name();
     } else {
-        android::base::StringAppendF(result, "%-22s %s", serial, t->connection_state_name().c_str());
+        android::base::StringAppendF(result, "%-22s %s", serial.c_str(),
+                                     t->connection_state_name().c_str());
 
         append_transport_info(result, "", t->devpath, false);
         append_transport_info(result, "product:", t->product, false);
@@ -1138,7 +1132,7 @@
         if (x->type != y->type) {
             return x->type < y->type;
         }
-        return strcmp(x->serial, y->serial) < 0;
+        return x->serial < y->serial;
     });
 
     std::string result;
@@ -1181,7 +1175,7 @@
 
     std::unique_lock<std::recursive_mutex> lock(transport_lock);
     for (const auto& transport : pending_list) {
-        if (transport->serial && strcmp(serial, transport->serial) == 0) {
+        if (strcmp(serial, transport->serial.c_str()) == 0) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
                             << " is already in pending_list and fails to register";
             delete t;
@@ -1190,7 +1184,7 @@
     }
 
     for (const auto& transport : transport_list) {
-        if (transport->serial && strcmp(serial, transport->serial) == 0) {
+        if (strcmp(serial, transport->serial.c_str()) == 0) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
                             << " is already in transport_list and fails to register";
             delete t;
@@ -1199,7 +1193,7 @@
     }
 
     pending_list.push_front(t);
-    t->serial = strdup(serial);
+    t->serial = serial;
 
     lock.unlock();
 
@@ -1220,7 +1214,7 @@
 
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (auto& t : transport_list) {
-        if (t->serial && strcmp(serial, t->serial) == 0) {
+        if (strcmp(serial, t->serial.c_str()) == 0) {
             result = t;
             break;
         }
@@ -1251,11 +1245,11 @@
     D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb, serial ? serial : "");
     init_usb_transport(t, usb);
     if (serial) {
-        t->serial = strdup(serial);
+        t->serial = serial;
     }
 
     if (devpath) {
-        t->devpath = strdup(devpath);
+        t->devpath = devpath;
     }
 
     {
diff --git a/adb/transport.h b/adb/transport.h
index cb20615..e9c9d37 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -238,11 +238,11 @@
     TransportType type = kTransportAny;
 
     // Used to identify transports for clients.
-    char* serial = nullptr;
-    char* product = nullptr;
-    char* model = nullptr;
-    char* device = nullptr;
-    char* devpath = nullptr;
+    std::string serial;
+    std::string product;
+    std::string model;
+    std::string device;
+    std::string devpath;
 
     bool IsTcpDevice() const { return type == kTransportLocal; }
 
@@ -253,7 +253,7 @@
     char token[TOKEN_SIZE] = {};
     size_t failed_auth_attempts = 0;
 
-    std::string serial_name() const { return serial ? serial : "<unknown>"; }
+    std::string serial_name() const { return !serial.empty() ? serial : "<unknown>"; }
     std::string connection_state_name() const;
 
     void update_version(int version, size_t payload);
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 181d666..fa38238 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -490,7 +490,7 @@
             std::make_unique<BlockingConnectionAdapter>(std::move(emulator_connection)));
         std::lock_guard<std::mutex> lock(local_transports_lock);
         atransport* existing_transport = find_emulator_transport_by_adb_port_locked(adb_port);
-        if (existing_transport != NULL) {
+        if (existing_transport != nullptr) {
             D("local transport for port %d already registered (%p)?", adb_port, existing_transport);
             fail = -1;
         } else if (local_transports.size() >= ADB_LOCAL_TRANSPORT_MAX) {
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index d987d4f..8c628d8 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -86,9 +86,9 @@
     ASSERT_EQ(0U, t.features().size());
     ASSERT_EQ(kCsHost, t.GetConnectionState());
 
-    ASSERT_EQ(nullptr, t.product);
-    ASSERT_EQ(nullptr, t.model);
-    ASSERT_EQ(nullptr, t.device);
+    ASSERT_EQ(std::string(), t.product);
+    ASSERT_EQ(std::string(), t.model);
+    ASSERT_EQ(std::string(), t.device);
 }
 
 TEST(transport, parse_banner_product_features) {
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 94b2e37..602970c 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -122,7 +122,7 @@
 // On Android devices, we rely on the kernel to provide buffered read.
 // So we can recover automatically from EOVERFLOW.
 static int remote_read(apacket* p, usb_handle* usb) {
-    if (usb_read(usb, &p->msg, sizeof(amessage))) {
+    if (usb_read(usb, &p->msg, sizeof(amessage)) != sizeof(amessage)) {
         PLOG(ERROR) << "remote usb: read terminated (message)";
         return -1;
     }
@@ -134,7 +134,8 @@
         }
 
         p->payload.resize(p->msg.data_length);
-        if (usb_read(usb, &p->payload[0], p->payload.size())) {
+        if (usb_read(usb, &p->payload[0], p->payload.size())
+                != static_cast<int>(p->payload.size())) {
             PLOG(ERROR) << "remote usb: terminated (data)";
             return -1;
         }
@@ -154,14 +155,14 @@
 }
 
 bool UsbConnection::Write(apacket* packet) {
-    unsigned size = packet->msg.data_length;
+    int size = packet->msg.data_length;
 
-    if (usb_write(handle_, &packet->msg, sizeof(packet->msg)) != 0) {
+    if (usb_write(handle_, &packet->msg, sizeof(packet->msg)) != sizeof(packet->msg)) {
         PLOG(ERROR) << "remote usb: 1 - write terminated";
         return false;
     }
 
-    if (packet->msg.data_length != 0 && usb_write(handle_, packet->payload.data(), size) != 0) {
+    if (packet->msg.data_length != 0 && usb_write(handle_, packet->payload.data(), size) != size) {
         PLOG(ERROR) << "remote usb: 2 - write terminated";
         return false;
     }
diff --git a/adb/types.h b/adb/types.h
index c6b3f07..a3e5d48 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 #include <deque>
+#include <memory>
 #include <type_traits>
 #include <utility>
 #include <vector>
@@ -37,7 +38,7 @@
 
     template <typename Iterator>
     Block(Iterator begin, Iterator end) : Block(end - begin) {
-        std::copy(begin, end, data_);
+        std::copy(begin, end, data_.get());
     }
 
     Block(const Block& copy) = delete;
@@ -73,11 +74,11 @@
     void assign(InputIt begin, InputIt end) {
         clear();
         allocate(end - begin);
-        std::copy(begin, end, data_);
+        std::copy(begin, end, data_.get());
     }
 
     void clear() {
-        free(data_);
+        data_.reset();
         capacity_ = 0;
         size_ = 0;
     }
@@ -86,11 +87,11 @@
     size_t size() const { return size_; }
     bool empty() const { return size() == 0; }
 
-    char* data() { return data_; }
-    const char* data() const { return data_; }
+    char* data() { return data_.get(); }
+    const char* data() const { return data_.get(); }
 
-    char* begin() { return data_; }
-    const char* begin() const { return data_; }
+    char* begin() { return data_.get(); }
+    const char* begin() const { return data_.get(); }
 
     char* end() { return data() + size_; }
     const char* end() const { return data() + size_; }
@@ -108,13 +109,13 @@
         CHECK_EQ(0ULL, capacity_);
         CHECK_EQ(0ULL, size_);
         if (size != 0) {
-            data_ = static_cast<char*>(malloc(size));
+            data_ = std::make_unique<char[]>(size);
             capacity_ = size;
             size_ = size;
         }
     }
 
-    char* data_ = nullptr;
+    std::unique_ptr<char[]> data_;
     size_t capacity_ = 0;
     size_t size_ = 0;
 };
diff --git a/base/Android.bp b/base/Android.bp
index 3d80d97..46a0233 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -135,6 +135,7 @@
         "strings_test.cpp",
         "test_main.cpp",
         "test_utils_test.cpp",
+        "unique_fd_test.cpp",
     ],
     target: {
         android: {
diff --git a/base/file.cpp b/base/file.cpp
index 2f697a1..d6fe753 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -199,17 +199,23 @@
 bool RemoveFileIfExists(const std::string& path, std::string* err) {
   struct stat st;
 #if defined(_WIN32)
-  //TODO: Windows version can't handle symbol link correctly.
+  // TODO: Windows version can't handle symbolic links correctly.
   int result = stat(path.c_str(), &st);
   bool file_type_removable = (result == 0 && S_ISREG(st.st_mode));
 #else
   int result = lstat(path.c_str(), &st);
   bool file_type_removable = (result == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)));
 #endif
+  if (result == -1) {
+    if (errno == ENOENT || errno == ENOTDIR) return true;
+    if (err != nullptr) *err = strerror(errno);
+    return false;
+  }
+
   if (result == 0) {
     if (!file_type_removable) {
       if (err != nullptr) {
-        *err = "is not a regular or symbol link file";
+        *err = "is not a regular file or symbolic link";
       }
       return false;
     }
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 02b431d..6794652 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -26,6 +26,10 @@
 
 #include "android-base/test_utils.h"
 
+#if !defined(_WIN32)
+#include <pwd.h>
+#endif
+
 TEST(file, ReadFileToString_ENOENT) {
   std::string s("hello");
   errno = 0;
@@ -115,7 +119,7 @@
   ASSERT_FALSE(android::base::ReadFully(tf.fd, &s[0], s.size()));
 }
 
-TEST(file, RemoveFileIfExist) {
+TEST(file, RemoveFileIfExists) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
   close(tf.fd);
@@ -126,9 +130,43 @@
   TemporaryDir td;
   ASSERT_FALSE(android::base::RemoveFileIfExists(td.path));
   ASSERT_FALSE(android::base::RemoveFileIfExists(td.path, &err));
-  ASSERT_EQ("is not a regular or symbol link file", err);
+  ASSERT_EQ("is not a regular file or symbolic link", err);
 }
 
+TEST(file, RemoveFileIfExists_ENOTDIR) {
+  TemporaryFile tf;
+  close(tf.fd);
+  tf.fd = -1;
+  std::string err{"xxx"};
+  ASSERT_TRUE(android::base::RemoveFileIfExists(std::string{tf.path} + "/abc", &err));
+  ASSERT_EQ("xxx", err);
+}
+
+#if !defined(_WIN32)
+TEST(file, RemoveFileIfExists_EACCES) {
+  // EACCES -- one of the directories in the path has no search permission
+  // root can bypass permission restrictions, so drop root.
+  if (getuid() == 0) {
+    passwd* shell = getpwnam("shell");
+    setgid(shell->pw_gid);
+    setuid(shell->pw_uid);
+  }
+
+  TemporaryDir td;
+  TemporaryFile tf(td.path);
+  close(tf.fd);
+  tf.fd = -1;
+  std::string err{"xxx"};
+  // Remove dir's search permission.
+  ASSERT_TRUE(chmod(td.path, S_IRUSR | S_IWUSR) == 0);
+  ASSERT_FALSE(android::base::RemoveFileIfExists(tf.path, &err));
+  ASSERT_EQ("Permission denied", err);
+  // Set dir's search permission again.
+  ASSERT_TRUE(chmod(td.path, S_IRWXU) == 0);
+  ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err));
+}
+#endif
+
 TEST(file, Readlink) {
 #if !defined(_WIN32)
   // Linux doesn't allow empty symbolic links.
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
index c3396ee..11fcf71 100644
--- a/base/include/android-base/chrono_utils.h
+++ b/base/include/android-base/chrono_utils.h
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_CHRONO_UTILS_H
-#define ANDROID_BASE_CHRONO_UTILS_H
+#pragma once
 
 #include <chrono>
 #include <sstream>
 
-#if __cplusplus > 201103L  // C++14
+#if __cplusplus > 201103L && !defined(__WIN32)  // C++14
 using namespace std::chrono_literals;
 #endif
 
@@ -52,5 +51,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_CHRONO_UTILS_H
diff --git a/base/include/android-base/endian.h b/base/include/android-base/endian.h
index 6eb677c..cbbd8c9 100644
--- a/base/include/android-base/endian.h
+++ b/base/include/android-base/endian.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_ENDIAN_H
-#define ANDROID_BASE_ENDIAN_H
+#pragma once
 
 /* A cross-platform equivalent of bionic's <sys/endian.h>. */
 
@@ -86,5 +85,3 @@
 #define le64toh(x) (x)
 
 #endif
-
-#endif  // ANDROID_BASE_ENDIAN_H
diff --git a/base/include/android-base/errors.h b/base/include/android-base/errors.h
index 04c299c..06f29fc 100644
--- a/base/include/android-base/errors.h
+++ b/base/include/android-base/errors.h
@@ -27,8 +27,7 @@
 // special handling to get the error string. Refer to Microsoft documentation
 // to determine which error code to check for each function.
 
-#ifndef ANDROID_BASE_ERRORS_H
-#define ANDROID_BASE_ERRORS_H
+#pragma once
 
 #include <string>
 
@@ -42,5 +41,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_ERRORS_H
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 667d6fb..908690b 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_FILE_H
-#define ANDROID_BASE_FILE_H
+#pragma once
 
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -78,5 +77,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif // ANDROID_BASE_FILE_H
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index 05a12e7..aea6ce6 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_LOGGING_H
-#define ANDROID_BASE_LOGGING_H
+#pragma once
 
 //
 // Google-style C++ logging.
@@ -488,23 +487,14 @@
 // Note: to print the pointer, use "<< static_cast<const void*>(string_pointer)" instead.
 // Note: a not-recommended alternative is to let Clang ignore the warning by adding
 //       -Wno-user-defined-warnings to CPPFLAGS.
-#ifdef __clang__
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wgcc-compat"
 #define OSTREAM_STRING_POINTER_USAGE_WARNING \
     __attribute__((diagnose_if(true, "Unexpected logging of string pointer", "warning")))
-#else
-#define OSTREAM_STRING_POINTER_USAGE_WARNING /* empty */
-#endif
 inline std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer)
     OSTREAM_STRING_POINTER_USAGE_WARNING {
   return stream << static_cast<const void*>(string_pointer);
 }
-#ifdef __clang__
 #pragma clang diagnostic pop
-#endif
-#undef OSTREAM_STRING_POINTER_USAGE_WARNING
 
 }  // namespace std
-
-#endif  // ANDROID_BASE_LOGGING_H
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index fd6efb2..49cc0c9 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_MACROS_H
-#define ANDROID_BASE_MACROS_H
+#pragma once
 
 #include <stddef.h>  // for size_t
 #include <unistd.h>  // for TEMP_FAILURE_RETRY
@@ -171,17 +170,7 @@
 //
 //  In either case this macro has no effect on runtime behavior and performance
 //  of code.
-#if defined(__clang__) && defined(__has_warning)
-#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
 #define FALLTHROUGH_INTENDED [[clang::fallthrough]]  // NOLINT
-#endif
-#endif
-
-#ifndef FALLTHROUGH_INTENDED
-#define FALLTHROUGH_INTENDED \
-  do {                       \
-  } while (0)
-#endif
 
 // Current ABI string
 #if defined(__arm__)
@@ -197,5 +186,3 @@
 #elif defined(__mips__) && defined(__LP64__)
 #define ABI_STRING "mips64"
 #endif
-
-#endif  // ANDROID_BASE_MACROS_H
diff --git a/base/include/android-base/memory.h b/base/include/android-base/memory.h
index 9971226..0277a03 100644
--- a/base/include/android-base/memory.h
+++ b/base/include/android-base/memory.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_MEMORY_H
-#define ANDROID_BASE_MEMORY_H
+#pragma once
 
 namespace android {
 namespace base {
@@ -37,5 +36,3 @@
 
 } // namespace base
 } // namespace android
-
-#endif  // ANDROID_BASE_MEMORY_H
diff --git a/base/include/android-base/parsedouble.h b/base/include/android-base/parsedouble.h
index daa6902..c273c61 100644
--- a/base/include/android-base/parsedouble.h
+++ b/base/include/android-base/parsedouble.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_PARSEDOUBLE_H
-#define ANDROID_BASE_PARSEDOUBLE_H
+#pragma once
 
 #include <errno.h>
 #include <stdlib.h>
@@ -46,5 +45,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_PARSEDOUBLE_H
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
index 1b7cc5f..bb54c99 100644
--- a/base/include/android-base/parseint.h
+++ b/base/include/android-base/parseint.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_PARSEINT_H
-#define ANDROID_BASE_PARSEINT_H
+#pragma once
 
 #include <errno.h>
 #include <stdlib.h>
@@ -27,9 +26,9 @@
 namespace android {
 namespace base {
 
-// Parses the unsigned decimal integer in the string 's' and sets 'out' to
-// that value. Optionally allows the caller to define a 'max' beyond which
-// otherwise valid values will be rejected. Returns boolean success; 'out'
+// Parses the unsigned decimal or hexadecimal integer in the string 's' and sets
+// 'out' to that value. Optionally allows the caller to define a 'max' beyond
+// which otherwise valid values will be rejected. Returns boolean success; 'out'
 // is untouched if parsing fails.
 template <typename T>
 bool ParseUint(const char* s, T* out, T max = std::numeric_limits<T>::max(),
@@ -43,14 +42,14 @@
     const char* suffixes = "bkmgtpe";
     const char* suffix;
     if (!allow_suffixes || (suffix = strchr(suffixes, tolower(*end))) == nullptr) return false;
-#if __clang__  // TODO: win32 still builds with GCC :-(
     if (__builtin_mul_overflow(result, 1ULL << (10 * (suffix - suffixes)), &result)) return false;
-#endif
   }
   if (max < result) {
     return false;
   }
-  *out = static_cast<T>(result);
+  if (out != nullptr) {
+    *out = static_cast<T>(result);
+  }
   return true;
 }
 
@@ -72,8 +71,8 @@
   return ParseByteCount(s.c_str(), out, max);
 }
 
-// Parses the signed decimal integer in the string 's' and sets 'out' to
-// that value. Optionally allows the caller to define a 'min' and 'max
+// Parses the signed decimal or hexadecimal integer in the string 's' and sets
+// 'out' to that value. Optionally allows the caller to define a 'min' and 'max'
 // beyond which otherwise valid values will be rejected. Returns boolean
 // success; 'out' is untouched if parsing fails.
 template <typename T>
@@ -90,7 +89,9 @@
   if (result < min || max < result) {
     return false;
   }
-  *out = static_cast<T>(result);
+  if (out != nullptr) {
+    *out = static_cast<T>(result);
+  }
   return true;
 }
 
@@ -104,5 +105,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_PARSEINT_H
diff --git a/base/include/android-base/parsenetaddress.h b/base/include/android-base/parsenetaddress.h
index b4ac025..47f8b5f 100644
--- a/base/include/android-base/parsenetaddress.h
+++ b/base/include/android-base/parsenetaddress.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_PARSENETADDRESS_H
-#define ANDROID_BASE_PARSENETADDRESS_H
+#pragma once
 
 #include <string>
 
@@ -34,5 +33,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_PARSENETADDRESS_H
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 3a05143..31e5273 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_PROPERTIES_H
-#define ANDROID_BASE_PROPERTIES_H
+#pragma once
 
 #include <sys/cdefs.h>
 
@@ -73,5 +72,3 @@
 
 } // namespace base
 } // namespace android
-
-#endif  // ANDROID_BASE_PROPERTIES_H
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
index c314e02..e6a9d10 100644
--- a/base/include/android-base/scopeguard.h
+++ b/base/include/android-base/scopeguard.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_SCOPEGUARD_H
-#define ANDROID_BASE_SCOPEGUARD_H
+#pragma once
 
 #include <utility>  // for std::move, std::forward
 
@@ -66,5 +65,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_SCOPEGUARD_H
diff --git a/base/include/android-base/stringprintf.h b/base/include/android-base/stringprintf.h
index 1fd6297..93c56af 100644
--- a/base/include/android-base/stringprintf.h
+++ b/base/include/android-base/stringprintf.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_STRINGPRINTF_H
-#define ANDROID_BASE_STRINGPRINTF_H
+#pragma once
 
 #include <stdarg.h>
 #include <string>
@@ -24,33 +23,18 @@
 namespace base {
 
 // These printf-like functions are implemented in terms of vsnprintf, so they
-// use the same attribute for compile-time format string checking. On Windows,
-// if the mingw version of vsnprintf is used, use `gnu_printf' which allows z
-// in %zd and PRIu64 (and related) to be recognized by the compile-time
-// checking.
-#define ANDROID_BASE_FORMAT_ARCHETYPE __printf__
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-#undef ANDROID_BASE_FORMAT_ARCHETYPE
-#define ANDROID_BASE_FORMAT_ARCHETYPE gnu_printf
-#endif
-#endif
+// use the same attribute for compile-time format string checking.
 
 // Returns a string corresponding to printf-like formatting of the arguments.
-std::string StringPrintf(const char* fmt, ...)
-    __attribute__((__format__(ANDROID_BASE_FORMAT_ARCHETYPE, 1, 2)));
+std::string StringPrintf(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
 
 // Appends a printf-like formatting of the arguments to 'dst'.
 void StringAppendF(std::string* dst, const char* fmt, ...)
-    __attribute__((__format__(ANDROID_BASE_FORMAT_ARCHETYPE, 2, 3)));
+    __attribute__((__format__(__printf__, 2, 3)));
 
 // Appends a printf-like formatting of the arguments to 'dst'.
 void StringAppendV(std::string* dst, const char* format, va_list ap)
-    __attribute__((__format__(ANDROID_BASE_FORMAT_ARCHETYPE, 2, 0)));
-
-#undef ANDROID_BASE_FORMAT_ARCHETYPE
+    __attribute__((__format__(__printf__, 2, 0)));
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_STRINGPRINTF_H
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index 4d9fa34..9c35560 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_STRINGS_H
-#define ANDROID_BASE_STRINGS_H
+#pragma once
 
 #include <sstream>
 #include <string>
@@ -75,5 +74,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_STRINGS_H
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index b29676f..9e2ea97 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_TEST_UTILS_H
-#define ANDROID_BASE_TEST_UTILS_H
+#pragma once
 
 #include <regex>
 #include <string>
@@ -114,5 +113,3 @@
       ADD_FAILURE() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << (str); \
     }                                                                                             \
   } while (0)
-
-#endif  // ANDROID_BASE_TEST_UTILS_H
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index 1307f0e..5c55e63 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -14,14 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_THREAD_ANNOTATIONS_H
-#define ANDROID_BASE_THREAD_ANNOTATIONS_H
+#pragma once
 
-#if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__)
-#define THREAD_ANNOTATION_ATTRIBUTE__(x)   __attribute__((x))
-#else
-#define THREAD_ANNOTATION_ATTRIBUTE__(x)   // no-op
-#endif
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
 
 #define CAPABILITY(x) \
       THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
@@ -109,5 +104,3 @@
 
 #define NO_THREAD_SAFETY_ANALYSIS \
       THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
-
-#endif  // ANDROID_BASE_THREAD_ANNOTATIONS_H
diff --git a/base/include/android-base/threads.h b/base/include/android-base/threads.h
index 85e65ba..dba1fc6 100644
--- a/base/include/android-base/threads.h
+++ b/base/include/android-base/threads.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_THREADS_H
-#define ANDROID_BASE_THREADS_H
+#pragma once
 
 #include <stdint.h>
 
@@ -25,4 +24,7 @@
 }
 }  // namespace android
 
+#if defined(__GLIBC__)
+// bionic has this Linux-specifix call, but glibc doesn't.
+extern "C" int tgkill(int tgid, int tid, int sig);
 #endif
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 5d89271..c6936f1 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_UNIQUE_FD_H
-#define ANDROID_BASE_UNIQUE_FD_H
+#pragma once
 
 #include <fcntl.h>
 
@@ -43,10 +42,35 @@
 //
 // unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help
 // you find this class if you're searching for one of those names.
+
+#if defined(__BIONIC__)
+#include <android/fdsan.h>
+#endif
+
 namespace android {
 namespace base {
 
 struct DefaultCloser {
+#if defined(__BIONIC__)
+  static void Tag(int fd, void* old_addr, void* new_addr) {
+    if (android_fdsan_exchange_owner_tag) {
+      uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
+                                                        reinterpret_cast<uint64_t>(old_addr));
+      uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
+                                                        reinterpret_cast<uint64_t>(new_addr));
+      android_fdsan_exchange_owner_tag(fd, old_tag, new_tag);
+    }
+  }
+  static void Close(int fd, void* addr) {
+    if (android_fdsan_close_with_tag) {
+      uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
+                                                    reinterpret_cast<uint64_t>(addr));
+      android_fdsan_close_with_tag(fd, tag);
+    } else {
+      close(fd);
+    }
+  }
+#else
   static void Close(int fd) {
     // Even if close(2) fails with EINTR, the fd will have been closed.
     // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone
@@ -54,40 +78,75 @@
     // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
     ::close(fd);
   }
+#endif
 };
 
 template <typename Closer>
 class unique_fd_impl final {
  public:
-  unique_fd_impl() : value_(-1) {}
+  unique_fd_impl() {}
 
-  explicit unique_fd_impl(int value) : value_(value) {}
+  explicit unique_fd_impl(int fd) { reset(fd); }
   ~unique_fd_impl() { reset(); }
 
-  unique_fd_impl(unique_fd_impl&& other) : value_(other.release()) {}
+  unique_fd_impl(unique_fd_impl&& other) { reset(other.release()); }
   unique_fd_impl& operator=(unique_fd_impl&& s) {
-    reset(s.release());
+    int fd = s.fd_;
+    s.fd_ = -1;
+    reset(fd, &s);
     return *this;
   }
 
-  void reset(int new_value = -1) {
-    if (value_ != -1) {
-      Closer::Close(value_);
-    }
-    value_ = new_value;
-  }
+  void reset(int new_value = -1) { reset(new_value, nullptr); }
 
-  int get() const { return value_; }
+  int get() const { return fd_; }
   operator int() const { return get(); }
 
   int release() __attribute__((warn_unused_result)) {
-    int ret = value_;
-    value_ = -1;
+    tag(fd_, this, nullptr);
+    int ret = fd_;
+    fd_ = -1;
     return ret;
   }
 
  private:
-  int value_;
+  void reset(int new_value, void* previous_tag) {
+    if (fd_ != -1) {
+      close(fd_, this);
+    }
+
+    fd_ = new_value;
+    if (new_value != -1) {
+      tag(new_value, previous_tag, this);
+    }
+  }
+
+  int fd_ = -1;
+
+  // Template magic to use Closer::Tag if available, and do nothing if not.
+  // If Closer::Tag exists, this implementation is preferred, because int is a better match.
+  // If not, this implementation is SFINAEd away, and the no-op below is the only one that exists.
+  template <typename T = Closer>
+  static auto tag(int fd, void* old_tag, void* new_tag)
+      -> decltype(T::Tag(fd, old_tag, new_tag), void()) {
+    T::Tag(fd, old_tag, new_tag);
+  }
+
+  template <typename T = Closer>
+  static void tag(long, void*, void*) {
+    // No-op.
+  }
+
+  // Same as above, to select between Closer::Close(int) and Closer::Close(int, void*).
+  template <typename T = Closer>
+  static auto close(int fd, void* tag_value) -> decltype(T::Close(fd, tag_value), void()) {
+    T::Close(fd, tag_value);
+  }
+
+  template <typename T = Closer>
+  static auto close(int fd, void*) -> decltype(T::Close(fd), void()) {
+    T::Close(fd);
+  }
 
   unique_fd_impl(const unique_fd_impl&);
   void operator=(const unique_fd_impl&);
@@ -98,7 +157,8 @@
 #if !defined(_WIN32)
 
 // Inline functions, so that they can be used header-only.
-inline bool Pipe(unique_fd* read, unique_fd* write) {
+template <typename Closer>
+inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write) {
   int pipefd[2];
 
 #if defined(__linux__)
@@ -122,7 +182,9 @@
   return true;
 }
 
-inline bool Socketpair(int domain, int type, int protocol, unique_fd* left, unique_fd* right) {
+template <typename Closer>
+inline bool Socketpair(int domain, int type, int protocol, unique_fd_impl<Closer>* left,
+                       unique_fd_impl<Closer>* right) {
   int sockfd[2];
   if (socketpair(domain, type, protocol, sockfd) != 0) {
     return false;
@@ -132,7 +194,8 @@
   return true;
 }
 
-inline bool Socketpair(int type, unique_fd* left, unique_fd* right) {
+template <typename Closer>
+inline bool Socketpair(int type, unique_fd_impl<Closer>* left, unique_fd_impl<Closer>* right) {
   return Socketpair(AF_UNIX, type, 0, left, right);
 }
 
@@ -143,12 +206,4 @@
 
 template <typename T>
 int close(const android::base::unique_fd_impl<T>&)
-#if defined(__clang__)
-  __attribute__((__unavailable__(
-#else
-  __attribute__((__error__(
-#endif
-    "close called on unique_fd"
-  )));
-
-#endif  // ANDROID_BASE_UNIQUE_FD_H
+    __attribute__((__unavailable__("close called on unique_fd")));
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
index c9cc1ab..4b91623 100755
--- a/base/include/android-base/utf8.h
+++ b/base/include/android-base/utf8.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_UTF8_H
-#define ANDROID_BASE_UTF8_H
+#pragma once
 
 #ifdef _WIN32
 #include <string>
@@ -102,5 +101,3 @@
 }  // namespace utf8
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_UTF8_H
diff --git a/base/parseint_test.cpp b/base/parseint_test.cpp
index fb1c339..8f9ed77 100644
--- a/base/parseint_test.cpp
+++ b/base/parseint_test.cpp
@@ -36,6 +36,10 @@
   ASSERT_EQ(12, i);
   ASSERT_FALSE(android::base::ParseInt("-12", &i, 0, 15));
   ASSERT_FALSE(android::base::ParseInt("16", &i, 0, 15));
+
+  ASSERT_FALSE(android::base::ParseInt<int>("x", nullptr));
+  ASSERT_FALSE(android::base::ParseInt<int>("123x", nullptr));
+  ASSERT_TRUE(android::base::ParseInt<int>("1234", nullptr));
 }
 
 TEST(parseint, unsigned_smoke) {
@@ -55,6 +59,10 @@
   ASSERT_EQ(12u, i);
   ASSERT_FALSE(android::base::ParseUint("-12", &i, 15u));
   ASSERT_FALSE(android::base::ParseUint("16", &i, 15u));
+
+  ASSERT_FALSE(android::base::ParseUint<unsigned short>("x", nullptr));
+  ASSERT_FALSE(android::base::ParseUint<unsigned short>("123x", nullptr));
+  ASSERT_TRUE(android::base::ParseUint<unsigned short>("1234", nullptr));
 }
 
 TEST(parseint, no_implicit_octal) {
diff --git a/base/threads.cpp b/base/threads.cpp
index a71382b..48f6197 100644
--- a/base/threads.cpp
+++ b/base/threads.cpp
@@ -46,3 +46,9 @@
 
 }  // namespace base
 }  // namespace android
+
+#if defined(__GLIBC__)
+int tgkill(int tgid, int tid, int sig) {
+  return syscall(__NR_tgkill, tgid, tid, sig);
+}
+#endif
diff --git a/base/unique_fd_test.cpp b/base/unique_fd_test.cpp
new file mode 100644
index 0000000..3fdf12a
--- /dev/null
+++ b/base/unique_fd_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/unique_fd.h"
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+using android::base::unique_fd;
+
+TEST(unique_fd, unowned_close) {
+#if defined(__BIONIC__)
+  unique_fd fd(open("/dev/null", O_RDONLY));
+  EXPECT_DEATH(close(fd.get()), "incorrect tag");
+#endif
+}
+
+TEST(unique_fd, untag_on_release) {
+  unique_fd fd(open("/dev/null", O_RDONLY));
+  close(fd.release());
+}
+
+TEST(unique_fd, move) {
+  unique_fd fd(open("/dev/null", O_RDONLY));
+  unique_fd fd_moved = std::move(fd);
+  ASSERT_EQ(-1, fd.get());
+  ASSERT_GT(fd_moved.get(), -1);
+}
+
+TEST(unique_fd, unowned_close_after_move) {
+#if defined(__BIONIC__)
+  unique_fd fd(open("/dev/null", O_RDONLY));
+  unique_fd fd_moved = std::move(fd);
+  ASSERT_EQ(-1, fd.get());
+  ASSERT_GT(fd_moved.get(), -1);
+  EXPECT_DEATH(close(fd_moved.get()), "incorrect tag");
+#endif
+}
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index ddd1caf..71d3ecb 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -24,6 +24,7 @@
 NORMAL="${ESCAPE}[0m"
 # Best guess to an average device's reboot time, refined as tests return
 DURATION_DEFAULT=45
+STOP_ON_FAILURE=false
 
 # Helper functions
 
@@ -50,11 +51,18 @@
   fi
 }
 
+[ "USAGE: get_property <prop>
+
+Returns the property value" ]
+get_property() {
+  adb shell getprop ${1} 2>&1 </dev/null
+}
+
 [ "USAGE: isDebuggable
 
 Returns: true if device is (likely) a debug build" ]
 isDebuggable() {
-  if inAdb && [ 1 -ne `adb shell getprop ro.debuggable` ]; then
+  if inAdb && [ 1 -ne `get_property ro.debuggable` ]; then
     false
   fi
 }
@@ -93,7 +101,7 @@
     return 1
   fi
   adb shell su root setprop persist.test.boot.reason "'${1}'" 2>/dev/null
-  test_reason="`adb shell getprop persist.test.boot.reason 2>/dev/null`"
+  test_reason="`get_property persist.test.boot.reason`"
   if [ X"${test_reason}" != X"${1}" ]; then
     echo "ERROR: can not set persist.test.boot.reason to '${1}'." >&2
     return 1
@@ -188,9 +196,9 @@
       if [ 0 != ${counter} ]; then
         adb wait-for-device </dev/null >/dev/null 2>/dev/null
       fi
-      if [ -n "`adb shell getprop sys.boot.reason </dev/null 2>/dev/null`" ]
+      if [ -n "`get_property sys.boot.reason`" ]
       then
-        vals=`adb shell getprop </dev/null 2>/dev/null |
+        vals=`get_property |
               sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
         if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]
         then
@@ -223,15 +231,38 @@
   rval="${2}"
   shift 2
   if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
-    echo "ERROR: expected \"${lval}\" got \"${rval}\"" >&2
-    if [ -n "${*}" ] ; then
-      echo "       ${*}" >&2
+    if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
+*}" ]; then
+      echo "ERROR: expected \"${lval}\"" >&2
+      echo "       got \"${rval}\"" |
+        sed ': again
+             N
+             s/\(\n\)\([^ ]\)/\1             \2/
+             t again' >&2
+      if [ -n "${*}" ] ; then
+        echo "       ${*}" >&2
+      fi
+    else
+      echo "ERROR: expected \"${lval}\" got \"${rval}\" ${*}" >&2
     fi
     return 1
   fi
   if [ -n "${*}" ] ; then
     if [ X"${lval}" != X"${rval}" ]; then
-      echo "INFO: ok \"${lval}\"(=\"${rval}\") ${*}" >&2
+      if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval%
+*}" ]; then
+        echo "INFO: ok \"${lval}\"" >&2
+        echo "       = \"${rval}\"" |
+          sed ': again
+               N
+               s/\(\n\)\([^ ]\)/\1          \2/
+               t again' >&2
+        if [ -n "${*}" ] ; then
+          echo "      ${*}" >&2
+        fi
+      else
+        echo "INFO: ok \"${lval}\" = \"${rval}\" ${*}" >&2
+      fi
     else
       echo "INFO: ok \"${lval}\" ${*}" >&2
     fi
@@ -250,7 +281,7 @@
   property="${1}"
   value="${2}"
   shift 2
-  val=`adb shell getprop ${property} 2>&1`
+  val=`get_property ${property}`
   EXPECT_EQ "${value}" "${val}" for Android property ${property}
   local_ret=${?}
   if [ 0 != ${local_ret} -a "ro.boot.bootreason" = "${property}" ]; then
@@ -317,6 +348,7 @@
 init    : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
 init    : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
 init    : Command 'exec - system log -- /system/bin/bootstat --record_time_since_factory_reset' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+init    : Command 'exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc
  (/system/bin/bootstat --record_boot_complete)'...
  (/system/bin/bootstat --record_boot_complete)' (pid${SPACE}
  (/system/bin/bootstat --record_boot_reason)'...
@@ -392,6 +424,9 @@
     echo "${GREEN}[       OK ]${NORMAL} ${TEST} ${*}"
   else
     echo "${RED}[  FAILED  ]${NORMAL} ${TEST} ${*}"
+    if ${STOP_ON_FAILURE}; then
+      exit ${save_ret}
+    fi
   fi
   return ${save_ret}
 }
@@ -462,7 +497,7 @@
 
 NB: must also roughly match heuristics in system/core/bootstat/bootstat.cpp" ]
 validate_property() {
-  val="`adb shell getprop ${1} 2>&1`"
+  val=`get_property ${1}`
   ret=`validate_reason "${val}"`
   if [ "reboot" = "${ret}" ]; then
     ret=`validate_reason "reboot,${val}"`
@@ -470,6 +505,17 @@
   echo ${ret}
 }
 
+[ "USAGE: check_boilerblate_properties
+
+Check for common property values" ]
+check_boilerplate_properties() {
+  EXPECT_PROPERTY persist.sys.boot.reason ""
+  save_ret=${?}
+  reason=`validate_property sys.boot.reason`
+  ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
+  EXPECT_PROPERTY persist.sys.boot.reason.history "${reason},[1-9][0-9]*\(\|[^0-9].*\)"
+}
+
 #
 # Actual test frames
 #
@@ -487,6 +533,7 @@
   duration_test 1
   wait_for_screen
   retval=0
+  # sys.boot.reason is last for a reason
   check_set="ro.boot.bootreason sys.boot.reason.last sys.boot.reason"
   bootloader=""
   # NB: this test could fail if performed _after_ optional_factory_reset test
@@ -498,12 +545,11 @@
     check_set="ro.boot.bootreason sys.boot.reason"
     bootloader="bootloader"
   fi
-  EXPECT_PROPERTY persist.sys.boot.reason ""
   for prop in ${check_set}; do
     reason=`validate_property ${prop}`
     EXPECT_PROPERTY ${prop} ${reason} || retval=${?}
   done
-  # sys.boot.reason is last for a reason
+  check_boilerplate_properties || retval=${?}
   report_bootstat_logs ${reason} ${bootloader}
   return ${retval}
 }
@@ -557,7 +603,7 @@
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason "\(reboot,ota\|bootloader\)"
   EXPECT_PROPERTY sys.boot.reason.last bootloader
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs reboot,ota bootloader
 }
 
@@ -572,7 +618,7 @@
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,ota
   EXPECT_PROPERTY sys.boot.reason.last reboot,ota
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs reboot,ota
 }
 
@@ -604,7 +650,7 @@
   fi
   EXPECT_PROPERTY sys.boot.reason ${reasons}
   EXPECT_PROPERTY sys.boot.reason.last ${reason}
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs ${reason} ${bootloader_reason}
 }
 
@@ -638,7 +684,7 @@
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
   EXPECT_PROPERTY sys.boot.reason.last "reboot,.*"
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs reboot,factory_reset reboot, reboot,adb \
     "-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
     "-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date"
@@ -670,7 +716,7 @@
   ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
   EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
   EXPECT_PROPERTY sys.boot.reason.last ""
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs reboot,factory_reset bootloader \
     "-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \
     "-bootstat: Failed to parse boot time record: /data/misc/bootstat/last_boot_time_utc" \
@@ -744,7 +790,7 @@
 
   EXPECT_PROPERTY sys.boot.reason shutdown,battery
   EXPECT_PROPERTY sys.boot.reason.last cold
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs shutdown,battery "-bootstat: Battery level at shutdown 2%"
   exitPstore
 }
@@ -766,7 +812,7 @@
   wait_for_screen -n >&2
   EXPECT_PROPERTY sys.boot.reason shutdown,battery
   EXPECT_PROPERTY sys.boot.reason.last shutdown,battery
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs shutdown,battery
 }
 
@@ -787,7 +833,7 @@
   wait_for_screen -n >&2
   EXPECT_PROPERTY sys.boot.reason shutdown,thermal,battery
   EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal,battery
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs shutdown,thermal,battery
 }
 
@@ -824,7 +870,7 @@
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
   EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs kernel_panic,sysrq
   exitPstore
 }
@@ -852,7 +898,7 @@
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
   EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs kernel_panic,sysrq,test \
     "-bootstat: Unknown boot reason: kernel_panic,sysrq,test"
   exitPstore
@@ -883,7 +929,7 @@
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
   EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs kernel_panic,hung
   exitPstore
 }
@@ -916,7 +962,7 @@
   wait_for_screen -n >&2
   EXPECT_PROPERTY sys.boot.reason shutdown,thermal
   EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs shutdown,thermal
 }
 
@@ -937,7 +983,7 @@
   wait_for_screen -n >&2
   EXPECT_PROPERTY sys.boot.reason shutdown,userrequested
   EXPECT_PROPERTY sys.boot.reason.last shutdown,userrequested
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs shutdown,userrequested
 }
 
@@ -954,7 +1000,7 @@
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,shell
   EXPECT_PROPERTY sys.boot.reason.last reboot,shell
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs reboot,shell
 }
 
@@ -971,7 +1017,7 @@
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,adb
   EXPECT_PROPERTY sys.boot.reason.last reboot,adb
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs reboot,adb
 }
 
@@ -1007,7 +1053,7 @@
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
   EXPECT_PROPERTY sys.boot.reason.last reboot,its_just_so_hard
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs reboot,its_just_so_hard
 }
 
@@ -1057,7 +1103,7 @@
   EXPECT_PROPERTY ro.boot.bootreason "${bootloader_expected}"
   EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
   EXPECT_PROPERTY sys.boot.reason.last "${last_expected}"
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  check_boilerplate_properties
   report_bootstat_logs "${sys_expected}"
 }
 
@@ -1111,88 +1157,98 @@
   shift 2
 fi
 
-if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
-  echo "USAGE: ${0##*/} [-s SERIAL] [tests]"
-  echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
-  exit 0
-fi
+# Helpful for debugging, allows us to import the functions.
+if [ X"--macros" != X"${1}" ]; then
 
-# Check if all conditions for the script are sane
+  if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
+    echo "USAGE: ${0##*/} [-s SERIAL] [tests]"
+    echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
+    exit 0
+  fi
 
-if [ -z "${ANDROID_SERIAL}" ]; then
-  ndev=`(
-      adb devices | grep -v 'List of devices attached'
-      fastboot devices
-    ) |
-    grep -v "^[${SPACE}${TAB}]*\$" |
-    wc -l`
-  if [ ${ndev} -gt 1 ]; then
-    echo "ERROR: no target device specified, ${ndev} connected" >&2
-    echo "${RED}[  FAILED  ]${NORMAL}"
-    exit 1
+  if [ X"--stop" = X"${1}" ]; then
+    STOP_ON_FAILURE=true
+    shift
   fi
-  echo "WARNING: no target device specified" >&2
-fi
 
-ret=0
+  # Check if all conditions for the script are sane
 
-# Test Series
-if [ X"all" = X"${*}" ]; then
-  # automagically pick up all test_<function>s.
-  eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
-  if [ X"nothing" = X"${1}" ]; then
-    shift 1
-  fi
-fi
-if [ -z "$*" ]; then
-  # automagically pick up all test_<function>, except test_optional_<function>.
-  eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null |
-                            grep -v '^optional_'`
-  if [ -z "${2}" ]; then
-    # Hard coded should shell fail to find them above (search/permission issues)
-    eval set properties ota cold factory_reset hard battery unknown \
-             kernel_panic kernel_panic_subreason kernel_panic_hung warm \
-             thermal_shutdown userrequested_shutdown shell_reboot adb_reboot \
-             Its_Just_So_Hard_reboot bootloader_normal bootloader_watchdog \
-             bootloader_kernel_panic bootloader_oem_powerkey \
-             bootloader_wdog_reset bootloader_wdog_reset bootloader_wdog_reset \
-             bootloader_hard bootloader_recovery
-  fi
-  if [ X"nothing" = X"${1}" ]; then
-    shift 1
-  fi
-fi
-echo "INFO: selected test(s): ${@}" >&2
-echo
-# Prepare device
-setBootloaderBootReason 2>/dev/null
-# Start pouring through the tests.
-failures=
-successes=
-for t in "${@}"; do
-  wrap_test ${t}
-  retval=${?}
-  if [ 0 = ${retval} ]; then
-    if [ -z "${successes}" ]; then
-      successes=${t}
-    else
-      successes="${successes} ${t}"
+  if [ -z "${ANDROID_SERIAL}" ]; then
+    ndev=`(
+        adb devices | grep -v 'List of devices attached'
+        fastboot devices
+      ) |
+      grep -v "^[${SPACE}${TAB}]*\$" |
+      wc -l`
+    if [ ${ndev} -gt 1 ]; then
+      echo "ERROR: no target device specified, ${ndev} connected" >&2
+      echo "${RED}[  FAILED  ]${NORMAL}"
+      exit 1
     fi
-  else
-    ret=${retval}
-    if [ -z "${failures}" ]; then
-      failures=${t}
-    else
-      failures="${failures} ${t}"
+    echo "WARNING: no target device specified" >&2
+  fi
+
+  ret=0
+
+  # Test Series
+  if [ X"all" = X"${*}" ]; then
+    # automagically pick up all test_<function>s.
+    eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
+    if [ X"nothing" = X"${1}" ]; then
+      shift 1
     fi
   fi
+  if [ -z "$*" ]; then
+    # automagically pick up all test_<function>, except test_optional_<function>.
+    eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null |
+                              grep -v '^optional_'`
+    if [ -z "${2}" ]; then
+      # Hard coded should shell fail to find them (search/permission issues)
+      eval set properties ota cold factory_reset hard battery unknown \
+               kernel_panic kernel_panic_subreason kernel_panic_hung warm \
+               thermal_shutdown userrequested_shutdown shell_reboot adb_reboot \
+               Its_Just_So_Hard_reboot bootloader_normal bootloader_watchdog \
+               bootloader_kernel_panic bootloader_oem_powerkey \
+               bootloader_wdog_reset bootloader_cold bootloader_warm \
+               bootloader_hard bootloader_recovery
+    fi
+    if [ X"nothing" = X"${1}" ]; then
+      shift 1
+    fi
+  fi
+  echo "INFO: selected test(s): ${@}" >&2
   echo
-done
+  # Prepare device
+  setBootloaderBootReason 2>/dev/null
+  # Start pouring through the tests.
+  failures=
+  successes=
+  for t in "${@}"; do
+    wrap_test ${t}
+    retval=${?}
+    if [ 0 = ${retval} ]; then
+      if [ -z "${successes}" ]; then
+        successes=${t}
+      else
+        successes="${successes} ${t}"
+      fi
+    else
+      ret=${retval}
+      if [ -z "${failures}" ]; then
+        failures=${t}
+      else
+        failures="${failures} ${t}"
+      fi
+    fi
+    echo
+  done
 
-if [ -n "${successes}" ]; then
-  echo "${GREEN}[  PASSED  ]${NORMAL} ${successes}"
+  if [ -n "${successes}" ]; then
+    echo "${GREEN}[  PASSED  ]${NORMAL} ${successes}"
+  fi
+  if [ -n "${failures}" ]; then
+    echo "${RED}[  FAILED  ]${NORMAL} ${failures}"
+  fi
+  exit ${ret}
+
 fi
-if [ -n "${failures}" ]; then
-  echo "${RED}[  FAILED  ]${NORMAL} ${failures}"
-fi
-exit ${ret}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 7ec57ec..4d9f1ac 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -27,6 +27,7 @@
 #include <cstddef>
 #include <cstdio>
 #include <ctime>
+#include <iterator>
 #include <map>
 #include <memory>
 #include <regex>
@@ -123,12 +124,12 @@
   return std::string(&temp[0], len);
 }
 
-void SetProperty(const char* key, const std::string& val) {
-  property_set(key, val.c_str());
+bool SetProperty(const char* key, const std::string& val) {
+  return property_set(key, val.c_str()) == 0;
 }
 
-void SetProperty(const char* key, const char* val) {
-  property_set(key, val);
+bool SetProperty(const char* key, const char* val) {
+  return property_set(key, val) == 0;
 }
 
 constexpr int32_t kEmptyBootReason = 0;
@@ -735,8 +736,49 @@
 const char system_reboot_reason_property[] = "sys.boot.reason";
 const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
 const char last_last_reboot_reason_property[] = "sys.boot.reason.last";
+constexpr size_t history_reboot_reason_size = 4;
+const char history_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY ".history";
 const char bootloader_reboot_reason_property[] = "ro.boot.bootreason";
 
+// Land system_boot_reason into system_reboot_reason_property.
+// Shift system_boot_reason into history_reboot_reason_property.
+void BootReasonAddToHistory(const std::string& system_boot_reason) {
+  if (system_boot_reason.empty()) return;
+  LOG(INFO) << "Canonical boot reason: " << system_boot_reason;
+  auto old_system_boot_reason = GetProperty(system_reboot_reason_property);
+  if (!SetProperty(system_reboot_reason_property, system_boot_reason)) {
+    SetProperty(system_reboot_reason_property, system_boot_reason.substr(0, PROPERTY_VALUE_MAX - 1));
+  }
+  auto reason_history = android::base::Split(GetProperty(history_reboot_reason_property), "\n");
+  static auto mark = time(nullptr);
+  auto mark_str = std::string(",") + std::to_string(mark);
+  auto marked_system_boot_reason = system_boot_reason + mark_str;
+  if (!reason_history.empty()) {
+    // delete any entries that we just wrote in a previous
+    // call and leveraging duplicate line handling
+    auto last = old_system_boot_reason + mark_str;
+    // trim the list to (history_reboot_reason_size - 1)
+    ssize_t max = history_reboot_reason_size;
+    for (auto it = reason_history.begin(); it != reason_history.end();) {
+      if (it->empty() || (last == *it) || (marked_system_boot_reason == *it) || (--max <= 0)) {
+        it = reason_history.erase(it);
+      } else {
+        last = *it;
+        ++it;
+      }
+    }
+  }
+  // insert at the front, concatenating mark (<epoch time>) detail to the value.
+  reason_history.insert(reason_history.begin(), marked_system_boot_reason);
+  // If the property string is too long ( > PROPERTY_VALUE_MAX)
+  // we get an error, so trim out last entry and try again.
+  while (!(SetProperty(history_reboot_reason_property, android::base::Join(reason_history, '\n')))) {
+    auto it = std::prev(reason_history.end());
+    if (it == reason_history.end()) break;
+    reason_history.erase(it);
+  }
+}
+
 // Scrub, Sanitize, Standardize and Enhance the boot reason string supplied.
 std::string BootReasonStrToReason(const std::string& boot_reason) {
   std::string ret(GetProperty(system_reboot_reason_property));
@@ -795,6 +837,8 @@
         {"!warm", "wdt_by_pass_pwk"},  // change flavour of blunt
         {"!reboot", "^wdt$"},          // change flavour of blunt
         {"reboot,tool", "tool_by_pass_pwk"},
+        {"!reboot,longkey", "reboot_longkey"},
+        {"!reboot,longkey", "kpdpwr"},
         {"bootloader", ""},
     };
 
@@ -930,13 +974,11 @@
   if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) {
     boot_complete_prefix = "factory_reset_" + boot_complete_prefix;
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
-    LOG(INFO) << "Canonical boot reason: reboot,factory_reset";
-    SetProperty(system_reboot_reason_property, "reboot,factory_reset");
+    BootReasonAddToHistory("reboot,factory_reset");
   } else if (build_date != record.second) {
     boot_complete_prefix = "ota_" + boot_complete_prefix;
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
-    LOG(INFO) << "Canonical boot reason: reboot,ota";
-    SetProperty(system_reboot_reason_property, "reboot,ota");
+    BootReasonAddToHistory("reboot,ota");
   }
 
   return boot_complete_prefix;
@@ -1034,7 +1076,7 @@
   const std::string bootloader_boot_reason(GetProperty(bootloader_reboot_reason_property));
   const std::string system_boot_reason(BootReasonStrToReason(bootloader_boot_reason));
   // Record the scrubbed system_boot_reason to the property
-  SetProperty(system_reboot_reason_property, system_boot_reason);
+  BootReasonAddToHistory(system_boot_reason);
   // Shift last_reboot_reason_property to last_last_reboot_reason_property
   std::string last_boot_reason(GetProperty(last_reboot_reason_property));
   if (last_boot_reason.empty() || isKernelRebootReason(system_boot_reason)) {
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index f31337d..f0fe1d0 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -183,6 +183,8 @@
     fprintf(stderr, "  exit                  call exit(1)\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "  fortify               fail a _FORTIFY_SOURCE check\n");
+    fprintf(stderr, "  fdsan_file            close a file descriptor that's owned by a FILE*\n");
+    fprintf(stderr, "  fdsan_dir             close a file descriptor that's owned by a DIR*\n");
     fprintf(stderr, "  seccomp               fail a seccomp check\n");
 #if defined(__arm__)
     fprintf(stderr, "  kuser_helper_version  call kuser_helper_version\n");
@@ -236,39 +238,45 @@
 
     // Actions.
     if (!strcasecmp(arg, "SIGSEGV-non-null")) {
-        sigsegv_non_null();
+      sigsegv_non_null();
     } else if (!strcasecmp(arg, "smash-stack")) {
-        volatile int len = 128;
-        return smash_stack(&len);
+      volatile int len = 128;
+      return smash_stack(&len);
     } else if (!strcasecmp(arg, "stack-overflow")) {
-        overflow_stack(nullptr);
+      overflow_stack(nullptr);
     } else if (!strcasecmp(arg, "nostack")) {
-        crashnostack();
+      crashnostack();
     } else if (!strcasecmp(arg, "exit")) {
-        exit(1);
+      exit(1);
     } else if (!strcasecmp(arg, "call-null")) {
       return crash_null();
     } else if (!strcasecmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
-        return crash(42);
+      return crash(42);
     } else if (!strcasecmp(arg, "abort")) {
-        maybe_abort();
+      maybe_abort();
     } else if (!strcasecmp(arg, "assert")) {
-        __assert("some_file.c", 123, "false");
+      __assert("some_file.c", 123, "false");
     } else if (!strcasecmp(arg, "assert2")) {
-        __assert2("some_file.c", 123, "some_function", "false");
+      __assert2("some_file.c", 123, "some_function", "false");
     } else if (!strcasecmp(arg, "fortify")) {
-        char buf[10];
-        __read_chk(-1, buf, 32, 10);
-        while (true) pause();
+      char buf[10];
+      __read_chk(-1, buf, 32, 10);
+      while (true) pause();
+    } else if (!strcasecmp(arg, "fdsan_file")) {
+      FILE* f = fopen("/dev/null", "r");
+      close(fileno(f));
+    } else if (!strcasecmp(arg, "fdsan_dir")) {
+      DIR* d = opendir("/dev/");
+      close(dirfd(d));
     } else if (!strcasecmp(arg, "LOG(FATAL)")) {
-        LOG(FATAL) << "hello " << 123;
+      LOG(FATAL) << "hello " << 123;
     } else if (!strcasecmp(arg, "LOG_ALWAYS_FATAL")) {
-        LOG_ALWAYS_FATAL("hello %s", "world");
+      LOG_ALWAYS_FATAL("hello %s", "world");
     } else if (!strcasecmp(arg, "LOG_ALWAYS_FATAL_IF")) {
-        LOG_ALWAYS_FATAL_IF(true, "hello %s", "world");
+      LOG_ALWAYS_FATAL_IF(true, "hello %s", "world");
     } else if (!strcasecmp(arg, "SIGFPE")) {
-        raise(SIGFPE);
-        return EXIT_SUCCESS;
+      raise(SIGFPE);
+      return EXIT_SUCCESS;
     } else if (!strcasecmp(arg, "SIGILL")) {
 #if defined(__aarch64__)
       __asm__ volatile(".word 0\n");
@@ -280,28 +288,28 @@
 #error
 #endif
     } else if (!strcasecmp(arg, "SIGTRAP")) {
-        raise(SIGTRAP);
-        return EXIT_SUCCESS;
+      raise(SIGTRAP);
+      return EXIT_SUCCESS;
     } else if (!strcasecmp(arg, "fprintf-NULL")) {
-        fprintf_null();
+      fprintf_null();
     } else if (!strcasecmp(arg, "readdir-NULL")) {
-        readdir_null();
+      readdir_null();
     } else if (!strcasecmp(arg, "strlen-NULL")) {
-        return strlen_null();
+      return strlen_null();
     } else if (!strcasecmp(arg, "pthread_join-NULL")) {
-        return pthread_join(0, nullptr);
+      return pthread_join(0, nullptr);
     } else if (!strcasecmp(arg, "heap-usage")) {
-        abuse_heap();
+      abuse_heap();
     } else if (!strcasecmp(arg, "leak")) {
-        leak();
+      leak();
     } else if (!strcasecmp(arg, "SIGSEGV-unmapped")) {
-        char* map = reinterpret_cast<char*>(mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE,
-                                                 MAP_SHARED | MAP_ANONYMOUS, -1, 0));
-        munmap(map, sizeof(int));
-        map[0] = '8';
+      char* map = reinterpret_cast<char*>(
+          mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0));
+      munmap(map, sizeof(int));
+      map[0] = '8';
     } else if (!strcasecmp(arg, "seccomp")) {
-        set_system_seccomp_filter();
-        syscall(99999);
+      set_system_seccomp_filter();
+      syscall(99999);
 #if defined(__arm__)
     } else if (!strcasecmp(arg, "kuser_helper_version")) {
         return __kuser_helper_version;
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index c07a34a..615fb46 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -59,7 +59,16 @@
 #include "protocol.h"
 
 using android::base::Pipe;
-using android::base::unique_fd;
+
+// We muck with our fds in a 'thread' that doesn't share the same fd table.
+// Close fds in that thread with a raw close syscall instead of going through libc.
+struct FdsanBypassCloser {
+  static void Close(int fd) {
+    syscall(__NR_close, fd);
+  }
+};
+
+using unique_fd = android::base::unique_fd_impl<FdsanBypassCloser>;
 
 // see man(2) prctl, specifically the section about PR_GET_NAME
 #define MAX_TASK_NAME_LEN (16)
@@ -299,7 +308,8 @@
   debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
 
   for (int i = 0; i < 1024; ++i) {
-    close(i);
+    // Don't use close to avoid bionic's file descriptor ownership checks.
+    syscall(__NR_close, i);
   }
 
   int devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 433bb46..0b8a936 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -474,7 +474,7 @@
 
 static void dump_log_file(log_t* log, pid_t pid, const char* filename, unsigned int tail) {
   bool first = true;
-  struct logger_list* logger_list;
+  logger_list* logger_list;
 
   if (!log->should_retrieve_logcat) {
     return;
@@ -488,11 +488,9 @@
     return;
   }
 
-  struct log_msg log_entry;
-
   while (true) {
+    log_msg log_entry;
     ssize_t actual = android_logger_list_read(logger_list, &log_entry);
-    struct logger_entry* entry;
 
     if (actual < 0) {
       if (actual == -EINTR) {
@@ -515,8 +513,6 @@
     // high-frequency debug diagnostics should just be written to
     // the tombstone file.
 
-    entry = &log_entry.entry_v1;
-
     if (first) {
       _LOG(log, logtype::LOGS, "--------- %slog %s\n",
         tail ? "tail end of " : "", filename);
@@ -527,19 +523,8 @@
     //
     // We want to display it in the same format as "logcat -v threadtime"
     // (although in this case the pid is redundant).
-    static const char* kPrioChars = "!.VDIWEFS";
-    unsigned hdr_size = log_entry.entry.hdr_size;
-    if (!hdr_size) {
-      hdr_size = sizeof(log_entry.entry_v1);
-    }
-    if ((hdr_size < sizeof(log_entry.entry_v1)) ||
-        (hdr_size > sizeof(log_entry.entry))) {
-      continue;
-    }
-    char* msg = reinterpret_cast<char*>(log_entry.buf) + hdr_size;
-
     char timeBuf[32];
-    time_t sec = static_cast<time_t>(entry->sec);
+    time_t sec = static_cast<time_t>(log_entry.entry.sec);
     struct tm tmBuf;
     struct tm* ptm;
     ptm = localtime_r(&sec, &tmBuf);
@@ -547,17 +532,23 @@
 
     if (log_entry.id() == LOG_ID_EVENTS) {
       if (!g_eventTagMap) {
-        g_eventTagMap = android_openEventTagMap(NULL);
+        g_eventTagMap = android_openEventTagMap(nullptr);
       }
       AndroidLogEntry e;
       char buf[512];
-      android_log_processBinaryLogBuffer(entry, &e, g_eventTagMap, buf, sizeof(buf));
-      _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8.*s: %s\n",
-         timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
-         'I', (int)e.tagLen, e.tag, e.message);
+      if (android_log_processBinaryLogBuffer(&log_entry.entry_v1, &e, g_eventTagMap, buf,
+                                             sizeof(buf)) == 0) {
+        _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8.*s: %s\n", timeBuf,
+             log_entry.entry.nsec / 1000000, log_entry.entry.pid, log_entry.entry.tid, 'I',
+             (int)e.tagLen, e.tag, e.message);
+      }
       continue;
     }
 
+    char* msg = log_entry.msg();
+    if (msg == nullptr) {
+      continue;
+    }
     unsigned char prio = msg[0];
     char* tag = msg + 1;
     msg = tag + strlen(tag) + 1;
@@ -568,20 +559,21 @@
       *nl-- = '\0';
     }
 
+    static const char* kPrioChars = "!.VDIWEFS";
     char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?');
 
     // Look for line breaks ('\n') and display each text line
     // on a separate line, prefixed with the header, like logcat does.
     do {
       nl = strchr(msg, '\n');
-      if (nl) {
+      if (nl != nullptr) {
         *nl = '\0';
         ++nl;
       }
 
-      _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n",
-         timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
-         prioChar, tag, msg);
+      _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n", timeBuf,
+           log_entry.entry.nsec / 1000000, log_entry.entry.pid, log_entry.entry.tid, prioChar, tag,
+           msg);
     } while ((msg = nl));
   }
 
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 1f6f3c8..8bdc02f 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -287,9 +287,7 @@
     case SIGFPE: return "SIGFPE";
     case SIGILL: return "SIGILL";
     case SIGSEGV: return "SIGSEGV";
-#if defined(SIGSTKFLT)
     case SIGSTKFLT: return "SIGSTKFLT";
-#endif
     case SIGSTOP: return "SIGSTOP";
     case SIGSYS: return "SIGSYS";
     case SIGTRAP: return "SIGTRAP";
@@ -311,8 +309,14 @@
         case ILL_PRVREG: return "ILL_PRVREG";
         case ILL_COPROC: return "ILL_COPROC";
         case ILL_BADSTK: return "ILL_BADSTK";
+        case ILL_BADIADDR:
+          return "ILL_BADIADDR";
+        case __ILL_BREAK:
+          return "ILL_BREAK";
+        case __ILL_BNDMOD:
+          return "ILL_BNDMOD";
       }
-      static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code");
+      static_assert(NSIGILL == __ILL_BNDMOD, "missing ILL_* si_code");
       break;
     case SIGBUS:
       switch (si->si_code) {
@@ -334,36 +338,44 @@
         case FPE_FLTRES: return "FPE_FLTRES";
         case FPE_FLTINV: return "FPE_FLTINV";
         case FPE_FLTSUB: return "FPE_FLTSUB";
+        case __FPE_DECOVF:
+          return "FPE_DECOVF";
+        case __FPE_DECDIV:
+          return "FPE_DECDIV";
+        case __FPE_DECERR:
+          return "FPE_DECERR";
+        case __FPE_INVASC:
+          return "FPE_INVASC";
+        case __FPE_INVDEC:
+          return "FPE_INVDEC";
+        case FPE_FLTUNK:
+          return "FPE_FLTUNK";
+        case FPE_CONDTRAP:
+          return "FPE_CONDTRAP";
       }
-      static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code");
+      static_assert(NSIGFPE == FPE_CONDTRAP, "missing FPE_* si_code");
       break;
     case SIGSEGV:
       switch (si->si_code) {
         case SEGV_MAPERR: return "SEGV_MAPERR";
         case SEGV_ACCERR: return "SEGV_ACCERR";
-#if defined(SEGV_BNDERR)
         case SEGV_BNDERR: return "SEGV_BNDERR";
-#endif
-#if defined(SEGV_PKUERR)
         case SEGV_PKUERR: return "SEGV_PKUERR";
-#endif
+        case SEGV_ACCADI:
+          return "SEGV_ACCADI";
+        case SEGV_ADIDERR:
+          return "SEGV_ADIDERR";
+        case SEGV_ADIPERR:
+          return "SEGV_ADIPERR";
       }
-#if defined(SEGV_PKUERR)
-      static_assert(NSIGSEGV == SEGV_PKUERR, "missing SEGV_* si_code");
-#elif defined(SEGV_BNDERR)
-      static_assert(NSIGSEGV == SEGV_BNDERR, "missing SEGV_* si_code");
-#else
-      static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code");
-#endif
+      static_assert(NSIGSEGV == SEGV_ADIPERR, "missing SEGV_* si_code");
       break;
-#if defined(SYS_SECCOMP) // Our glibc is too old, and we build this for the host too.
     case SIGSYS:
       switch (si->si_code) {
         case SYS_SECCOMP: return "SYS_SECCOMP";
       }
       static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code");
       break;
-#endif
     case SIGTRAP:
       switch (si->si_code) {
         case TRAP_BRKPT: return "TRAP_BRKPT";
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
new file mode 100644
index 0000000..2b4a954
--- /dev/null
+++ b/fastboot/Android.bp
@@ -0,0 +1,56 @@
+cc_library_host_static {
+    name: "libfastboot2",
+
+    //host_supported: true,
+
+    compile_multilib: "first",
+    srcs: [
+      "bootimg_utils.cpp",
+      "fs.cpp",
+      "socket.cpp",
+      "tcp.cpp",
+      "udp.cpp",
+      "util.cpp",
+      "fastboot_driver.cpp",
+    ],
+
+    static_libs: [
+      "libziparchive",
+      "libsparse",
+      "libutils",
+      "liblog",
+      "libz",
+      "libdiagnose_usb",
+      "libbase",
+      "libcutils",
+      "libgtest",
+      "libgtest_main",
+      "libbase",
+      "libadb_host"
+    ],
+
+    header_libs: [
+      "bootimg_headers"
+    ],
+
+    export_header_lib_headers: [
+      "bootimg_headers"
+    ],
+
+
+    target: {
+      linux: {
+        srcs: ["usb_linux.cpp"],
+      },
+    },
+
+    cflags: [
+      "-Wall",
+      "-Wextra",
+      "-Werror",
+      "-Wunreachable-code",
+    ],
+
+    export_include_dirs: ["."],
+
+}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 983e195..60c338c 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -50,12 +50,12 @@
     bootimg_utils.cpp \
     engine.cpp \
     fastboot.cpp \
-    fs.cpp\
-    protocol.cpp \
+    fs.cpp \
     socket.cpp \
     tcp.cpp \
     udp.cpp \
     util.cpp \
+    fastboot_driver.cpp \
 
 LOCAL_SRC_FILES_darwin := usb_osx.cpp
 LOCAL_SRC_FILES_linux := usb_linux.cpp
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
index 2d12d50..2088ae3 100644
--- a/fastboot/OWNERS
+++ b/fastboot/OWNERS
@@ -1,3 +1,4 @@
 dpursell@google.com
 enh@google.com
 jmgao@google.com
+tomcherry@google.com
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index c9814e4..1152007 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -28,7 +28,7 @@
 
 #include "bootimg_utils.h"
 
-#include "fastboot.h"
+#include "util.h"
 
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/fastboot/constants.h b/fastboot/constants.h
new file mode 100644
index 0000000..5e7e955
--- /dev/null
+++ b/fastboot/constants.h
@@ -0,0 +1,51 @@
+/*
+ * 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
+
+#define FB_CMD_GETVAR "getvar"
+#define FB_CMD_DOWNLOAD "download"
+#define FB_CMD_UPLOAD "upload"
+#define FB_CMD_VERIFY "verify"
+#define FB_CMD_FLASH "flash"
+#define FB_CMD_ERASE "erase"
+#define FB_CMD_BOOT "boot"
+#define FB_CMD_SET_ACTIVE "set_active"
+#define FB_CMD_CONTINUE "continue"
+#define FB_CMD_REBOOT "reboot"
+#define FB_CMD_SHUTDOWN "shutdown"
+#define FB_CMD_REBOOT_BOOTLOADER "reboot-bootloader"
+#define FB_CMD_POWERDOWN "powerdown"
+
+#define RESPONSE_OKAY "OKAY"
+#define RESPONSE_FAIL "FAIL"
+#define RESPONSE_DATA "DATA"
+#define RESPONSE_INFO "INFO"
+
+#define FB_COMMAND_SZ 64
+#define FB_RESPONSE_SZ 64
+
+#define FB_VAR_VERSION "version"
+#define FB_VAR_VERSION_BOOTLOADER "version-bootloader"
+#define FB_VAR_VERSION_BASEBAND "version-baseband"
+#define FB_VAR_PRODUCT "product"
+#define FB_VAR_SERIALNO "serialno"
+#define FB_VAR_SECURE "secure"
+#define FB_VAR_UNLOCKED "unlocked"
+#define FB_VAR_CURRENT_SLOT "current-slot"
+#define FB_VAR_MAX_DOWNLOAD_SIZE "max-download-size"
+#define FB_VAR_HAS_SLOT "has-slot"
+#define FB_VAR_SLOT_COUNT "slot-count"
+#define FB_VAR_PARTITION_SIZE "partition-size"
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index f271d09..63ee2af 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -25,8 +25,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-
-#include "fastboot.h"
+#include "engine.h"
 
 #include <errno.h>
 #include <stdarg.h>
@@ -42,6 +41,7 @@
 
 #include <android-base/stringprintf.h>
 
+#include "constants.h"
 #include "transport.h"
 
 enum Op {
@@ -77,17 +77,20 @@
 };
 
 static std::vector<std::unique_ptr<Action>> action_list;
+static fastboot::FastBootDriver* fb = nullptr;
 
-bool fb_getvar(Transport* transport, const std::string& key, std::string* value) {
-    std::string cmd = "getvar:" + key;
+void fb_init(fastboot::FastBootDriver& fbi) {
+    fb = &fbi;
+    auto cb = [](std::string& info) { fprintf(stderr, "(bootloader) %s\n", info.c_str()); };
+    fb->SetInfoCallback(cb);
+}
 
-    char buf[FB_RESPONSE_SZ + 1];
-    memset(buf, 0, sizeof(buf));
-    if (fb_command_response(transport, cmd, buf)) {
-        return false;
-    }
-    *value = buf;
-    return true;
+const std::string fb_get_error() {
+    return fb->Error();
+}
+
+bool fb_getvar(const std::string& key, std::string* value) {
+    return !fb->GetVar(key, value);
 }
 
 static int cb_default(Action& a, int status, const char* resp) {
@@ -110,12 +113,12 @@
 }
 
 void fb_set_active(const std::string& slot) {
-    Action& a = queue_action(OP_COMMAND, "set_active:" + slot);
+    Action& a = queue_action(OP_COMMAND, FB_CMD_SET_ACTIVE ":" + slot);
     a.msg = "Setting current slot to '" + slot + "'";
 }
 
 void fb_queue_erase(const std::string& partition) {
-    Action& a = queue_action(OP_COMMAND, "erase:" + partition);
+    Action& a = queue_action(OP_COMMAND, FB_CMD_ERASE ":" + partition);
     a.msg = "Erasing '" + partition + "'";
 }
 
@@ -125,7 +128,7 @@
     a.size = sz;
     a.msg = android::base::StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024);
 
-    Action& b = queue_action(OP_COMMAND, "flash:" + partition);
+    Action& b = queue_action(OP_COMMAND, FB_CMD_FLASH ":" + partition);
     b.msg = "Writing '" + partition + "'";
 }
 
@@ -135,7 +138,7 @@
     a.size = sz;
     a.msg = android::base::StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024);
 
-    Action& b = queue_action(OP_COMMAND, "flash:" + partition);
+    Action& b = queue_action(OP_COMMAND, FB_CMD_FLASH ":" + partition);
     b.msg = "Writing '" + partition + "'";
 }
 
@@ -147,7 +150,7 @@
     a.msg = android::base::StringPrintf("Sending sparse '%s' %zu/%zu (%u KB)", partition.c_str(),
                                         current, total, sz / 1024);
 
-    Action& b = queue_action(OP_COMMAND, "flash:" + partition);
+    Action& b = queue_action(OP_COMMAND, FB_CMD_FLASH ":" + partition);
     b.msg = android::base::StringPrintf("Writing sparse '%s' %zu/%zu", partition.c_str(), current,
                                         total);
 }
@@ -223,7 +226,7 @@
 
 void fb_queue_require(const std::string& product, const std::string& var, bool invert,
                       size_t nvalues, const char** values) {
-    Action& a = queue_action(OP_QUERY, "getvar:" + var);
+    Action& a = queue_action(OP_QUERY, FB_CMD_GETVAR ":" + var);
     a.product = product;
     a.data = values;
     a.size = nvalues;
@@ -243,7 +246,7 @@
 }
 
 void fb_queue_display(const std::string& label, const std::string& var) {
-    Action& a = queue_action(OP_QUERY, "getvar:" + var);
+    Action& a = queue_action(OP_QUERY, FB_CMD_GETVAR ":" + var);
     a.data = xstrdup(label.c_str());
     a.func = cb_display;
 }
@@ -258,7 +261,7 @@
 }
 
 void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size) {
-    Action& a = queue_action(OP_QUERY, "getvar:" + var);
+    Action& a = queue_action(OP_QUERY, FB_CMD_GETVAR ":" + var);
     a.data = dest;
     a.size = dest_size;
     a.func = cb_save;
@@ -270,7 +273,7 @@
 }
 
 void fb_queue_reboot() {
-    Action& a = queue_action(OP_COMMAND, "reboot");
+    Action& a = queue_action(OP_COMMAND, FB_CMD_REBOOT);
     a.func = cb_do_nothing;
     a.msg = "Rebooting";
 }
@@ -309,7 +312,7 @@
     queue_action(OP_WAIT_FOR_DISCONNECT, "");
 }
 
-int64_t fb_execute_queue(Transport* transport) {
+int64_t fb_execute_queue() {
     int64_t status = 0;
     for (auto& a : action_list) {
         a->start = now();
@@ -318,33 +321,34 @@
             verbose("\n");
         }
         if (a->op == OP_DOWNLOAD) {
-            status = fb_download_data(transport, a->data, a->size);
+            char* cbuf = static_cast<char*>(a->data);
+            status = fb->Download(cbuf, a->size);
             status = a->func(*a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_DOWNLOAD_FD) {
-            status = fb_download_data_fd(transport, a->fd, a->size);
+            status = fb->Download(a->fd, a->size);
             status = a->func(*a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_COMMAND) {
-            status = fb_command(transport, a->cmd);
+            status = fb->RawCommand(a->cmd);
             status = a->func(*a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_QUERY) {
-            char resp[FB_RESPONSE_SZ + 1] = {};
-            status = fb_command_response(transport, a->cmd, resp);
-            status = a->func(*a, status, status ? fb_get_error().c_str() : resp);
+            std::string resp;
+            status = fb->RawCommand(a->cmd, &resp);
+            status = a->func(*a, status, status ? fb_get_error().c_str() : resp.c_str());
             if (status) break;
         } else if (a->op == OP_NOTICE) {
             // We already showed the notice because it's in `Action::msg`.
             fprintf(stderr, "\n");
         } else if (a->op == OP_DOWNLOAD_SPARSE) {
-            status = fb_download_data_sparse(transport, reinterpret_cast<sparse_file*>(a->data));
+            status = fb->Download(reinterpret_cast<sparse_file*>(a->data));
             status = a->func(*a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
-            transport->WaitForDisconnect();
+            fb->WaitForDisconnect();
         } else if (a->op == OP_UPLOAD) {
-            status = fb_upload_data(transport, reinterpret_cast<char*>(a->data));
+            status = fb->Upload(reinterpret_cast<const char*>(a->data));
             status = a->func(*a, status, status ? fb_get_error().c_str() : "");
         } else {
             die("unknown action: %d", a->op);
diff --git a/fastboot/fastboot.h b/fastboot/engine.h
similarity index 66%
rename from fastboot/fastboot.h
rename to fastboot/engine.h
index 2935eb5..74aaa6a 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/engine.h
@@ -34,24 +34,24 @@
 #include <string>
 
 #include <bootimg.h>
+#include "fastboot_driver.h"
+#include "util.h"
+
+#include "constants.h"
 
 class Transport;
 struct sparse_file;
 
-/* protocol.c - fastboot protocol */
-int fb_command(Transport* transport, const std::string& cmd);
-int fb_command_response(Transport* transport, const std::string& cmd, char* response);
-int64_t fb_download_data(Transport* transport, const void* data, uint32_t size);
-int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size);
-int fb_download_data_sparse(Transport* transport, struct sparse_file* s);
-int64_t fb_upload_data(Transport* transport, const char* outfile);
 const std::string fb_get_error();
 
-#define FB_COMMAND_SZ 64
-#define FB_RESPONSE_SZ 64
+//#define FB_COMMAND_SZ (fastboot::FB_COMMAND_SZ)
+//#define FB_RESPONSE_SZ (fastboot::FB_RESPONSE_SZ)
 
 /* engine.c - high level command queue engine */
-bool fb_getvar(Transport* transport, const std::string& key, std::string* value);
+
+void fb_init(fastboot::FastBootDriver& fbi);
+
+bool fb_getvar(const std::string& key, std::string* value);
 void fb_queue_flash(const std::string& partition, void* data, uint32_t sz);
 void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz);
 void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
@@ -69,35 +69,13 @@
 void fb_queue_upload(const std::string& outfile);
 void fb_queue_notice(const std::string& notice);
 void fb_queue_wait_for_disconnect(void);
-int64_t fb_execute_queue(Transport* transport);
+int64_t fb_execute_queue();
 void fb_set_active(const std::string& slot);
 
-/* util stuff */
-double now();
-char* xstrdup(const char*);
-void set_verbose();
-
-// These printf-like functions are implemented in terms of vsnprintf, so they
-// use the same attribute for compile-time format string checking. On Windows,
-// if the mingw version of vsnprintf is used, use `gnu_printf' which allows z
-// in %zd and PRIu64 (and related) to be recognized by the compile-time
-// checking.
-#define FASTBOOT_FORMAT_ARCHETYPE __printf__
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-#undef FASTBOOT_FORMAT_ARCHETYPE
-#define FASTBOOT_FORMAT_ARCHETYPE gnu_printf
-#endif
-#endif
-void die(const char* fmt, ...) __attribute__((__noreturn__))
-__attribute__((__format__(FASTBOOT_FORMAT_ARCHETYPE, 1, 2)));
-void verbose(const char* fmt, ...) __attribute__((__format__(FASTBOOT_FORMAT_ARCHETYPE, 1, 2)));
-#undef FASTBOOT_FORMAT_ARCHETYPE
-
 /* Current product */
 extern char cur_product[FB_RESPONSE_SZ + 1];
 
-class FastBoot {
+class FastBootTool {
   public:
     int Main(int argc, char* argv[]);
 
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
index dca3b4e..cb1d354 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -16,11 +16,11 @@
 #
 
 _fastboot() {
-    if ! type -t "$1" >/dev/null; then
+    if ! check_type "$1" >/dev/null; then
         return
     fi
 
-    if type -t _init_completion >/dev/null; then
+    if check_type _init_completion >/dev/null; then
         _init_completion || return
     fi
 
@@ -135,7 +135,7 @@
     xspec=$2
 
     # Since we're probably doing file completion here, don't add a space after.
-    if [[ $(type -t compopt) = "builtin" ]]; then
+    if [[ $(check_type compopt) == "builtin" ]]; then
         compopt -o plusdirs
         if [[ "${xspec}" == "" ]]; then
             COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -f -- "${cur}") )
@@ -175,7 +175,7 @@
     fi
 }
 
-if [[ $(type -t compopt) = "builtin" ]]; then
+if [[ $(check_type compopt) == "builtin" ]]; then
     complete -F _fastboot fastboot
 else
     complete -o nospace -F _fastboot fastboot
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 5aa87d9..263ea17 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -60,7 +60,7 @@
 
 #include "bootimg_utils.h"
 #include "diagnose_usb.h"
-#include "fastboot.h"
+#include "engine.h"
 #include "fs.h"
 #include "tcp.h"
 #include "transport.h"
@@ -592,7 +592,7 @@
 }
 
 #define MAX_OPTIONS 32
-static void check_requirement(Transport* transport, char* line) {
+static void check_requirement(char* line) {
     char *val[MAX_OPTIONS];
     unsigned count;
     char *x;
@@ -635,7 +635,7 @@
     if (!strcmp(name, "partition-exists")) {
         const char* partition_name = val[0];
         std::string has_slot;
-        if (!fb_getvar(transport, std::string("has-slot:") + partition_name, &has_slot) ||
+        if (!fb_getvar(std::string("has-slot:") + partition_name, &has_slot) ||
             (has_slot != "yes" && has_slot != "no")) {
             die("device doesn't have required partition %s!", partition_name);
         }
@@ -674,18 +674,18 @@
     fb_queue_require(product, var, invert, count, out);
 }
 
-static void check_requirements(Transport* transport, char* data, int64_t sz) {
+static void check_requirements(char* data, int64_t sz) {
     char* s = data;
     while (sz-- > 0) {
         if (*s == '\n') {
             *s++ = 0;
-            check_requirement(transport, data);
+            check_requirement(data);
             data = s;
         } else {
             s++;
         }
     }
-    if (fb_execute_queue(transport)) die("requirements not met!");
+    if (fb_execute_queue()) die("requirements not met!");
 }
 
 static void queue_info_dump() {
@@ -716,9 +716,9 @@
     return out_s;
 }
 
-static int64_t get_target_sparse_limit(Transport* transport) {
+static int64_t get_target_sparse_limit() {
     std::string max_download_size;
-    if (!fb_getvar(transport, "max-download-size", &max_download_size) ||
+    if (!fb_getvar("max-download-size", &max_download_size) ||
         max_download_size.empty()) {
         verbose("target didn't report max-download-size");
         return 0;
@@ -736,13 +736,13 @@
     return limit;
 }
 
-static int64_t get_sparse_limit(Transport* transport, int64_t size) {
+static int64_t get_sparse_limit(int64_t size) {
     int64_t limit = sparse_limit;
     if (limit == 0) {
         // Unlimited, so see what the target device's limit is.
         // TODO: shouldn't we apply this limit even if you've used -S?
         if (target_sparse_limit == -1) {
-            target_sparse_limit = get_target_sparse_limit(transport);
+            target_sparse_limit = get_target_sparse_limit();
         }
         if (target_sparse_limit > 0) {
             limit = target_sparse_limit;
@@ -758,14 +758,14 @@
     return 0;
 }
 
-static bool load_buf_fd(Transport* transport, int fd, struct fastboot_buffer* buf) {
+static bool load_buf_fd(int fd, struct fastboot_buffer* buf) {
     int64_t sz = get_file_size(fd);
     if (sz == -1) {
         return false;
     }
 
     lseek64(fd, 0, SEEK_SET);
-    int64_t limit = get_sparse_limit(transport, sz);
+    int64_t limit = get_sparse_limit(sz);
     if (limit) {
         sparse_file** s = load_sparse_files(fd, limit);
         if (s == nullptr) {
@@ -783,7 +783,7 @@
     return true;
 }
 
-static bool load_buf(Transport* transport, const char* fname, struct fastboot_buffer* buf) {
+static bool load_buf(const char* fname, struct fastboot_buffer* buf) {
     unique_fd fd(TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_BINARY)));
 
     if (fd == -1) {
@@ -799,7 +799,7 @@
         return false;
     }
 
-    return load_buf_fd(transport, fd.release(), buf);
+    return load_buf_fd(fd.release(), buf);
 }
 
 static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf) {
@@ -871,23 +871,23 @@
     }
 }
 
-static std::string get_current_slot(Transport* transport) {
+static std::string get_current_slot() {
     std::string current_slot;
-    if (!fb_getvar(transport, "current-slot", &current_slot)) return "";
+    if (!fb_getvar("current-slot", &current_slot)) return "";
     return current_slot;
 }
 
-static int get_slot_count(Transport* transport) {
+static int get_slot_count() {
     std::string var;
     int count = 0;
-    if (!fb_getvar(transport, "slot-count", &var) || !android::base::ParseInt(var, &count)) {
+    if (!fb_getvar("slot-count", &var) || !android::base::ParseInt(var, &count)) {
         return 0;
     }
     return count;
 }
 
-static bool supports_AB(Transport* transport) {
-  return get_slot_count(transport) >= 2;
+static bool supports_AB() {
+  return get_slot_count() >= 2;
 }
 
 // Given a current slot, this returns what the 'other' slot is.
@@ -898,25 +898,25 @@
     return std::string(1, next);
 }
 
-static std::string get_other_slot(Transport* transport, const std::string& current_slot) {
-    return get_other_slot(current_slot, get_slot_count(transport));
+static std::string get_other_slot(const std::string& current_slot) {
+    return get_other_slot(current_slot, get_slot_count());
 }
 
-static std::string get_other_slot(Transport* transport, int count) {
-    return get_other_slot(get_current_slot(transport), count);
+static std::string get_other_slot(int count) {
+    return get_other_slot(get_current_slot(), count);
 }
 
-static std::string get_other_slot(Transport* transport) {
-    return get_other_slot(get_current_slot(transport), get_slot_count(transport));
+static std::string get_other_slot() {
+    return get_other_slot(get_current_slot(), get_slot_count());
 }
 
-static std::string verify_slot(Transport* transport, const std::string& slot_name, bool allow_all) {
+static std::string verify_slot(const std::string& slot_name, bool allow_all) {
     std::string slot = slot_name;
     if (slot == "all") {
         if (allow_all) {
             return "all";
         } else {
-            int count = get_slot_count(transport);
+            int count = get_slot_count();
             if (count > 0) {
                 return "a";
             } else {
@@ -925,11 +925,11 @@
         }
     }
 
-    int count = get_slot_count(transport);
+    int count = get_slot_count();
     if (count == 0) die("Device does not support slots");
 
     if (slot == "other") {
-        std::string other = get_other_slot(transport, count);
+        std::string other = get_other_slot( count);
         if (other == "") {
            die("No known slots");
         }
@@ -946,22 +946,22 @@
     exit(1);
 }
 
-static std::string verify_slot(Transport* transport, const std::string& slot) {
-   return verify_slot(transport, slot, true);
+static std::string verify_slot(const std::string& slot) {
+   return verify_slot(slot, true);
 }
 
-static void do_for_partition(Transport* transport, const std::string& part, const std::string& slot,
+static void do_for_partition(const std::string& part, const std::string& slot,
                              const std::function<void(const std::string&)>& func, bool force_slot) {
     std::string has_slot;
     std::string current_slot;
 
-    if (!fb_getvar(transport, "has-slot:" + part, &has_slot)) {
+    if (!fb_getvar("has-slot:" + part, &has_slot)) {
         /* If has-slot is not supported, the answer is no. */
         has_slot = "no";
     }
     if (has_slot == "yes") {
         if (slot == "") {
-            current_slot = get_current_slot(transport);
+            current_slot = get_current_slot();
             if (current_slot == "") {
                 die("Failed to identify current slot");
             }
@@ -983,30 +983,30 @@
  * partition names. If force_slot is true, it will fail if a slot is specified, and the given
  * partition does not support slots.
  */
-static void do_for_partitions(Transport* transport, const std::string& part, const std::string& slot,
+static void do_for_partitions(const std::string& part, const std::string& slot,
                               const std::function<void(const std::string&)>& func, bool force_slot) {
     std::string has_slot;
 
     if (slot == "all") {
-        if (!fb_getvar(transport, "has-slot:" + part, &has_slot)) {
+        if (!fb_getvar("has-slot:" + part, &has_slot)) {
             die("Could not check if partition %s has slot %s", part.c_str(), slot.c_str());
         }
         if (has_slot == "yes") {
-            for (int i=0; i < get_slot_count(transport); i++) {
-                do_for_partition(transport, part, std::string(1, (char)(i + 'a')), func, force_slot);
+            for (int i=0; i < get_slot_count(); i++) {
+                do_for_partition(part, std::string(1, (char)(i + 'a')), func, force_slot);
             }
         } else {
-            do_for_partition(transport, part, "", func, force_slot);
+            do_for_partition(part, "", func, force_slot);
         }
     } else {
-        do_for_partition(transport, part, slot, func, force_slot);
+        do_for_partition(part, slot, func, force_slot);
     }
 }
 
-static void do_flash(Transport* transport, const char* pname, const char* fname) {
+static void do_flash(const char* pname, const char* fname) {
     struct fastboot_buffer buf;
 
-    if (!load_buf(transport, fname, &buf)) {
+    if (!load_buf(fname, &buf)) {
         die("cannot load '%s': %s", fname, strerror(errno));
     }
     flash_buf(pname, &buf);
@@ -1022,20 +1022,20 @@
 
 // Sets slot_override as the active slot. If slot_override is blank,
 // set current slot as active instead. This clears slot-unbootable.
-static void set_active(Transport* transport, const std::string& slot_override) {
-    if (!supports_AB(transport)) return;
+static void set_active(const std::string& slot_override) {
+    if (!supports_AB()) return;
 
     if (slot_override != "") {
         fb_set_active(slot_override);
     } else {
-        std::string current_slot = get_current_slot(transport);
+        std::string current_slot = get_current_slot();
         if (current_slot != "") {
             fb_set_active(current_slot);
         }
     }
 }
 
-static void do_update(Transport* transport, const char* filename, const std::string& slot_override, bool skip_secondary) {
+static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
     queue_info_dump();
 
     fb_queue_query_save("product", cur_product, sizeof(cur_product));
@@ -1052,17 +1052,17 @@
         die("update package '%s' has no android-info.txt", filename);
     }
 
-    check_requirements(transport, reinterpret_cast<char*>(data), sz);
+    check_requirements(reinterpret_cast<char*>(data), sz);
 
     std::string secondary;
     if (!skip_secondary) {
         if (slot_override != "") {
-            secondary = get_other_slot(transport, slot_override);
+            secondary = get_other_slot(slot_override);
         } else {
-            secondary = get_other_slot(transport);
+            secondary = get_other_slot();
         }
         if (secondary == "") {
-            if (supports_AB(transport)) {
+            if (supports_AB()) {
                 fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
             }
             skip_secondary = true;
@@ -1087,7 +1087,7 @@
         }
 
         fastboot_buffer buf;
-        if (!load_buf_fd(transport, fd, &buf)) {
+        if (!load_buf_fd(fd, &buf)) {
             die("cannot load %s from flash: %s", images[i].img_name, strerror(errno));
         }
 
@@ -1099,13 +1099,13 @@
              * program exits.
              */
         };
-        do_for_partitions(transport, images[i].part_name, slot, update, false);
+        do_for_partitions(images[i].part_name, slot, update, false);
     }
 
     if (slot_override == "all") {
-        set_active(transport, "a");
+        set_active("a");
     } else {
-        set_active(transport, slot_override);
+        set_active(slot_override);
     }
 
     CloseArchive(zip);
@@ -1125,7 +1125,7 @@
     fb_queue_command("signature", "installing signature");
 }
 
-static void do_flashall(Transport* transport, const std::string& slot_override, bool skip_secondary) {
+static void do_flashall(const std::string& slot_override, bool skip_secondary) {
     std::string fname;
     queue_info_dump();
 
@@ -1138,17 +1138,17 @@
     void* data = load_file(fname.c_str(), &sz);
     if (data == nullptr) die("could not load android-info.txt: %s", strerror(errno));
 
-    check_requirements(transport, reinterpret_cast<char*>(data), sz);
+    check_requirements(reinterpret_cast<char*>(data), sz);
 
     std::string secondary;
     if (!skip_secondary) {
         if (slot_override != "") {
-            secondary = get_other_slot(transport, slot_override);
+            secondary = get_other_slot(slot_override);
         } else {
-            secondary = get_other_slot(transport);
+            secondary = get_other_slot();
         }
         if (secondary == "") {
-            if (supports_AB(transport)) {
+            if (supports_AB()) {
                 fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
             }
             skip_secondary = true;
@@ -1165,7 +1165,7 @@
         if (!slot) continue;
         fname = find_item_given_name(images[i].img_name);
         fastboot_buffer buf;
-        if (!load_buf(transport, fname.c_str(), &buf)) {
+        if (!load_buf(fname.c_str(), &buf)) {
             if (images[i].is_optional) continue;
             die("could not load '%s': %s", images[i].img_name, strerror(errno));
         }
@@ -1174,13 +1174,13 @@
             do_send_signature(fname.c_str());
             flash_buf(partition.c_str(), &buf);
         };
-        do_for_partitions(transport, images[i].part_name, slot, flashall, false);
+        do_for_partitions(images[i].part_name, slot, flashall, false);
     }
 
     if (slot_override == "all") {
-        set_active(transport, "a");
+        set_active("a");
     } else {
-        set_active(transport, slot_override);
+        set_active(slot_override);
     }
 }
 
@@ -1210,9 +1210,9 @@
     return var;
 }
 
-static unsigned fb_get_flash_block_size(Transport* transport, std::string name) {
+static unsigned fb_get_flash_block_size(std::string name) {
     std::string sizeString;
-    if (!fb_getvar(transport, name, &sizeString) || sizeString.empty()) {
+    if (!fb_getvar(name, &sizeString) || sizeString.empty()) {
         // This device does not report flash block sizes, so return 0.
         return 0;
     }
@@ -1230,7 +1230,7 @@
     return size;
 }
 
-static void fb_perform_format(Transport* transport,
+static void fb_perform_format(
                               const std::string& partition, int skip_if_not_supported,
                               const std::string& type_override, const std::string& size_override,
                               const std::string& initial_dir) {
@@ -1250,7 +1250,7 @@
         limit = sparse_limit;
     }
 
-    if (!fb_getvar(transport, "partition-type:" + partition, &partition_type)) {
+    if (!fb_getvar("partition-type:" + partition, &partition_type)) {
         errMsg = "Can't determine partition type.\n";
         goto failed;
     }
@@ -1262,7 +1262,7 @@
         partition_type = type_override;
     }
 
-    if (!fb_getvar(transport, "partition-size:" + partition, &partition_size)) {
+    if (!fb_getvar("partition-size:" + partition, &partition_size)) {
         errMsg = "Unable to get partition size\n";
         goto failed;
     }
@@ -1294,8 +1294,8 @@
     }
 
     unsigned eraseBlkSize, logicalBlkSize;
-    eraseBlkSize = fb_get_flash_block_size(transport, "erase-block-size");
-    logicalBlkSize = fb_get_flash_block_size(transport, "logical-block-size");
+    eraseBlkSize = fb_get_flash_block_size("erase-block-size");
+    logicalBlkSize = fb_get_flash_block_size("logical-block-size");
 
     if (fs_generator_generate(gen, output.path, size, initial_dir,
             eraseBlkSize, logicalBlkSize)) {
@@ -1308,7 +1308,7 @@
         fprintf(stderr, "Cannot open generated image: %s\n", strerror(errno));
         return;
     }
-    if (!load_buf_fd(transport, fd.release(), &buf)) {
+    if (!load_buf_fd(fd.release(), &buf)) {
         fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
         return;
     }
@@ -1323,7 +1323,7 @@
     fprintf(stderr, "FAILED (%s)\n", fb_get_error().c_str());
 }
 
-int FastBoot::Main(int argc, char* argv[]) {
+int FastBootTool::Main(int argc, char* argv[]) {
     bool wants_wipe = false;
     bool wants_reboot = false;
     bool wants_reboot_bootloader = false;
@@ -1470,23 +1470,25 @@
     if (transport == nullptr) {
         return 1;
     }
+    fastboot::FastBootDriver fb(transport);
+    fb_init(fb);
 
     const double start = now();
 
-    if (slot_override != "") slot_override = verify_slot(transport, slot_override);
-    if (next_active != "") next_active = verify_slot(transport, next_active, false);
+    if (slot_override != "") slot_override = verify_slot(slot_override);
+    if (next_active != "") next_active = verify_slot(next_active, false);
 
     if (wants_set_active) {
         if (next_active == "") {
             if (slot_override == "") {
                 std::string current_slot;
-                if (fb_getvar(transport, "current-slot", &current_slot)) {
-                    next_active = verify_slot(transport, current_slot, false);
+                if (fb_getvar("current-slot", &current_slot)) {
+                    next_active = verify_slot(current_slot, false);
                 } else {
                     wants_set_active = false;
                 }
             } else {
-                next_active = verify_slot(transport, slot_override, false);
+                next_active = verify_slot(slot_override, false);
             }
         }
     }
@@ -1502,7 +1504,7 @@
             std::string partition = next_arg(&args);
             auto erase = [&](const std::string& partition) {
                 std::string partition_type;
-                if (fb_getvar(transport, std::string("partition-type:") + partition,
+                if (fb_getvar(std::string("partition-type:") + partition,
                               &partition_type) &&
                     fs_get_generator(partition_type) != nullptr) {
                     fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
@@ -1511,7 +1513,7 @@
 
                 fb_queue_erase(partition);
             };
-            do_for_partitions(transport, partition, slot_override, erase, true);
+            do_for_partitions(partition, slot_override, erase, true);
         } else if (android::base::StartsWith(command, "format")) {
             // Parsing for: "format[:[type][:[size]]]"
             // Some valid things:
@@ -1529,9 +1531,9 @@
             std::string partition = next_arg(&args);
 
             auto format = [&](const std::string& partition) {
-                fb_perform_format(transport, partition, 0, type_override, size_override, "");
+                fb_perform_format(partition, 0, type_override, size_override, "");
             };
-            do_for_partitions(transport, partition.c_str(), slot_override, format, true);
+            do_for_partitions(partition.c_str(), slot_override, format, true);
         } else if (command == "signature") {
             std::string filename = next_arg(&args);
             data = load_file(filename.c_str(), &sz);
@@ -1579,9 +1581,9 @@
             if (fname.empty()) die("cannot determine image filename for '%s'", pname.c_str());
 
             auto flash = [&](const std::string &partition) {
-                do_flash(transport, partition.c_str(), fname.c_str());
+                do_flash(partition.c_str(), fname.c_str());
             };
-            do_for_partitions(transport, pname.c_str(), slot_override, flash, true);
+            do_for_partitions(pname.c_str(), slot_override, flash, true);
         } else if (command == "flash:raw") {
             std::string partition = next_arg(&args);
             std::string kernel = next_arg(&args);
@@ -1594,13 +1596,13 @@
             auto flashraw = [&](const std::string& partition) {
                 fb_queue_flash(partition, data, sz);
             };
-            do_for_partitions(transport, partition, slot_override, flashraw, true);
+            do_for_partitions(partition, slot_override, flashraw, true);
         } else if (command == "flashall") {
             if (slot_override == "all") {
                 fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
-                do_flashall(transport, slot_override, true);
+                do_flashall(slot_override, true);
             } else {
-                do_flashall(transport, slot_override, skip_secondary);
+                do_flashall(slot_override, skip_secondary);
             }
             wants_reboot = true;
         } else if (command == "update") {
@@ -1612,16 +1614,16 @@
             if (!args.empty()) {
                 filename = next_arg(&args);
             }
-            do_update(transport, filename.c_str(), slot_override, skip_secondary || slot_all);
+            do_update(filename.c_str(), slot_override, skip_secondary || slot_all);
             wants_reboot = true;
         } else if (command == "set_active") {
-            std::string slot = verify_slot(transport, next_arg(&args), false);
+            std::string slot = verify_slot(next_arg(&args), false);
             fb_set_active(slot);
         } else if (command == "stage") {
             std::string filename = next_arg(&args);
 
             struct fastboot_buffer buf;
-            if (!load_buf(transport, filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
+            if (!load_buf(filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
                 die("cannot load '%s'", filename.c_str());
             }
             fb_queue_download_fd(filename, buf.fd, buf.sz);
@@ -1650,16 +1652,16 @@
         std::vector<std::string> partitions = { "userdata", "cache", "metadata" };
         for (const auto& partition : partitions) {
             std::string partition_type;
-            if (!fb_getvar(transport, std::string{"partition-type:"} + partition, &partition_type)) continue;
+            if (!fb_getvar(std::string{"partition-type:"} + partition, &partition_type)) continue;
             if (partition_type.empty()) continue;
             fb_queue_erase(partition);
             if (partition == "userdata" && set_fbe_marker) {
                 fprintf(stderr, "setting FBE marker on initial userdata...\n");
                 std::string initial_userdata_dir = create_fbemarker_tmpdir();
-                fb_perform_format(transport, partition, 1, "", "", initial_userdata_dir);
+                fb_perform_format(partition, 1, "", "", initial_userdata_dir);
                 delete_fbemarker_tmpdir(initial_userdata_dir);
             } else {
-                fb_perform_format(transport, partition, 1, "", "", "");
+                fb_perform_format(partition, 1, "", "", "");
             }
         }
     }
@@ -1674,12 +1676,12 @@
         fb_queue_wait_for_disconnect();
     }
 
-    int status = fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
+    int status = fb_execute_queue() ? EXIT_FAILURE : EXIT_SUCCESS;
     fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
     return status;
 }
 
-void FastBoot::ParseOsPatchLevel(boot_img_hdr_v1* hdr, const char* arg) {
+void FastBootTool::ParseOsPatchLevel(boot_img_hdr_v1* hdr, const char* arg) {
     unsigned year, month, day;
     if (sscanf(arg, "%u-%u-%u", &year, &month, &day) != 3) {
         syntax_error("OS patch level should be YYYY-MM-DD: %s", arg);
@@ -1689,7 +1691,7 @@
     hdr->SetOsPatchLevel(year, month);
 }
 
-void FastBoot::ParseOsVersion(boot_img_hdr_v1* hdr, const char* arg) {
+void FastBootTool::ParseOsVersion(boot_img_hdr_v1* hdr, const char* arg) {
     unsigned major = 0, minor = 0, patch = 0;
     std::vector<std::string> versions = android::base::Split(arg, ".");
     if (versions.size() < 1 || versions.size() > 3 ||
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
new file mode 100644
index 0000000..c308420
--- /dev/null
+++ b/fastboot/fastboot_driver.cpp
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "fastboot_driver.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <algorithm>
+#include <chrono>
+#include <fstream>
+#include <memory>
+#include <regex>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <utils/FileMap.h>
+#include "fastboot_driver.h"
+#include "transport.h"
+
+namespace fastboot {
+
+/*************************** PUBLIC *******************************/
+FastBootDriver::FastBootDriver(Transport* transport, std::function<void(std::string&)> info,
+                               bool no_checks)
+    : transport(transport) {
+    info_cb_ = info;
+    disable_checks_ = no_checks;
+}
+
+RetCode FastBootDriver::Boot(std::string* response, std::vector<std::string>* info) {
+    return RawCommand(Commands::BOOT, response, info);
+}
+
+RetCode FastBootDriver::Continue(std::string* response, std::vector<std::string>* info) {
+    return RawCommand(Commands::CONTINUE, response, info);
+}
+
+RetCode FastBootDriver::Erase(const std::string& part, std::string* response,
+                              std::vector<std::string>* info) {
+    return RawCommand(Commands::ERASE + part, response, info);
+}
+
+RetCode FastBootDriver::Flash(const std::string& part, std::string* response,
+                              std::vector<std::string>* info) {
+    return RawCommand(Commands::FLASH + part, response, info);
+}
+
+RetCode FastBootDriver::GetVar(const std::string& key, std::string* val,
+                               std::vector<std::string>* info) {
+    return RawCommand(Commands::GET_VAR + key, val, info);
+}
+
+RetCode FastBootDriver::GetVarAll(std::vector<std::string>* response) {
+    std::string tmp;
+    return GetVar("all", &tmp, response);
+}
+
+RetCode FastBootDriver::Powerdown(std::string* response, std::vector<std::string>* info) {
+    return RawCommand(Commands::POWERDOWN, response, info);
+}
+
+RetCode FastBootDriver::Reboot(std::string* response, std::vector<std::string>* info) {
+    return RawCommand(Commands::REBOOT, response, info);
+}
+
+RetCode FastBootDriver::SetActive(const std::string& part, std::string* response,
+                                  std::vector<std::string>* info) {
+    return RawCommand(Commands::SET_ACTIVE + part, response, info);
+}
+
+RetCode FastBootDriver::Verify(uint32_t num, std::string* response, std::vector<std::string>* info) {
+    std::string cmd = android::base::StringPrintf("%s%08" PRIx32, Commands::VERIFY.c_str(), num);
+    return RawCommand(cmd, response, info);
+}
+
+RetCode FastBootDriver::FlashPartition(const std::string& part, const std::vector<char>& data) {
+    RetCode ret;
+    if ((ret = Download(data))) {
+        return ret;
+    }
+    return RawCommand(Commands::FLASH + part);
+}
+
+RetCode FastBootDriver::FlashPartition(const std::string& part, int fd, uint32_t sz) {
+    RetCode ret;
+    if ((ret = Download(fd, sz))) {
+        return ret;
+    }
+    return RawCommand(Commands::FLASH + part);
+}
+
+RetCode FastBootDriver::FlashPartition(const std::string& part, sparse_file* s) {
+    RetCode ret;
+    if ((ret = Download(s))) {
+        return ret;
+    }
+    return RawCommand(Commands::FLASH + part);
+}
+
+RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint32_t>>* parts) {
+    std::vector<std::string> all;
+    RetCode ret;
+    if ((ret = GetVarAll(&all))) {
+        return ret;
+    }
+
+    std::regex reg("partition-size[[:s:]]*:[[:s:]]*([[:w:]]+)[[:s:]]*:[[:s:]]*0x([[:d:]]+)");
+    std::smatch sm;
+
+    for (auto& s : all) {
+        if (std::regex_match(s, sm, reg)) {
+            std::string m1(sm[1]);
+            std::string m2(sm[2]);
+            uint32_t tmp = strtol(m2.c_str(), 0, 16);
+            parts->push_back(std::make_tuple(m1, tmp));
+        }
+    }
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::Require(const std::string& var, const std::vector<std::string>& allowed,
+                                bool* reqmet, bool invert) {
+    *reqmet = invert;
+    RetCode ret;
+    std::string response;
+    if ((ret = GetVar(var, &response))) {
+        return ret;
+    }
+
+    // Now check if we have a match
+    for (const auto s : allowed) {
+        // If it ends in *, and starting substring match
+        if (response == s || (s.length() && s.back() == '*' &&
+                              !response.compare(0, s.length() - 1, s, 0, s.length() - 1))) {
+            *reqmet = !invert;
+            break;
+        }
+    }
+
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::Download(int fd, size_t size, std::string* response,
+                                 std::vector<std::string>* info) {
+    RetCode ret;
+
+    if ((size <= 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
+        error_ = "File is too large to download";
+        return BAD_ARG;
+    }
+
+    uint32_t u32size = static_cast<uint32_t>(size);
+    if ((ret = DownloadCommand(u32size, response, info))) {
+        return ret;
+    }
+
+    // Write the buffer
+    if ((ret = SendBuffer(fd, size))) {
+        return ret;
+    }
+
+    // Wait for response
+    return HandleResponse(response, info);
+}
+
+RetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,
+                                 std::vector<std::string>* info) {
+    return Download(buf.data(), buf.size(), response, info);
+}
+
+RetCode FastBootDriver::Download(const char* buf, uint32_t size, std::string* response,
+                                 std::vector<std::string>* info) {
+    RetCode ret;
+    error_ = "";
+    if ((size == 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
+        error_ = "Buffer is too large or 0 bytes";
+        return BAD_ARG;
+    }
+
+    if ((ret = DownloadCommand(size, response, info))) {
+        return ret;
+    }
+
+    // Write the buffer
+    if ((ret = SendBuffer(buf, size))) {
+        return ret;
+    }
+
+    // Wait for response
+    return HandleResponse(response, info);
+}
+
+RetCode FastBootDriver::Download(sparse_file* s, std::string* response,
+                                 std::vector<std::string>* info) {
+    error_ = "";
+    int64_t size = sparse_file_len(s, true, false);
+    if (size <= 0 || size > MAX_DOWNLOAD_SIZE) {
+        error_ = "Sparse file is too large or invalid";
+        return BAD_ARG;
+    }
+
+    RetCode ret;
+    uint32_t u32size = static_cast<uint32_t>(size);
+    if ((ret = DownloadCommand(u32size, response, info))) {
+        return ret;
+    }
+
+    struct SparseCBPrivate {
+        FastBootDriver* self;
+        std::vector<char> tpbuf;
+    } cb_priv;
+    cb_priv.self = this;
+
+    auto cb = [](void* priv, const void* buf, size_t len) -> int {
+        SparseCBPrivate* data = static_cast<SparseCBPrivate*>(priv);
+        const char* cbuf = static_cast<const char*>(buf);
+        return data->self->SparseWriteCallback(data->tpbuf, cbuf, len);
+    };
+
+    if (sparse_file_callback(s, true, false, cb, &cb_priv) < 0) {
+        error_ = "Error reading sparse file";
+        return IO_ERROR;
+    }
+
+    // Now flush
+    if (cb_priv.tpbuf.size() && (ret = SendBuffer(cb_priv.tpbuf))) {
+        return ret;
+    }
+
+    return HandleResponse(response, info);
+}
+
+RetCode FastBootDriver::Upload(const std::string& outfile, std::string* response,
+                               std::vector<std::string>* info) {
+    RetCode ret;
+    int dsize;
+    if ((ret = RawCommand(Commands::UPLOAD, response, info, &dsize)) || dsize == 0) {
+        error_ = "Upload request failed";
+        return ret;
+    }
+
+    std::vector<char> data;
+    data.resize(dsize);
+
+    if ((ret = ReadBuffer(data))) {
+        return ret;
+    }
+
+    std::ofstream ofs;
+    ofs.open(outfile, std::ofstream::out | std::ofstream::binary);
+    if (ofs.fail()) {
+        error_ = android::base::StringPrintf("Failed to open '%s'", outfile.c_str());
+        return IO_ERROR;
+    }
+    ofs.write(data.data(), data.size());
+    if (ofs.fail() || ofs.bad()) {
+        error_ = android::base::StringPrintf("Writing to '%s' failed", outfile.c_str());
+        return IO_ERROR;
+    }
+    ofs.close();
+
+    return HandleResponse(response, info);
+}
+
+// Helpers
+void FastBootDriver::SetInfoCallback(std::function<void(std::string&)> info) {
+    info_cb_ = info;
+}
+
+const std::string FastBootDriver::RCString(RetCode rc) {
+    switch (rc) {
+        case SUCCESS:
+            return std::string("Success");
+
+        case BAD_ARG:
+            return std::string("Invalid Argument");
+
+        case IO_ERROR:
+            return std::string("I/O Error");
+
+        case BAD_DEV_RESP:
+            return std::string("Invalid Device Response");
+
+        case DEVICE_FAIL:
+            return std::string("Device Error");
+
+        case TIMEOUT:
+            return std::string("Timeout");
+
+        default:
+            return std::string("Unknown Error");
+    }
+}
+
+std::string FastBootDriver::Error() {
+    return error_;
+}
+
+RetCode FastBootDriver::WaitForDisconnect() {
+    return transport->WaitForDisconnect() ? IO_ERROR : SUCCESS;
+}
+
+/****************************** PROTECTED *************************************/
+RetCode FastBootDriver::RawCommand(const std::string& cmd, std::string* response,
+                                   std::vector<std::string>* info, int* dsize) {
+    error_ = "";  // Clear any pending error
+    if (cmd.size() > FB_COMMAND_SZ && !disable_checks_) {
+        error_ = "Command length to RawCommand() is too long";
+        return BAD_ARG;
+    }
+
+    if (transport->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
+        error_ = ErrnoStr("Write to device failed");
+        return IO_ERROR;
+    }
+
+    // Read the response
+    return HandleResponse(response, info, dsize);
+}
+
+RetCode FastBootDriver::DownloadCommand(uint32_t size, std::string* response,
+                                        std::vector<std::string>* info) {
+    std::string cmd(android::base::StringPrintf("%s%08" PRIx32, Commands::DOWNLOAD.c_str(), size));
+    RetCode ret;
+    if ((ret = RawCommand(cmd, response, info))) {
+        return ret;
+    }
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::HandleResponse(std::string* response, std::vector<std::string>* info,
+                                       int* dsize) {
+    char status[FB_RESPONSE_SZ + 1];
+    auto start = std::chrono::system_clock::now();
+
+    auto set_response = [response](std::string s) {
+        if (response) *response = std::move(s);
+    };
+    auto add_info = [info](std::string s) {
+        if (info) info->push_back(std::move(s));
+    };
+
+    // erase response
+    set_response("");
+    while ((std::chrono::system_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
+        int r = transport->Read(status, FB_RESPONSE_SZ);
+        if (r < 0) {
+            error_ = ErrnoStr("Status read failed");
+            return IO_ERROR;
+        }
+
+        status[r] = '\0';  // Need the null terminator
+        std::string input(status);
+        if (android::base::StartsWith(input, "INFO")) {
+            std::string tmp = input.substr(strlen("INFO"));
+            info_cb_(tmp);
+            add_info(std::move(tmp));
+        } else if (android::base::StartsWith(input, "OKAY")) {
+            set_response(input.substr(strlen("OKAY")));
+            return SUCCESS;
+        } else if (android::base::StartsWith(input, "FAIL")) {
+            error_ = android::base::StringPrintf("remote: '%s'", status + strlen("FAIL"));
+            set_response(input.substr(strlen("FAIL")));
+            return DEVICE_FAIL;
+        } else if (android::base::StartsWith(input, "DATA")) {
+            std::string tmp = input.substr(strlen("DATA"));
+            uint32_t num = strtol(tmp.c_str(), 0, 16);
+            if (num > MAX_DOWNLOAD_SIZE) {
+                error_ = android::base::StringPrintf("Data size too large (%d)", num);
+                return BAD_DEV_RESP;
+            }
+            if (dsize) *dsize = num;
+            set_response(std::move(tmp));
+            return SUCCESS;
+        } else {
+            error_ = android::base::StringPrintf("Device sent unknown status code: %s", status);
+            return BAD_DEV_RESP;
+        }
+
+    }  // End of while loop
+
+    return TIMEOUT;
+}
+
+std::string FastBootDriver::ErrnoStr(const std::string& msg) {
+    return android::base::StringPrintf("%s (%s)", msg.c_str(), strerror(errno));
+}
+
+const std::string FastBootDriver::Commands::BOOT = "boot";
+const std::string FastBootDriver::Commands::CONTINUE = "continue";
+const std::string FastBootDriver::Commands::DOWNLOAD = "download:";
+const std::string FastBootDriver::Commands::ERASE = "erase:";
+const std::string FastBootDriver::Commands::FLASH = "flash:";
+const std::string FastBootDriver::Commands::GET_VAR = "getvar:";
+const std::string FastBootDriver::Commands::POWERDOWN = "powerdown";
+const std::string FastBootDriver::Commands::REBOOT = "reboot";
+const std::string FastBootDriver::Commands::SET_ACTIVE = "set_active:";
+const std::string FastBootDriver::Commands::UPLOAD = "upload";
+const std::string FastBootDriver::Commands::VERIFY = "verify:";
+
+/******************************* PRIVATE **************************************/
+RetCode FastBootDriver::SendBuffer(int fd, size_t size) {
+    static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
+    off64_t offset = 0;
+    uint32_t remaining = size;
+    RetCode ret;
+
+    while (remaining) {
+        // Memory map the file
+        android::FileMap filemap;
+        size_t len = std::min(remaining, MAX_MAP_SIZE);
+
+        if (!filemap.create(NULL, fd, offset, len, true)) {
+            error_ = "Creating filemap failed";
+            return IO_ERROR;
+        }
+
+        if ((ret = SendBuffer(filemap.getDataPtr(), len))) {
+            return ret;
+        }
+
+        remaining -= len;
+        offset += len;
+    }
+
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::SendBuffer(const std::vector<char>& buf) {
+    // Write the buffer
+    return SendBuffer(buf.data(), buf.size());
+}
+
+RetCode FastBootDriver::SendBuffer(const void* buf, size_t size) {
+    // Write the buffer
+    ssize_t tmp = transport->Write(buf, size);
+
+    if (tmp < 0) {
+        error_ = ErrnoStr("Write to device failed in SendBuffer()");
+        return IO_ERROR;
+    } else if (static_cast<size_t>(tmp) != size) {
+        error_ = android::base::StringPrintf("Failed to write all %zu bytes", size);
+
+        return IO_ERROR;
+    }
+
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::ReadBuffer(std::vector<char>& buf) {
+    // Read the buffer
+    return ReadBuffer(buf.data(), buf.size());
+}
+
+RetCode FastBootDriver::ReadBuffer(void* buf, size_t size) {
+    // Read the buffer
+    ssize_t tmp = transport->Read(buf, size);
+
+    if (tmp < 0) {
+        error_ = ErrnoStr("Read from device failed in ReadBuffer()");
+        return IO_ERROR;
+    } else if (static_cast<size_t>(tmp) != size) {
+        error_ = android::base::StringPrintf("Failed to read all %zu bytes", size);
+        return IO_ERROR;
+    }
+
+    return SUCCESS;
+}
+
+int FastBootDriver::SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len) {
+    size_t total = 0;
+    size_t to_write = std::min(TRANSPORT_CHUNK_SIZE - tpbuf.size(), len);
+
+    // Handle the residual
+    tpbuf.insert(tpbuf.end(), data, data + to_write);
+    if (tpbuf.size() < TRANSPORT_CHUNK_SIZE) {  // Nothing enough to send rn
+        return 0;
+    }
+
+    if (SendBuffer(tpbuf)) {
+        error_ = ErrnoStr("Send failed in SparseWriteCallback()");
+        return -1;
+    }
+    tpbuf.clear();
+    total += to_write;
+
+    // Now we need to send a multiple of chunk size
+    size_t nchunks = (len - total) / TRANSPORT_CHUNK_SIZE;
+    size_t nbytes = TRANSPORT_CHUNK_SIZE * nchunks;
+    if (SendBuffer(data + total, nbytes)) {
+        error_ = ErrnoStr("Send failed in SparseWriteCallback()");
+        return -1;
+    }
+    total += nbytes;
+
+    if (len - total > 0) {  // We have residual data to save for next time
+        tpbuf.assign(data + total, data + len);
+    }
+
+    return 0;
+}
+
+}  // End namespace fastboot
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
new file mode 100644
index 0000000..9fdd317
--- /dev/null
+++ b/fastboot/fastboot_driver.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#pragma once
+#include <cstdlib>
+#include <deque>
+#include <limits>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <bootimg.h>
+#include <inttypes.h>
+#include <sparse/sparse.h>
+#include "transport.h"
+
+class Transport;
+
+namespace fastboot {
+
+static constexpr int FB_COMMAND_SZ = 64;
+static constexpr int FB_RESPONSE_SZ = 64;
+
+enum RetCode : int {
+    SUCCESS = 0,
+    BAD_ARG,
+    IO_ERROR,
+    BAD_DEV_RESP,
+    DEVICE_FAIL,
+    TIMEOUT,
+};
+
+class FastBootDriver {
+  public:
+    static constexpr int RESP_TIMEOUT = 10;  // 10 seconds
+    static constexpr uint32_t MAX_DOWNLOAD_SIZE = std::numeric_limits<uint32_t>::max();
+    static constexpr size_t TRANSPORT_CHUNK_SIZE = 1024;
+
+    FastBootDriver(Transport* transport,
+                   std::function<void(std::string&)> info = [](std::string&) {},
+                   bool no_checks = false);
+
+    RetCode Boot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode Continue(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode Download(int fd, size_t size, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    RetCode Download(const std::vector<char>& buf, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    // This will be removed after fastboot is modified to use a vector
+    RetCode Download(const char* buf, uint32_t size, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    RetCode Download(sparse_file* s, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    RetCode Erase(const std::string& part, std::string* response = nullptr,
+                  std::vector<std::string>* info = nullptr);
+    RetCode Flash(const std::string& part, std::string* response = nullptr,
+                  std::vector<std::string>* info = nullptr);
+    RetCode GetVar(const std::string& key, std::string* val,
+                   std::vector<std::string>* info = nullptr);
+    RetCode GetVarAll(std::vector<std::string>* response);
+    RetCode Powerdown(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode Reboot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode SetActive(const std::string& part, std::string* response = nullptr,
+                      std::vector<std::string>* info = nullptr);
+    RetCode Upload(const std::string& outfile, std::string* response = nullptr,
+                   std::vector<std::string>* info = nullptr);
+    RetCode Verify(uint32_t num, std::string* response = nullptr,
+                   std::vector<std::string>* info = nullptr);
+
+    /* HIGHER LEVEL COMMANDS -- Composed of the commands above */
+    RetCode FlashPartition(const std::string& part, const std::vector<char>& data);
+    RetCode FlashPartition(const std::string& part, int fd, uint32_t sz);
+    RetCode FlashPartition(const std::string& part, sparse_file* s);
+
+    RetCode Partitions(std::vector<std::tuple<std::string, uint32_t>>* parts);
+    RetCode Require(const std::string& var, const std::vector<std::string>& allowed, bool* reqmet,
+                    bool invert = false);
+
+    /* HELPERS */
+    void SetInfoCallback(std::function<void(std::string&)> info);
+    const std::string RCString(RetCode rc);
+    std::string Error();
+    RetCode WaitForDisconnect();
+
+    // This is temporarily public for engine.cpp
+    RetCode RawCommand(const std::string& cmd, std::string* response = nullptr,
+                       std::vector<std::string>* info = nullptr, int* dsize = nullptr);
+
+  protected:
+    RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,
+                            std::vector<std::string>* info = nullptr);
+    RetCode HandleResponse(std::string* response = nullptr,
+                           std::vector<std::string>* info = nullptr, int* dsize = nullptr);
+
+    std::string ErrnoStr(const std::string& msg);
+
+    // More like a namespace...
+    struct Commands {
+        static const std::string BOOT;
+        static const std::string CONTINUE;
+        static const std::string DOWNLOAD;
+        static const std::string ERASE;
+        static const std::string FLASH;
+        static const std::string GET_VAR;
+        static const std::string POWERDOWN;
+        static const std::string REBOOT;
+        static const std::string SET_ACTIVE;
+        static const std::string UPLOAD;
+        static const std::string VERIFY;
+    };
+
+    Transport* const transport;
+
+  private:
+    RetCode SendBuffer(int fd, size_t size);
+    RetCode SendBuffer(const std::vector<char>& buf);
+    RetCode SendBuffer(const void* buf, size_t size);
+
+    RetCode ReadBuffer(std::vector<char>& buf);
+    RetCode ReadBuffer(void* buf, size_t size);
+
+    int SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len);
+
+    std::string error_;
+    std::function<void(std::string&)> info_cb_;
+    bool disable_checks_;
+};
+
+}  // namespace fastboot
diff --git a/fastboot/fastboot_test.cpp b/fastboot/fastboot_test.cpp
index 1681427..43201fa 100644
--- a/fastboot/fastboot_test.cpp
+++ b/fastboot/fastboot_test.cpp
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#include "fastboot.h"
+#include "engine.h"
 
 #include <gtest/gtest.h>
 
 TEST(FastBoot, ParseOsPatchLevel) {
-    FastBoot fb;
+    FastBootTool fb;
     boot_img_hdr_v1 hdr;
 
     hdr = {};
@@ -34,7 +34,7 @@
 }
 
 TEST(FastBoot, ParseOsVersion) {
-    FastBoot fb;
+    FastBootTool fb;
     boot_img_hdr_v1 hdr;
 
     hdr = {};
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 14dabaa..cc1714f 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -1,6 +1,5 @@
 #include "fs.h"
 
-#include "fastboot.h"
 
 #include <errno.h>
 #include <fcntl.h>
diff --git a/fastboot/main.cpp b/fastboot/main.cpp
index f1c8afb..c3683f7 100644
--- a/fastboot/main.cpp
+++ b/fastboot/main.cpp
@@ -26,9 +26,9 @@
  * SUCH DAMAGE.
  */
 
-#include "fastboot.h"
+#include "engine.h"
 
 int main(int argc, char* argv[]) {
-    FastBoot fb;
+    FastBootTool fb;
     return fb.Main(argc, argv);
 }
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
deleted file mode 100644
index fda6f5d..0000000
--- a/fastboot/protocol.cpp
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#define round_down(a, b) \
-    ({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); })
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <algorithm>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <sparse/sparse.h>
-#include <utils/FileMap.h>
-
-#include "fastboot.h"
-#include "transport.h"
-
-static std::string g_error;
-
-using android::base::unique_fd;
-using android::base::WriteStringToFile;
-
-const std::string fb_get_error() {
-    return g_error;
-}
-
-static int64_t check_response(Transport* transport, uint32_t size, char* response) {
-    char status[FB_RESPONSE_SZ + 1];
-
-    while (true) {
-        int r = transport->Read(status, FB_RESPONSE_SZ);
-        if (r < 0) {
-            g_error = android::base::StringPrintf("status read failed (%s)", strerror(errno));
-            transport->Close();
-            return -1;
-        }
-        status[r] = 0;
-
-        if (r < 4) {
-            g_error = android::base::StringPrintf("status malformed (%d bytes)", r);
-            transport->Close();
-            return -1;
-        }
-
-        if (!memcmp(status, "INFO", 4)) {
-            verbose("received INFO \"%s\"", status + 4);
-            fprintf(stderr, "(bootloader) %s\n", status + 4);
-            continue;
-        }
-
-        if (!memcmp(status, "OKAY", 4)) {
-            verbose("received OKAY \"%s\"", status + 4);
-            if (response) {
-                strcpy(response, status + 4);
-            }
-            return 0;
-        }
-
-        if (!memcmp(status, "FAIL", 4)) {
-            verbose("received FAIL \"%s\"", status + 4);
-            if (r > 4) {
-                g_error = android::base::StringPrintf("remote: %s", status + 4);
-            } else {
-                g_error = "remote failure";
-            }
-            return -1;
-        }
-
-        if (!memcmp(status, "DATA", 4) && size > 0){
-            verbose("received DATA %s", status + 4);
-            uint32_t dsize = strtol(status + 4, 0, 16);
-            if (dsize > size) {
-                g_error = android::base::StringPrintf("data size too large (%d)", dsize);
-                transport->Close();
-                return -1;
-            }
-            return dsize;
-        }
-
-        verbose("received unknown status code \"%4.4s\"", status);
-        g_error = "unknown status code";
-        transport->Close();
-        break;
-    }
-
-    return -1;
-}
-
-static int64_t _command_start(Transport* transport, const std::string& cmd, uint32_t size,
-                              char* response) {
-    if (cmd.size() > FB_COMMAND_SZ) {
-        g_error = android::base::StringPrintf("command too large (%zu)", cmd.size());
-        return -1;
-    }
-
-    if (response) {
-        response[0] = 0;
-    }
-
-    verbose("sending command \"%s\"", cmd.c_str());
-
-    if (transport->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
-        g_error = android::base::StringPrintf("command write failed (%s)", strerror(errno));
-        transport->Close();
-        return -1;
-    }
-
-    return check_response(transport, size, response);
-}
-
-static int64_t _command_write_data(Transport* transport, const void* data, uint32_t size) {
-    verbose("sending data (%" PRIu32 " bytes)", size);
-
-    int64_t r = transport->Write(data, size);
-    if (r < 0) {
-        g_error = android::base::StringPrintf("data write failure (%s)", strerror(errno));
-        transport->Close();
-        return -1;
-    }
-    if (r != static_cast<int64_t>(size)) {
-        g_error = "data write failure (short transfer)";
-        transport->Close();
-        return -1;
-    }
-    return r;
-}
-
-static int64_t _command_read_data(Transport* transport, void* data, uint32_t size) {
-    verbose("reading data (%" PRIu32 " bytes)", size);
-
-    int64_t r = transport->Read(data, size);
-    if (r < 0) {
-        g_error = android::base::StringPrintf("data read failure (%s)", strerror(errno));
-        transport->Close();
-        return -1;
-    }
-    if (r != (static_cast<int64_t>(size))) {
-        g_error = "data read failure (short transfer)";
-        transport->Close();
-        return -1;
-    }
-    return r;
-}
-
-static int64_t _command_end(Transport* transport) {
-    return check_response(transport, 0, 0) < 0 ? -1 : 0;
-}
-
-static int64_t _command_send(Transport* transport, const std::string& cmd, const void* data,
-                             uint32_t size, char* response) {
-    if (size == 0) {
-        return -1;
-    }
-
-    int64_t r = _command_start(transport, cmd, size, response);
-    if (r < 0) {
-        return -1;
-    }
-    r = _command_write_data(transport, data, size);
-    if (r < 0) {
-        return -1;
-    }
-
-    r = _command_end(transport);
-    if (r < 0) {
-        return -1;
-    }
-
-    return size;
-}
-
-static int64_t _command_send_fd(Transport* transport, const std::string& cmd, int fd, uint32_t size,
-                                char* response) {
-    static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
-    off64_t offset = 0;
-    uint32_t remaining = size;
-
-    if (_command_start(transport, cmd, size, response) < 0) {
-        return -1;
-    }
-
-    while (remaining) {
-        android::FileMap filemap;
-        size_t len = std::min(remaining, MAX_MAP_SIZE);
-
-        if (!filemap.create(NULL, fd, offset, len, true)) {
-            return -1;
-        }
-
-        if (_command_write_data(transport, filemap.getDataPtr(), len) < 0) {
-            return -1;
-        }
-
-        remaining -= len;
-        offset += len;
-    }
-
-    if (_command_end(transport) < 0) {
-        return -1;
-    }
-
-    return size;
-}
-
-static int _command_send_no_data(Transport* transport, const std::string& cmd, char* response) {
-    return _command_start(transport, cmd, 0, response);
-}
-
-int fb_command(Transport* transport, const std::string& cmd) {
-    return _command_send_no_data(transport, cmd, 0);
-}
-
-int fb_command_response(Transport* transport, const std::string& cmd, char* response) {
-    return _command_send_no_data(transport, cmd, response);
-}
-
-int64_t fb_download_data(Transport* transport, const void* data, uint32_t size) {
-    std::string cmd(android::base::StringPrintf("download:%08x", size));
-    return _command_send(transport, cmd.c_str(), data, size, 0) < 0 ? -1 : 0;
-}
-
-int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size) {
-    std::string cmd(android::base::StringPrintf("download:%08x", size));
-    return _command_send_fd(transport, cmd.c_str(), fd, size, 0) < 0 ? -1 : 0;
-}
-
-int64_t fb_upload_data(Transport* transport, const char* outfile) {
-    // positive return value is the upload size sent by the device
-    int64_t r = _command_start(transport, "upload", std::numeric_limits<int32_t>::max(), nullptr);
-    if (r <= 0) {
-        g_error = android::base::StringPrintf("command start failed (%s)", strerror(errno));
-        return r;
-    }
-
-    std::string data;
-    data.resize(r);
-    if ((r = _command_read_data(transport, &data[0], data.size())) == -1) {
-        return r;
-    }
-
-    if (!WriteStringToFile(data, outfile, true)) {
-        g_error = android::base::StringPrintf("write to '%s' failed", outfile);
-        return -1;
-    }
-
-    return _command_end(transport);
-}
-
-static constexpr size_t TRANSPORT_BUF_SIZE = 1024;
-static char transport_buf[TRANSPORT_BUF_SIZE];
-static size_t transport_buf_len;
-
-static int fb_download_data_sparse_write(void* priv, const void* data, size_t len) {
-    const char* ptr = static_cast<const char*>(data);
-    if (transport_buf_len) {
-        size_t to_write = std::min(TRANSPORT_BUF_SIZE - transport_buf_len, len);
-
-        memcpy(transport_buf + transport_buf_len, ptr, to_write);
-        transport_buf_len += to_write;
-        ptr += to_write;
-        len -= to_write;
-    }
-
-    Transport* transport = static_cast<Transport*>(priv);
-    if (transport_buf_len == TRANSPORT_BUF_SIZE) {
-        int64_t r = _command_write_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
-        if (r != static_cast<int64_t>(TRANSPORT_BUF_SIZE)) {
-            return -1;
-        }
-        transport_buf_len = 0;
-    }
-
-    if (len > TRANSPORT_BUF_SIZE) {
-        if (transport_buf_len > 0) {
-            g_error = "internal error: transport_buf not empty";
-            return -1;
-        }
-        size_t to_write = round_down(len, TRANSPORT_BUF_SIZE);
-        int64_t r = _command_write_data(transport, ptr, to_write);
-        if (r != static_cast<int64_t>(to_write)) {
-            return -1;
-        }
-        ptr += to_write;
-        len -= to_write;
-    }
-
-    if (len > 0) {
-        if (len > TRANSPORT_BUF_SIZE) {
-            g_error = "internal error: too much left for transport_buf";
-            return -1;
-        }
-        memcpy(transport_buf, ptr, len);
-        transport_buf_len = len;
-    }
-
-    return 0;
-}
-
-static int fb_download_data_sparse_flush(Transport* transport) {
-    if (transport_buf_len > 0) {
-        int64_t r = _command_write_data(transport, transport_buf, transport_buf_len);
-        if (r != static_cast<int64_t>(transport_buf_len)) {
-            return -1;
-        }
-        transport_buf_len = 0;
-    }
-    return 0;
-}
-
-int fb_download_data_sparse(Transport* transport, struct sparse_file* s) {
-    int64_t size = sparse_file_len(s, true, false);
-    if (size <= 0 || size > std::numeric_limits<uint32_t>::max()) {
-        return -1;
-    }
-
-    std::string cmd(android::base::StringPrintf("download:%08" PRIx64, size));
-    int r = _command_start(transport, cmd, size, 0);
-    if (r < 0) {
-        return -1;
-    }
-
-    r = sparse_file_callback(s, true, false, fb_download_data_sparse_write, transport);
-    if (r < 0) {
-        return -1;
-    }
-
-    r = fb_download_data_sparse_flush(transport);
-    if (r < 0) {
-        return -1;
-    }
-
-    return _command_end(transport);
-}
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index cdab4f1..386dd30 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -47,8 +47,8 @@
 #include <memory>
 #include <thread>
 
-#include "fastboot.h"
 #include "usb.h"
+#include "util.h"
 
 using namespace std::chrono_literals;
 
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index 3dab5ac..0e5fba1 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -106,6 +106,7 @@
 
     if (nullptr == ret->adb_interface) {
         errno = GetLastError();
+        DBG("failed to open interface %S\n", interface_name);
         return nullptr;
     }
 
@@ -157,7 +158,7 @@
     unsigned count = 0;
     int ret;
 
-    DBG("usb_write %d\n", len);
+    DBG("usb_write %zu\n", len);
     if (nullptr != handle_) {
         // Perform write
         while(len > 0) {
@@ -195,7 +196,7 @@
     unsigned long read = 0;
     int ret;
 
-    DBG("usb_read %d\n", len);
+    DBG("usb_read %zu\n", len);
     if (nullptr != handle_) {
         while (1) {
             int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
@@ -269,19 +270,22 @@
         return 0;
 
     // Check vendor and product id first
-    if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
-                                 &device_desc)) {
+    if (!AdbGetUsbDeviceDescriptor(handle->adb_interface, &device_desc)) {
+        DBG("skipping device %x:%x\n", device_desc.idVendor, device_desc.idProduct);
         return 0;
     }
 
     // Then check interface properties
-    if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
-                                    &interf_desc)) {
+    if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface, &interf_desc)) {
+        DBG("skipping device %x:%x, failed to find interface\n", device_desc.idVendor,
+            device_desc.idProduct);
         return 0;
     }
 
     // Must have two endpoints
     if (2 != interf_desc.bNumEndpoints) {
+        DBG("skipping device %x:%x, incorrect number of endpoints\n", device_desc.idVendor,
+            device_desc.idProduct);
         return 0;
     }
 
@@ -305,9 +309,13 @@
     info.device_path[0] = 0;
 
     if (callback(&info) == 0) {
+        DBG("skipping device %x:%x, not selected by callback\n", device_desc.idVendor,
+            device_desc.idProduct);
         return 1;
     }
 
+    DBG("found device %x:%x (%s)\n", device_desc.idVendor, device_desc.idProduct,
+        info.serial_number);
     return 0;
 }
 
@@ -338,6 +346,7 @@
         }
         *copy_name = '\0';
 
+        DBG("attempting to open interface %S\n", next_interface->device_name);
         handle = do_usb_open(next_interface->device_name);
         if (NULL != handle) {
             // Lets see if this interface (device) belongs to us
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index 140270f..8f6e52a 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -33,7 +33,7 @@
 
 #include <sys/time.h>
 
-#include "fastboot.h"
+#include "util.h"
 
 static bool g_verbose = false;
 
diff --git a/fastboot/util.h b/fastboot/util.h
new file mode 100644
index 0000000..9033f93
--- /dev/null
+++ b/fastboot/util.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include <bootimg.h>
+
+/* util stuff */
+double now();
+char* xstrdup(const char*);
+void set_verbose();
+
+// These printf-like functions are implemented in terms of vsnprintf, so they
+// use the same attribute for compile-time format string checking.
+void die(const char* fmt, ...) __attribute__((__noreturn__))
+__attribute__((__format__(__printf__, 1, 2)));
+void verbose(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index b0b4839..a3ce879 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -38,7 +38,6 @@
     include_dirs: ["system/vold"],
     srcs: [
         "fs_mgr.cpp",
-        "fs_mgr_dm_ioctl.cpp",
         "fs_mgr_format.cpp",
         "fs_mgr_verity.cpp",
         "fs_mgr_avb.cpp",
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 9856126..b3df811 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -31,6 +31,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <functional>
 #include <memory>
 #include <string>
 #include <thread>
@@ -50,6 +51,7 @@
 #include <ext4_utils/ext4_sb.h>
 #include <ext4_utils/ext4_utils.h>
 #include <ext4_utils/wipe.h>
+#include <libdm/dm.h>
 #include <linux/fs.h>
 #include <linux/loop.h>
 #include <linux/magic.h>
@@ -59,7 +61,6 @@
 #include "fs_mgr.h"
 #include "fs_mgr_avb.h"
 #include "fs_mgr_priv.h"
-#include "fs_mgr_priv_dm_ioctl.h"
 
 #define KEY_LOC_PROP   "ro.crypto.keyfile.userdata"
 #define KEY_IN_FOOTER  "footer"
@@ -76,6 +77,8 @@
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
+using DeviceMapper = android::dm::DeviceMapper;
+
 // record fs stat
 enum FsStatFlags {
     FS_STAT_IS_EXT4 = 0x0001,
@@ -802,14 +805,9 @@
         return true;
     }
 
-    android::base::unique_fd dm_fd(open("/dev/device-mapper", O_RDONLY));
-    if (dm_fd < 0) {
-        PLOG(ERROR) << "open /dev/device-mapper failed";
-        return false;
-    }
-    struct dm_ioctl io;
+    DeviceMapper& dm = DeviceMapper::Instance();
     std::string device_name;
-    if (!fs_mgr_dm_get_device_name(&io, rec->blk_device, dm_fd, &device_name)) {
+    if (!dm.GetDmDevicePathByName(rec->blk_device, &device_name)) {
         return false;
     }
     free(rec->blk_device);
@@ -1359,7 +1357,7 @@
     return true;
 }
 
-bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback) {
+bool fs_mgr_update_verity_state(std::function<fs_mgr_verity_state_callback> callback) {
     if (!callback) {
         return false;
     }
@@ -1369,12 +1367,6 @@
         return false;
     }
 
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC)));
-    if (fd == -1) {
-        PERROR << "Error opening device mapper";
-        return false;
-    }
-
     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
                                                                fs_mgr_free_fstab);
     if (!fstab) {
@@ -1382,8 +1374,8 @@
         return false;
     }
 
-    alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
-    struct dm_ioctl* io = (struct dm_ioctl*)buffer;
+    DeviceMapper& dm = DeviceMapper::Instance();
+
     bool system_root = android::base::GetProperty("ro.build.system_root_image", "") == "true";
 
     for (int i = 0; i < fstab->num_entries; i++) {
@@ -1399,20 +1391,20 @@
             mount_point = basename(fstab->recs[i].mount_point);
         }
 
-        fs_mgr_dm_ioctl_init(io, DM_BUF_SIZE, mount_point);
+        const char* status = nullptr;
 
-        const char* status;
-        if (ioctl(fd, DM_TABLE_STATUS, io)) {
+        std::vector<DeviceMapper::TargetInfo> table;
+        if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {
             if (fstab->recs[i].fs_mgr_flags & MF_VERIFYATBOOT) {
                 status = "V";
             } else {
                 PERROR << "Failed to query DM_TABLE_STATUS for " << mount_point.c_str();
                 continue;
             }
+        } else {
+            status = table[0].data.c_str();
         }
 
-        status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
-
         // To be consistent in vboot 1.0 and vboot 2.0 (AVB), change the mount_point
         // back to 'system' for the callback. So it has property [partition.system.verified]
         // instead of [partition.vroot.verified].
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 2020fa6..7c6093e 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -33,11 +33,11 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <libavb/libavb.h>
+#include <libdm/dm.h>
 
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
 #include "fs_mgr_priv_avb_ops.h"
-#include "fs_mgr_priv_dm_ioctl.h"
 #include "fs_mgr_priv_sha.h"
 
 static inline bool nibble_value(const char& c, uint8_t* value) {
@@ -218,9 +218,9 @@
 // Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
 // See the following link for more details:
 // https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
-static std::string construct_verity_table(const AvbHashtreeDescriptor& hashtree_desc,
-                                          const std::string& salt, const std::string& root_digest,
-                                          const std::string& blk_device) {
+static bool construct_verity_table(const AvbHashtreeDescriptor& hashtree_desc,
+                                   const std::string& salt, const std::string& root_digest,
+                                   const std::string& blk_device, android::dm::DmTable* table) {
     // Loads androidboot.veritymode from kernel cmdline.
     std::string verity_mode;
     if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
@@ -235,145 +235,56 @@
         dm_verity_mode = "ignore_corruption";
     } else if (verity_mode != "eio") {  // Default dm_verity_mode is eio.
         LERROR << "Unknown androidboot.veritymode: " << verity_mode;
-        return "";
+        return false;
     }
 
-    // dm-verity construction parameters:
-    //   <version> <dev> <hash_dev>
-    //   <data_block_size> <hash_block_size>
-    //   <num_data_blocks> <hash_start_block>
-    //   <algorithm> <digest> <salt>
-    //   [<#opt_params> <opt_params>]
-    std::ostringstream verity_table;
-    verity_table << hashtree_desc.dm_verity_version << " " << blk_device << " " << blk_device << " "
-                 << hashtree_desc.data_block_size << " " << hashtree_desc.hash_block_size << " "
-                 << hashtree_desc.image_size / hashtree_desc.data_block_size << " "
-                 << hashtree_desc.tree_offset / hashtree_desc.hash_block_size << " "
-                 << hashtree_desc.hash_algorithm << " " << root_digest << " " << salt;
+    std::ostringstream hash_algorithm;
+    hash_algorithm << hashtree_desc.hash_algorithm;
 
-    // Continued from the above optional parameters:
-    //   [<#opt_params> <opt_params>]
-    int optional_argc = 0;
-    std::ostringstream optional_args;
-
-    // dm-verity optional parameters for FEC (forward error correction):
-    //   use_fec_from_device <fec_dev>
-    //   fec_roots <num>
-    //   fec_blocks <num>
-    //   fec_start <offset>
+    android::dm::DmTargetVerity target(0, hashtree_desc.image_size / 512,
+                                       hashtree_desc.dm_verity_version, blk_device, blk_device,
+                                       hashtree_desc.data_block_size, hashtree_desc.hash_block_size,
+                                       hashtree_desc.image_size / hashtree_desc.data_block_size,
+                                       hashtree_desc.tree_offset / hashtree_desc.hash_block_size,
+                                       hash_algorithm.str(), root_digest, salt);
     if (hashtree_desc.fec_size > 0) {
-        // Note that fec_blocks is the size that FEC covers, *NOT* the
-        // size of the FEC data. Since we use FEC for everything up until
-        // the FEC data, it's the same as the offset (fec_start).
-        optional_argc += 8;
-        // clang-format off
-        optional_args << "use_fec_from_device " << blk_device
-                      << " fec_roots " << hashtree_desc.fec_num_roots
-                      << " fec_blocks " << hashtree_desc.fec_offset / hashtree_desc.data_block_size
-                      << " fec_start " << hashtree_desc.fec_offset / hashtree_desc.data_block_size
-                      << " ";
-        // clang-format on
+        target.UseFec(blk_device, hashtree_desc.fec_num_roots,
+                      hashtree_desc.fec_offset / hashtree_desc.data_block_size,
+                      hashtree_desc.fec_offset / hashtree_desc.data_block_size);
     }
-
     if (!dm_verity_mode.empty()) {
-        optional_argc += 1;
-        optional_args << dm_verity_mode << " ";
+        target.SetVerityMode(dm_verity_mode);
     }
-
     // Always use ignore_zero_blocks.
-    optional_argc += 1;
-    optional_args << "ignore_zero_blocks";
+    target.IgnoreZeroBlocks();
 
-    verity_table << " " << optional_argc << " " << optional_args.str();
-    return verity_table.str();
-}
+    LINFO << "Built verity table: '" << target.GetParameterString() << "'";
 
-static bool load_verity_table(struct dm_ioctl* io, const std::string& dm_device_name, int fd,
-                              uint64_t image_size, const std::string& verity_table) {
-    fs_mgr_dm_ioctl_init(io, DM_BUF_SIZE, dm_device_name);
-
-    // The buffer consists of [dm_ioctl][dm_target_spec][verity_params].
-    char* buffer = (char*)io;
-
-    // Builds the dm_target_spec arguments.
-    struct dm_target_spec* dm_target = (struct dm_target_spec*)&buffer[sizeof(struct dm_ioctl)];
-    io->flags = DM_READONLY_FLAG;
-    io->target_count = 1;
-    dm_target->status = 0;
-    dm_target->sector_start = 0;
-    dm_target->length = image_size / 512;
-    strcpy(dm_target->target_type, "verity");
-
-    // Builds the verity params.
-    char* verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
-    size_t bufsize = DM_BUF_SIZE - (verity_params - buffer);
-
-    LINFO << "Loading verity table: '" << verity_table << "'";
-
-    // Copies verity_table to verity_params (including the terminating null byte).
-    if (verity_table.size() > bufsize - 1) {
-        LERROR << "Verity table size too large: " << verity_table.size()
-               << " (max allowable size: " << bufsize - 1 << ")";
-        return false;
-    }
-    memcpy(verity_params, verity_table.c_str(), verity_table.size() + 1);
-
-    // Sets ext target boundary.
-    verity_params += verity_table.size() + 1;
-    verity_params = (char*)(((unsigned long)verity_params + 7) & ~7);
-    dm_target->next = verity_params - buffer;
-
-    // Sends the ioctl to load the verity table.
-    if (ioctl(fd, DM_TABLE_LOAD, io)) {
-        PERROR << "Error loading verity table";
-        return false;
-    }
-
-    return true;
+    return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));
 }
 
 static bool hashtree_dm_verity_setup(struct fstab_rec* fstab_entry,
                                      const AvbHashtreeDescriptor& hashtree_desc,
                                      const std::string& salt, const std::string& root_digest,
                                      bool wait_for_verity_dev) {
-    // Gets the device mapper fd.
-    android::base::unique_fd fd(open("/dev/device-mapper", O_RDWR));
-    if (fd < 0) {
-        PERROR << "Error opening device mapper";
+    android::dm::DmTable table;
+    if (!construct_verity_table(hashtree_desc, salt, root_digest, fstab_entry->blk_device, &table) ||
+        !table.valid()) {
+        LERROR << "Failed to construct verity table.";
         return false;
     }
+    table.set_readonly(true);
 
-    // Creates the device.
-    alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
-    struct dm_ioctl* io = (struct dm_ioctl*)buffer;
     const std::string mount_point(basename(fstab_entry->mount_point));
-    if (!fs_mgr_dm_create_device(io, mount_point, fd)) {
+    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+    if (!dm.CreateDevice(mount_point, table)) {
         LERROR << "Couldn't create verity device!";
         return false;
     }
 
-    // Gets the name of the device file.
-    std::string verity_blk_name;
-    if (!fs_mgr_dm_get_device_name(io, mount_point, fd, &verity_blk_name)) {
-        LERROR << "Couldn't get verity device number!";
-        return false;
-    }
-
-    std::string verity_table =
-        construct_verity_table(hashtree_desc, salt, root_digest, fstab_entry->blk_device);
-    if (verity_table.empty()) {
-        LERROR << "Failed to construct verity table.";
-        return false;
-    }
-
-    // Loads the verity mapping table.
-    if (!load_verity_table(io, mount_point, fd, hashtree_desc.image_size, verity_table)) {
-        LERROR << "Couldn't load verity table!";
-        return false;
-    }
-
-    // Activates the device.
-    if (!fs_mgr_dm_resume_table(io, mount_point, fd)) {
+    std::string dev_path;
+    if (!dm.GetDmDevicePathByName(mount_point, &dev_path)) {
+        LERROR << "Couldn't get verity device path!";
         return false;
     }
 
@@ -382,10 +293,10 @@
 
     // Updates fstab_rec->blk_device to verity device name.
     free(fstab_entry->blk_device);
-    fstab_entry->blk_device = strdup(verity_blk_name.c_str());
+    fstab_entry->blk_device = strdup(dev_path.c_str());
 
     // Makes sure we've set everything up properly.
-    if (wait_for_verity_dev && !fs_mgr_wait_for_file(verity_blk_name, 1s)) {
+    if (wait_for_verity_dev && !fs_mgr_wait_for_file(dev_path, 1s)) {
         return false;
     }
 
diff --git a/fs_mgr/fs_mgr_dm_ioctl.cpp b/fs_mgr/fs_mgr_dm_ioctl.cpp
deleted file mode 100644
index 3a7fae4..0000000
--- a/fs_mgr/fs_mgr_dm_ioctl.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2016 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 <errno.h>
-#include <string.h>
-
-#include <android-base/logging.h>
-#include <sys/ioctl.h>
-
-#include "fs_mgr_priv.h"
-#include "fs_mgr_priv_dm_ioctl.h"
-
-void fs_mgr_dm_ioctl_init(struct dm_ioctl* io, size_t size, const std::string& name) {
-    memset(io, 0, size);
-    io->data_size = size;
-    io->data_start = sizeof(struct dm_ioctl);
-    io->version[0] = 4;
-    io->version[1] = 0;
-    io->version[2] = 0;
-    if (!name.empty()) {
-        strlcpy(io->name, name.c_str(), sizeof(io->name));
-    }
-}
-
-bool fs_mgr_dm_create_device(struct dm_ioctl* io, const std::string& name, int fd) {
-    fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
-    if (ioctl(fd, DM_DEV_CREATE, io)) {
-        PERROR << "Error creating device mapping";
-        return false;
-    }
-    return true;
-}
-
-bool fs_mgr_dm_destroy_device(struct dm_ioctl* io, const std::string& name, int fd) {
-    fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
-    if (ioctl(fd, DM_DEV_REMOVE, io)) {
-        PERROR << "Error removing device mapping";
-        return false;
-    }
-    return true;
-}
-
-bool fs_mgr_dm_get_device_name(struct dm_ioctl* io, const std::string& name, int fd,
-                               std::string* out_dev_name) {
-    FS_MGR_CHECK(out_dev_name != nullptr);
-
-    fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
-    if (ioctl(fd, DM_DEV_STATUS, io)) {
-        PERROR << "Error fetching device-mapper device number";
-        return false;
-    }
-
-    int dev_num = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
-    *out_dev_name = "/dev/block/dm-" + std::to_string(dev_num);
-
-    return true;
-}
-
-bool fs_mgr_dm_resume_table(struct dm_ioctl* io, const std::string& name, int fd) {
-    fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
-    if (ioctl(fd, DM_DEV_SUSPEND, io)) {
-        PERROR << "Error activating device table";
-        return false;
-    }
-    return true;
-}
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index ed42d40..05e03e1 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -40,102 +40,62 @@
 #include <liblp/reader.h>
 
 #include "fs_mgr_priv.h"
-#include "fs_mgr_priv_dm_ioctl.h"
 
 namespace android {
 namespace fs_mgr {
 
-std::string LogicalPartitionExtent::Serialize() const {
-    // Note: we need to include an explicit null-terminator.
-    std::string argv =
-        android::base::StringPrintf("%s %" PRIu64, block_device_.c_str(), first_sector_);
-    argv.push_back(0);
+using DeviceMapper = android::dm::DeviceMapper;
+using DmTable = android::dm::DmTable;
+using DmTarget = android::dm::DmTarget;
+using DmTargetZero = android::dm::DmTargetZero;
+using DmTargetLinear = android::dm::DmTargetLinear;
 
-    // The kernel expects each target to be aligned.
-    size_t spec_bytes = sizeof(struct dm_target_spec) + argv.size();
-    size_t padding = ((spec_bytes + 7) & ~7) - spec_bytes;
-    for (size_t i = 0; i < padding; i++) {
-        argv.push_back(0);
-    }
-
-    struct dm_target_spec spec;
-    spec.sector_start = logical_sector_;
-    spec.length = num_sectors_;
-    spec.status = 0;
-    strcpy(spec.target_type, "linear");
-    spec.next = sizeof(struct dm_target_spec) + argv.size();
-
-    return std::string((char*)&spec, sizeof(spec)) + argv;
-}
-
-static bool LoadDmTable(int dm_fd, const LogicalPartition& partition) {
-    // Combine all dm_target_spec buffers together.
-    std::string target_string;
-    for (const auto& extent : partition.extents) {
-        target_string += extent.Serialize();
-    }
-
-    // Allocate the ioctl buffer.
-    size_t buffer_size = sizeof(struct dm_ioctl) + target_string.size();
-    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(buffer_size);
-
-    // Initialize the ioctl buffer header, then copy our target specs in.
-    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
-    fs_mgr_dm_ioctl_init(io, buffer_size, partition.name);
-    io->target_count = partition.extents.size();
-    if (partition.attributes & kPartitionReadonly) {
-        io->flags |= DM_READONLY_FLAG;
-    }
-    memcpy(io + 1, target_string.c_str(), target_string.size());
-
-    if (ioctl(dm_fd, DM_TABLE_LOAD, io)) {
-        PERROR << "Failed ioctl() on DM_TABLE_LOAD, partition " << partition.name;
-        return false;
-    }
-    return true;
-}
-
-static bool LoadTablesAndActivate(int dm_fd, const LogicalPartition& partition) {
-    if (!LoadDmTable(dm_fd, partition)) {
-        return false;
-    }
-
-    struct dm_ioctl io;
-    return fs_mgr_dm_resume_table(&io, partition.name, dm_fd);
-}
-
-static bool CreateDmDeviceForPartition(int dm_fd, const LogicalPartition& partition) {
-    struct dm_ioctl io;
-    if (!fs_mgr_dm_create_device(&io, partition.name, dm_fd)) {
-        return false;
-    }
-    if (!LoadTablesAndActivate(dm_fd, partition)) {
-        // Remove the device rather than leave it in an inactive state.
-        fs_mgr_dm_destroy_device(&io, partition.name, dm_fd);
-        return false;
-    }
-
-    LINFO << "Created device-mapper device: " << partition.name;
-    return true;
-}
-
-bool CreateLogicalPartitions(const LogicalPartitionTable& table) {
-    android::base::unique_fd dm_fd(open("/dev/device-mapper", O_RDWR));
-    if (dm_fd < 0) {
-        PLOG(ERROR) << "failed to open /dev/device-mapper";
-        return false;
-    }
-    for (const auto& partition : table.partitions) {
-        if (!CreateDmDeviceForPartition(dm_fd, partition)) {
-            LOG(ERROR) << "could not create dm-linear device for partition: " << partition.name;
+static bool CreateDmTable(const std::string& block_device, const LpMetadata& metadata,
+                          const LpMetadataPartition& partition, DmTable* table) {
+    uint64_t sector = 0;
+    for (size_t i = 0; i < partition.num_extents; i++) {
+        const auto& extent = metadata.extents[partition.first_extent_index + i];
+        std::unique_ptr<DmTarget> target;
+        switch (extent.target_type) {
+            case LP_TARGET_TYPE_ZERO:
+                target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
+                break;
+            case LP_TARGET_TYPE_LINEAR:
+                target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, block_device,
+                                                          extent.target_data);
+                break;
+            default:
+                LOG(ERROR) << "Unknown target type in metadata: " << extent.target_type;
+                return false;
+        }
+        if (!table->AddTarget(std::move(target))) {
             return false;
         }
+        sector += extent.num_sectors;
+    }
+    if (partition.attributes & LP_PARTITION_ATTR_READONLY) {
+        table->set_readonly(true);
     }
     return true;
 }
 
-std::unique_ptr<LogicalPartitionTable> LoadPartitionsFromDeviceTree() {
-    return nullptr;
+static bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
+                                   const LpMetadataPartition& partition, std::string* path) {
+    DeviceMapper& dm = DeviceMapper::Instance();
+
+    DmTable table;
+    if (!CreateDmTable(block_device, metadata, partition, &table)) {
+        return false;
+    }
+    std::string name = GetPartitionName(partition);
+    if (!dm.CreateDevice(name, table)) {
+        return false;
+    }
+    if (!dm.GetDmDevicePathByName(name, path)) {
+        return false;
+    }
+    LINFO << "Created logical partition " << name << " on device " << *path;
+    return true;
 }
 
 bool CreateLogicalPartitions(const std::string& block_device) {
@@ -145,22 +105,35 @@
         LOG(ERROR) << "Could not read partition table.";
         return true;
     }
-
-    LogicalPartitionTable table;
     for (const auto& partition : metadata->partitions) {
-        LogicalPartition new_partition;
-        new_partition.name = GetPartitionName(partition);
-        new_partition.attributes = partition.attributes;
-        for (size_t i = 0; i < partition.num_extents; i++) {
-            const auto& extent = metadata->extents[partition.first_extent_index + i];
-            new_partition.extents.emplace_back(new_partition.num_sectors, extent.target_data,
-                                               extent.num_sectors, block_device.c_str());
-            new_partition.num_sectors += extent.num_sectors;
+        std::string path;
+        if (!CreateLogicalPartition(block_device, *metadata.get(), partition, &path)) {
+            LERROR << "Could not create logical partition: " << GetPartitionName(partition);
+            return false;
         }
-        table.partitions.push_back(new_partition);
     }
+    return true;
+}
 
-    return CreateLogicalPartitions(table);
+bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
+                            const std::string& partition_name, std::string* path) {
+    auto metadata = ReadMetadata(block_device.c_str(), metadata_slot);
+    if (!metadata) {
+        LOG(ERROR) << "Could not read partition table.";
+        return true;
+    }
+    for (const auto& partition : metadata->partitions) {
+        if (GetPartitionName(partition) == partition_name) {
+            return CreateLogicalPartition(block_device, *metadata.get(), partition, path);
+        }
+    }
+    LERROR << "Could not find any partition with name: " << partition_name;
+    return false;
+}
+
+bool DestroyLogicalPartition(const std::string& name) {
+    DeviceMapper& dm = DeviceMapper::Instance();
+    return dm.DeleteDevice(name);
 }
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index a14dba3..a5b3fe8 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -235,41 +235,46 @@
          * If not found, the loop exits with fl[i].name being null.
          */
         for (i = 0; fl[i].name; i++) {
-            if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
+            auto name = fl[i].name;
+            auto len = strlen(name);
+            auto end = len;
+            if (name[end - 1] == '=') --end;
+            if (!strncmp(p, name, len) && (p[end] == name[end])) {
                 f |= fl[i].flag;
-                if ((fl[i].flag == MF_CRYPT) && flag_vals) {
+                if (!flag_vals) break;
+                if (p[end] != '=') break;
+                char* arg = p + end + 1;
+                auto flag = fl[i].flag;
+                if (flag == MF_CRYPT) {
                     /* The encryptable flag is followed by an = and the
                      * location of the keys.  Get it and return it.
                      */
-                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
-                } else if ((fl[i].flag == MF_VERIFY) && flag_vals) {
+                    flag_vals->key_loc = strdup(arg);
+                } else if (flag == MF_VERIFY) {
                     /* If the verify flag is followed by an = and the
                      * location for the verity state,  get it and return it.
                      */
-                    char *start = strchr(p, '=');
-                    if (start) {
-                        flag_vals->verity_loc = strdup(start + 1);
-                    }
-                } else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) {
+                    flag_vals->verity_loc = strdup(arg);
+                } else if (flag == MF_FORCECRYPT) {
                     /* The forceencrypt flag is followed by an = and the
                      * location of the keys.  Get it and return it.
                      */
-                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
-                } else if ((fl[i].flag == MF_FORCEFDEORFBE) && flag_vals) {
+                    flag_vals->key_loc = strdup(arg);
+                } else if (flag == MF_FORCEFDEORFBE) {
                     /* The forcefdeorfbe flag is followed by an = and the
                      * location of the keys.  Get it and return it.
                      */
-                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+                    flag_vals->key_loc = strdup(arg);
                     flag_vals->file_contents_mode = EM_AES_256_XTS;
                     flag_vals->file_names_mode = EM_AES_256_CTS;
-                } else if ((fl[i].flag == MF_FILEENCRYPTION) && flag_vals) {
+                } else if (flag == MF_FILEENCRYPTION) {
                     /* The fileencryption flag is followed by an = and
                      * the mode of contents encryption, then optionally a
                      * : and the mode of filenames encryption (defaults
                      * to aes-256-cts).  Get it and return it.
                      */
-                    char *mode = strchr(p, '=') + 1;
-                    char *colon = strchr(mode, ':');
+                    auto mode = arg;
+                    auto colon = strchr(mode, ':');
                     if (colon) {
                         *colon = '\0';
                     }
@@ -283,33 +288,30 @@
                     } else {
                         flag_vals->file_names_mode = EM_AES_256_CTS;
                     }
-                } else if ((fl[i].flag == MF_KEYDIRECTORY) && flag_vals) {
+                } else if (flag == MF_KEYDIRECTORY) {
                     /* The metadata flag is followed by an = and the
                      * directory for the keys.  Get it and return it.
                      */
-                    flag_vals->key_dir = strdup(strchr(p, '=') + 1);
-                } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
+                    flag_vals->key_dir = strdup(arg);
+                } else if (flag == MF_LENGTH) {
                     /* The length flag is followed by an = and the
                      * size of the partition.  Get it and return it.
                      */
-                    flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
-                } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) {
+                    flag_vals->part_length = strtoll(arg, NULL, 0);
+                } else if (flag == MF_VOLDMANAGED) {
                     /* The voldmanaged flag is followed by an = and the
                      * label, a colon and the partition number or the
                      * word "auto", e.g.
                      *   voldmanaged=sdcard:3
                      * Get and return them.
                      */
-                    char *label_start;
-                    char *label_end;
-                    char *part_start;
+                    auto label_start = arg;
+                    auto label_end = strchr(label_start, ':');
 
-                    label_start = strchr(p, '=') + 1;
-                    label_end = strchr(p, ':');
                     if (label_end) {
                         flag_vals->label = strndup(label_start,
                                                    (int) (label_end - label_start));
-                        part_start = strchr(p, ':') + 1;
+                        auto part_start = label_end + 1;
                         if (!strcmp(part_start, "auto")) {
                             flag_vals->partnum = -1;
                         } else {
@@ -318,41 +320,41 @@
                     } else {
                         LERROR << "Warning: voldmanaged= flag malformed";
                     }
-                } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
-                    flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
-                } else if ((fl[i].flag == MF_MAX_COMP_STREAMS) && flag_vals) {
-                    flag_vals->max_comp_streams = strtoll(strchr(p, '=') + 1, NULL, 0);
-                } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
-                    int is_percent = !!strrchr(p, '%');
-                    unsigned int val = strtoll(strchr(p, '=') + 1, NULL, 0);
+                } else if (flag == MF_SWAPPRIO) {
+                    flag_vals->swap_prio = strtoll(arg, NULL, 0);
+                } else if (flag == MF_MAX_COMP_STREAMS) {
+                    flag_vals->max_comp_streams = strtoll(arg, NULL, 0);
+                } else if (flag == MF_ZRAMSIZE) {
+                    auto is_percent = !!strrchr(arg, '%');
+                    auto val = strtoll(arg, NULL, 0);
                     if (is_percent)
                         flag_vals->zram_size = calculate_zram_size(val);
                     else
                         flag_vals->zram_size = val;
-                } else if ((fl[i].flag == MF_RESERVEDSIZE) && flag_vals) {
+                } else if (flag == MF_RESERVEDSIZE) {
                     /* The reserved flag is followed by an = and the
                      * reserved size of the partition.  Get it and return it.
                      */
-                    flag_vals->reserved_size = parse_size(strchr(p, '=') + 1);
-                } else if ((fl[i].flag == MF_ERASEBLKSIZE) && flag_vals) {
+                    flag_vals->reserved_size = parse_size(arg);
+                } else if (flag == MF_ERASEBLKSIZE) {
                     /* The erase block size flag is followed by an = and the flash
                      * erase block size. Get it, check that it is a power of 2 and
                      * at least 4096, and return it.
                      */
-                    unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
+                    auto val = strtoul(arg, NULL, 0);
                     if (val >= 4096 && (val & (val - 1)) == 0)
                         flag_vals->erase_blk_size = val;
-                } else if ((fl[i].flag == MF_LOGICALBLKSIZE) && flag_vals) {
+                } else if (flag == MF_LOGICALBLKSIZE) {
                     /* The logical block size flag is followed by an = and the flash
                      * logical block size. Get it, check that it is a power of 2 and
                      * at least 4096, and return it.
                      */
-                    unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
+                    auto val = strtoul(arg, NULL, 0);
                     if (val >= 4096 && (val & (val - 1)) == 0)
                         flag_vals->logical_blk_size = val;
-                } else if ((fl[i].flag == MF_SYSFS) && flag_vals) {
+                } else if (flag == MF_SYSFS) {
                     /* The path to trigger device gc by idle-maint of vold. */
-                    flag_vals->sysfs_path = strdup(strchr(p, '=') + 1);
+                    flag_vals->sysfs_path = strdup(arg);
                 }
                 break;
             }
@@ -506,8 +508,7 @@
     return false;
 }
 
-static struct fstab *fs_mgr_read_fstab_file(FILE *fstab_file)
-{
+static struct fstab* fs_mgr_read_fstab_file(FILE* fstab_file, bool proc_mounts) {
     int cnt, entries;
     ssize_t len;
     size_t alloc_len = 0;
@@ -607,7 +608,10 @@
             fstab->recs[cnt].fs_options = NULL;
         }
 
-        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+        // For /proc/mounts, ignore everything after mnt_freq and mnt_passno
+        if (proc_mounts) {
+            p += strlen(p);
+        } else if (!(p = strtok_r(NULL, delim, &save_ptr))) {
             LERROR << "Error parsing fs_mgr_options";
             goto err;
         }
@@ -739,7 +743,7 @@
         return nullptr;
     }
 
-    fstab = fs_mgr_read_fstab_file(fstab_file);
+    fstab = fs_mgr_read_fstab_file(fstab_file, !strcmp("/proc/mounts", fstab_path));
     if (!fstab) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << fstab_path << "'";
     }
@@ -767,7 +771,7 @@
         return nullptr;
     }
 
-    struct fstab *fstab = fs_mgr_read_fstab_file(fstab_file.get());
+    struct fstab* fstab = fs_mgr_read_fstab_file(fstab_file.get(), false);
     if (!fstab) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:"
                << std::endl << fstab_buf;
diff --git a/fs_mgr/fs_mgr_priv_dm_ioctl.h b/fs_mgr/fs_mgr_priv_dm_ioctl.h
deleted file mode 100644
index 792475d..0000000
--- a/fs_mgr/fs_mgr_priv_dm_ioctl.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CORE_FS_MGR_PRIV_DM_IOCTL_H
-#define __CORE_FS_MGR_PRIV_DM_IOCTL_H
-
-#include <linux/dm-ioctl.h>
-#include <string>
-
-void fs_mgr_dm_ioctl_init(struct dm_ioctl* io, size_t size, const std::string& name);
-
-bool fs_mgr_dm_create_device(struct dm_ioctl* io, const std::string& name, int fd);
-
-bool fs_mgr_dm_destroy_device(struct dm_ioctl* io, const std::string& name, int fd);
-
-bool fs_mgr_dm_get_device_name(struct dm_ioctl* io, const std::string& name, int fd,
-                               std::string* out_dev_name);
-
-bool fs_mgr_dm_resume_table(struct dm_ioctl* io, const std::string& name, int fd);
-
-#endif /* __CORE_FS_MGR_PRIV_DM_IOCTL_H */
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index fe41f8a..5fb4ebb 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -35,6 +35,7 @@
 #include <android-base/unique_fd.h>
 #include <crypto_utils/android_pubkey.h>
 #include <cutils/properties.h>
+#include <libdm/dm.h>
 #include <logwrap/logwrap.h>
 #include <openssl/obj_mac.h>
 #include <openssl/rsa.h>
@@ -44,7 +45,6 @@
 
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
-#include "fs_mgr_priv_dm_ioctl.h"
 
 #define VERITY_TABLE_RSA_KEY "/verity_key"
 #define VERITY_TABLE_HASH_IDX 8
@@ -250,48 +250,27 @@
     return true;
 }
 
-static int load_verity_table(struct dm_ioctl *io, const std::string &name,
-                             uint64_t device_size, int fd,
-        const struct verity_table_params *params, format_verity_table_func format)
-{
-    char *verity_params;
-    char *buffer = (char*) io;
-    size_t bufsize;
+static int load_verity_table(android::dm::DeviceMapper& dm, const std::string& name,
+                             uint64_t device_size, const struct verity_table_params* params,
+                             format_verity_table_func format) {
+    android::dm::DmTable table;
+    table.set_readonly(true);
 
-    fs_mgr_dm_ioctl_init(io, DM_BUF_SIZE, name);
-
-    struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
-
-    // set tgt arguments
-    io->target_count = 1;
-    io->flags = DM_READONLY_FLAG;
-    tgt->status = 0;
-    tgt->sector_start = 0;
-    tgt->length = device_size / 512;
-    strcpy(tgt->target_type, "verity");
-
-    // build the verity params
-    verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
-    bufsize = DM_BUF_SIZE - (verity_params - buffer);
-
-    if (!format(verity_params, bufsize, params)) {
+    char buffer[DM_BUF_SIZE];
+    if (!format(buffer, sizeof(buffer), params)) {
         LERROR << "Failed to format verity parameters";
         return -1;
     }
 
-    LINFO << "loading verity table: '" << verity_params << "'";
-
-    // set next target boundary
-    verity_params += strlen(verity_params) + 1;
-    verity_params = (char*)(((uintptr_t)verity_params + 7) & ~7);
-    tgt->next = verity_params - buffer;
-
-    // send the ioctl to load the verity table
-    if (ioctl(fd, DM_TABLE_LOAD, io)) {
-        PERROR << "Error loading verity table";
+    android::dm::DmTargetVerityString target(0, device_size / 512, buffer);
+    if (!table.AddTarget(std::make_unique<decltype(target)>(target))) {
+        LERROR << "Failed to add verity target";
         return -1;
     }
-
+    if (!dm.CreateDevice(name, table)) {
+        LERROR << "Failed to create verity device \"" << name << "\"";
+        return -1;
+    }
     return 0;
 }
 
@@ -761,11 +740,11 @@
     struct fec_verity_metadata verity;
     struct verity_table_params params = { .table = NULL };
 
-    alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
-    struct dm_ioctl *io = (struct dm_ioctl *) buffer;
     const std::string mount_point(basename(fstab->mount_point));
     bool verified_at_boot = false;
 
+    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+
     if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
             FEC_DEFAULT_ROOTS) < 0) {
         PERROR << "Failed to open '" << fstab->blk_device << "'";
@@ -798,24 +777,6 @@
 
     params.ecc_dev = fstab->blk_device;
 
-    // get the device mapper fd
-    if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
-        PERROR << "Error opening device mapper";
-        goto out;
-    }
-
-    // create the device
-    if (!fs_mgr_dm_create_device(io, mount_point, fd)) {
-        LERROR << "Couldn't create verity device!";
-        goto out;
-    }
-
-    // get the name of the device file
-    if (!fs_mgr_dm_get_device_name(io, mount_point, fd, &verity_blk_name)) {
-        LERROR << "Couldn't get verity device number!";
-        goto out;
-    }
-
     if (load_verity_state(fstab, &params.mode) < 0) {
         /* if accessing or updating the state failed, switch to the default
          * safe mode. This makes sure the device won't end up in an endless
@@ -861,8 +822,7 @@
                                    fstab->fs_mgr_flags & MF_SLOTSELECT);
 
     // load the verity mapping table
-    if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
-            format_verity_table) == 0) {
+    if (load_verity_table(dm, mount_point, verity.data_size, &params, format_verity_table) == 0) {
         goto loaded;
     }
 
@@ -871,15 +831,14 @@
         LINFO << "Disabling error correction for " << mount_point.c_str();
         params.ecc.valid = false;
 
-        if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
-                format_verity_table) == 0) {
+        if (load_verity_table(dm, mount_point, verity.data_size, &params, format_verity_table) == 0) {
             goto loaded;
         }
     }
 
     // try the legacy format for backwards compatibility
-    if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
-            format_legacy_verity_table) == 0) {
+    if (load_verity_table(dm, mount_point, verity.data_size, &params, format_legacy_verity_table) ==
+        0) {
         goto loaded;
     }
 
@@ -888,8 +847,8 @@
         LINFO << "Falling back to EIO mode for " << mount_point.c_str();
         params.mode = VERITY_MODE_EIO;
 
-        if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
-                format_legacy_verity_table) == 0) {
+        if (load_verity_table(dm, mount_point, verity.data_size, &params,
+                              format_legacy_verity_table) == 0) {
             goto loaded;
         }
     }
@@ -898,9 +857,8 @@
     goto out;
 
 loaded:
-
-    // activate the device
-    if (!fs_mgr_dm_resume_table(io, mount_point, fd)) {
+    if (!dm.GetDmDevicePathByName(mount_point, &verity_blk_name)) {
+        LERROR << "Couldn't get verity device number!";
         goto out;
     }
 
@@ -923,7 +881,7 @@
     if (!verified_at_boot) {
         free(fstab->blk_device);
         fstab->blk_device = strdup(verity_blk_name.c_str());
-    } else if (!fs_mgr_dm_destroy_device(io, mount_point, fd)) {
+    } else if (!dm.DeleteDevice(mount_point)) {
         LERROR << "Failed to remove verity device " << mount_point.c_str();
         goto out;
     }
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index c1b2ed9..1049fb6 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -22,6 +22,8 @@
 #include <stdbool.h>
 #include <linux/dm-ioctl.h>
 
+#include <functional>
+
 #include <fstab/fstab.h>
 
 // Magic number at start of verity metadata
@@ -48,8 +50,8 @@
 };
 
 // Callback function for verity status
-typedef void (*fs_mgr_verity_state_callback)(struct fstab_rec *fstab,
-        const char *mount_point, int mode, int status);
+typedef void fs_mgr_verity_state_callback(struct fstab_rec* fstab, const char* mount_point,
+                                          int mode, int status);
 
 #define FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED 7
 #define FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION 6
@@ -73,7 +75,7 @@
 struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab);
 void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size);
 bool fs_mgr_load_verity_state(int* mode);
-bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback);
+bool fs_mgr_update_verity_state(std::function<fs_mgr_verity_state_callback> callback);
 int fs_mgr_swapon_all(struct fstab *fstab);
 bool fs_mgr_update_logical_partition(struct fstab_rec* rec);
 
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 9772c4b..cac475c 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -26,74 +26,28 @@
 #define __CORE_FS_MGR_DM_LINEAR_H
 
 #include <stdint.h>
+
 #include <memory>
 #include <string>
 #include <vector>
 
+#include <libdm/dm.h>
+#include <liblp/metadata_format.h>
+
 namespace android {
 namespace fs_mgr {
 
-static const uint32_t kPartitionReadonly = 0x1;
-
-class LogicalPartitionExtent {
-  public:
-    LogicalPartitionExtent() : logical_sector_(0), first_sector_(0), num_sectors_(0) {}
-    LogicalPartitionExtent(uint64_t logical_sector, uint64_t first_sector, uint64_t num_sectors,
-                           const std::string& block_device)
-        : logical_sector_(logical_sector),
-          first_sector_(first_sector),
-          num_sectors_(num_sectors),
-          block_device_(block_device) {}
-
-    // Return a string containing the dm_target_spec buffer needed to use this
-    // extent in a device-mapper table.
-    std::string Serialize() const;
-
-    const std::string& block_device() const { return block_device_; }
-
-  private:
-    // Logical sector this extent represents in the presented block device.
-    // This is equal to the previous extent's logical sector plus the number
-    // of sectors in that extent. The first extent always starts at 0.
-    uint64_t logical_sector_;
-    // First 512-byte sector of this extent, on the source block device.
-    uint64_t first_sector_;
-    // Number of 512-byte sectors.
-    uint64_t num_sectors_;
-    // Target block device.
-    std::string block_device_;
-};
-
-struct LogicalPartition {
-    LogicalPartition() : attributes(0), num_sectors(0) {}
-
-    std::string name;
-    uint32_t attributes;
-    // Number of 512-byte sectors total.
-    uint64_t num_sectors;
-    // List of extents.
-    std::vector<LogicalPartitionExtent> extents;
-};
-
-struct LogicalPartitionTable {
-    // List of partitions in the partition table.
-    std::vector<LogicalPartition> partitions;
-};
-
-// Load a dm-linear table from the device tree if one is available; otherwise,
-// return null.
-std::unique_ptr<LogicalPartitionTable> LoadPartitionsFromDeviceTree();
-
-// Create device-mapper devices for the given partition table.
-//
-// On success, two devices nodes will be created for each partition, both
-// pointing to the same device:
-//   /dev/block/dm-<N> where N is a sequential ID assigned by device-mapper.
-//   /dev/block/dm-<name> where |name| is the partition name.
-//
-bool CreateLogicalPartitions(const LogicalPartitionTable& table);
 bool CreateLogicalPartitions(const std::string& block_device);
 
+// 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.
+bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
+                            const std::string& partition_name, std::string* path);
+
+// Destroy the block device for a logical partition, by name.
+bool DestroyLogicalPartition(const std::string& name);
+
 }  // namespace fs_mgr
 }  // namespace android
 
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index b96f4c1..ad3b6f4 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -272,6 +272,46 @@
     return true;
 }
 
+bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
+    char buffer[4096];
+    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer);
+
+    InitIo(io, name);
+    io->data_size = sizeof(buffer);
+    io->data_start = sizeof(*io);
+    if (ioctl(fd_, DM_TABLE_STATUS, io) < 0) {
+        PLOG(ERROR) << "DM_TABLE_STATUS failed for " << name;
+        return false;
+    }
+    if (io->flags & DM_BUFFER_FULL_FLAG) {
+        PLOG(ERROR) << "DM_TABLE_STATUS result for " << name << " was too large";
+        return false;
+    }
+
+    uint32_t cursor = io->data_start;
+    uint32_t data_end = std::min(io->data_size, uint32_t(sizeof(buffer)));
+    for (uint32_t i = 0; i < io->target_count; i++) {
+        if (cursor + sizeof(struct dm_target_spec) > data_end) {
+            break;
+        }
+        // After each dm_target_spec is a status string. spec->next is an
+        // offset from |io->data_start|, and we clamp it to the size of our
+        // buffer.
+        struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(buffer + cursor);
+        uint32_t data_offset = cursor + sizeof(dm_target_spec);
+        uint32_t next_cursor = std::min(io->data_start + spec->next, data_end);
+
+        std::string data;
+        if (next_cursor > data_offset) {
+            // Note: we use c_str() to eliminate any extra trailing 0s.
+            data = std::string(buffer + data_offset, next_cursor - data_offset).c_str();
+        }
+        table->emplace_back(*spec, data);
+        cursor = next_cursor;
+    }
+    return true;
+}
+
 // private methods of DeviceMapper
 void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
     CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp
index cb6f210..15c7ce1 100644
--- a/fs_mgr/libdm/dm_table.cpp
+++ b/fs_mgr/libdm/dm_table.cpp
@@ -23,6 +23,9 @@
 namespace dm {
 
 bool DmTable::AddTarget(std::unique_ptr<DmTarget>&& target) {
+    if (!target->Valid()) {
+        return false;
+    }
     targets_.push_back(std::move(target));
     return true;
 }
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 5934416..20b26df 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -18,6 +18,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/macros.h>
+#include <android-base/strings.h>
 
 #include <libdm/dm.h>
 
@@ -55,5 +56,60 @@
     return block_device_ + " " + std::to_string(physical_sector_);
 }
 
+DmTargetVerity::DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
+                               const std::string& block_device, const std::string& hash_device,
+                               uint32_t data_block_size, uint32_t hash_block_size,
+                               uint32_t num_data_blocks, uint32_t hash_start_block,
+                               const std::string& hash_algorithm, const std::string& root_digest,
+                               const std::string& salt)
+    : DmTarget(start, length), valid_(true) {
+    base_args_ = {
+            std::to_string(version),
+            block_device,
+            hash_device,
+            std::to_string(data_block_size),
+            std::to_string(hash_block_size),
+            std::to_string(num_data_blocks),
+            std::to_string(hash_start_block),
+            hash_algorithm,
+            root_digest,
+            salt,
+    };
+}
+
+void DmTargetVerity::UseFec(const std::string& device, uint32_t num_roots, uint32_t num_blocks,
+                            uint32_t start) {
+    optional_args_.emplace_back("use_fec_from_device");
+    optional_args_.emplace_back(device);
+    optional_args_.emplace_back("fec_roots");
+    optional_args_.emplace_back(std::to_string(num_roots));
+    optional_args_.emplace_back("fec_blocks");
+    optional_args_.emplace_back(std::to_string(num_blocks));
+    optional_args_.emplace_back("fec_start");
+    optional_args_.emplace_back(std::to_string(start));
+}
+
+void DmTargetVerity::SetVerityMode(const std::string& mode) {
+    if (mode != "restart_on_corruption" && mode != "ignore_corruption") {
+        LOG(ERROR) << "Unknown verity mode: " << mode;
+        valid_ = false;
+        return;
+    }
+    optional_args_.emplace_back(mode);
+}
+
+void DmTargetVerity::IgnoreZeroBlocks() {
+    optional_args_.emplace_back("ignore_zero_blocks");
+}
+
+std::string DmTargetVerity::GetParameterString() const {
+    std::string base = android::base::Join(base_args_, " ");
+    if (optional_args_.empty()) {
+        return base;
+    }
+    std::string optional = android::base::Join(optional_args_, " ");
+    return base + " " + std::to_string(optional_args_.size()) + " " + optional;
+}
+
 }  // namespace dm
 }  // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 67dc958..cc61917 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -160,7 +160,43 @@
         ASSERT_EQ(strncmp(sector, message2, sizeof(message2)), 0);
     }
 
+    // Test GetTableStatus.
+    DeviceMapper& dm = DeviceMapper::Instance();
+    vector<DeviceMapper::TargetInfo> targets;
+    ASSERT_TRUE(dm.GetTableStatus(dev.name(), &targets));
+    ASSERT_EQ(targets.size(), 2);
+    EXPECT_EQ(strcmp(targets[0].spec.target_type, "linear"), 0);
+    EXPECT_TRUE(targets[0].data.empty());
+    EXPECT_EQ(targets[0].spec.sector_start, 0);
+    EXPECT_EQ(targets[0].spec.length, 1);
+    EXPECT_EQ(strcmp(targets[1].spec.target_type, "linear"), 0);
+    EXPECT_TRUE(targets[1].data.empty());
+    EXPECT_EQ(targets[1].spec.sector_start, 1);
+    EXPECT_EQ(targets[1].spec.length, 1);
+
     // Normally the TestDevice destructor would delete this, but at least one
     // test should ensure that device deletion works.
     ASSERT_TRUE(dev.Destroy());
 }
+
+TEST(libdm, DmVerityArgsAvb2) {
+    std::string device = "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a";
+    std::string algorithm = "sha1";
+    std::string digest = "4be7e823b8c40f7bd5c8ccd5123f0722c5baca21";
+    std::string salt = "cc99f81ecb9484220a003b0719ee59dcf9be7e5d";
+
+    DmTargetVerity target(0, 10000, 1, device, device, 4096, 4096, 125961, 125961, algorithm,
+                          digest, salt);
+    target.UseFec(device, 2, 126955, 126955);
+    target.SetVerityMode("restart_on_corruption");
+    target.IgnoreZeroBlocks();
+
+    // Verity table from a walleye build.
+    std::string expected =
+            "1 /dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a "
+            "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a 4096 4096 125961 125961 sha1 "
+            "4be7e823b8c40f7bd5c8ccd5123f0722c5baca21 cc99f81ecb9484220a003b0719ee59dcf9be7e5d 10 "
+            "use_fec_from_device /dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a fec_roots "
+            "2 fec_blocks 126955 fec_start 126955 restart_on_corruption ignore_zero_blocks";
+    EXPECT_EQ(target.GetParameterString(), expected);
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 60bceed..e2bc729 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -26,6 +26,7 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include <android-base/logging.h>
@@ -117,6 +118,18 @@
         }
     }
 
+    // Query the status of a table, given a device name. The output vector will
+    // contain one TargetInfo for each target in the table. If the device does
+    // not exist, or there were too many targets, the call will fail and return
+    // false.
+    struct TargetInfo {
+        struct dm_target_spec spec;
+        std::string data;
+        TargetInfo(const struct dm_target_spec& spec, const std::string& data)
+            : spec(spec), data(data) {}
+    };
+    bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table);
+
   private:
     // Maximum possible device mapper targets registered in the kernel.
     // This is only used to read the list of targets from kernel so we allocate
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 31b6a70..d5974f4 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -21,6 +21,7 @@
 #include <stdint.h>
 
 #include <string>
+#include <vector>
 
 #include <android-base/logging.h>
 
@@ -69,6 +70,8 @@
     // must implement this, for it to be used on a device.
     std::string Serialize() const;
 
+    virtual bool Valid() const { return true; }
+
   protected:
     // Get the parameter string that is passed to the end of the dm_target_spec
     // for this target type.
@@ -96,12 +99,51 @@
 
     std::string name() const override { return "linear"; }
     std::string GetParameterString() const override;
+    const std::string& block_device() const { return block_device_; }
 
   private:
     std::string block_device_;
     uint64_t physical_sector_;
 };
 
+class DmTargetVerity final : public DmTarget {
+  public:
+    DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
+                   const std::string& block_device, const std::string& hash_device,
+                   uint32_t data_block_size, uint32_t hash_block_size, uint32_t num_data_blocks,
+                   uint32_t hash_start_block, const std::string& hash_algorithm,
+                   const std::string& root_digest, const std::string& salt);
+
+    void UseFec(const std::string& device, uint32_t num_roots, uint32_t num_blocks, uint32_t start);
+    void SetVerityMode(const std::string& mode);
+    void IgnoreZeroBlocks();
+
+    std::string name() const override { return "verity"; }
+    std::string GetParameterString() const override;
+    bool Valid() const override { return valid_; }
+
+  private:
+    std::vector<std::string> base_args_;
+    std::vector<std::string> optional_args_;
+    bool valid_;
+};
+
+// This is the same as DmTargetVerity, but the table may be specified as a raw
+// string. This code exists only for fs_mgr_verity and should be avoided. Use
+// DmTargetVerity for new code instead.
+class DmTargetVerityString final : public DmTarget {
+  public:
+    DmTargetVerityString(uint64_t start, uint64_t length, const std::string& target_string)
+        : DmTarget(start, length), target_string_(target_string) {}
+
+    std::string name() const override { return "verity"; }
+    std::string GetParameterString() const override { return target_string_; }
+    bool Valid() const override { return true; }
+
+  private:
+    std::string target_string_;
+};
+
 }  // namespace dm
 }  // namespace android
 
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index f7086a8..1434b21 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -24,6 +24,7 @@
     ],
     srcs: [
         "builder.cpp",
+        "images.cpp",
         "reader.cpp",
         "utility.cpp",
         "writer.cpp",
@@ -33,6 +34,7 @@
         "liblog",
         "libcrypto",
         "libcrypto_utils",
+        "libsparse",
     ],
     whole_static_libs: [
         "libext2_uuid",
@@ -46,12 +48,16 @@
 cc_test {
     name: "liblp_test",
     defaults: ["fs_mgr_defaults"],
+    cppflags: [
+        "-Wno-unused-parameter",
+    ],
     static_libs: [
         "libbase",
         "liblog",
         "libcrypto",
         "libcrypto_utils",
         "liblp",
+        "libfs_mgr",
     ],
     srcs: [
         "builder_test.cpp",
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 0e4838c..9d710f9 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -16,22 +16,49 @@
 
 #include "liblp/builder.h"
 
+#if defined(__linux__)
+#include <linux/fs.h>
+#endif
 #include <string.h>
+#include <sys/ioctl.h>
 
 #include <algorithm>
 
+#include <android-base/unique_fd.h>
 #include <uuid/uuid.h>
 
-#include "liblp/metadata_format.h"
+#include "liblp/liblp.h"
+#include "reader.h"
 #include "utility.h"
 
 namespace android {
 namespace fs_mgr {
 
-// Align a byte count up to the nearest 512-byte sector.
-template <typename T>
-static inline T AlignToSector(T value) {
-    return (value + (LP_SECTOR_SIZE - 1)) & ~T(LP_SECTOR_SIZE - 1);
+bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
+#if defined(__linux__)
+    android::base::unique_fd fd(open(block_device.c_str(), O_RDONLY));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed";
+        return false;
+    }
+    if (!GetDescriptorSize(fd, &device_info->size)) {
+        return false;
+    }
+    if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+        return false;
+    }
+    if (ioctl(fd, BLKALIGNOFF, &device_info->alignment_offset) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+        return false;
+    }
+    return true;
+#else
+    (void)block_device;
+    (void)device_info;
+    LERROR << __PRETTY_FUNCTION__ << ": Not supported on this operating system.";
+    return false;
+#endif
 }
 
 void LinearExtent::AddTo(LpMetadata* out) const {
@@ -56,7 +83,7 @@
 }
 
 void Partition::ShrinkTo(uint64_t requested_size) {
-    uint64_t aligned_size = AlignToSector(requested_size);
+    uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
     if (size_ <= aligned_size) {
         return;
     }
@@ -82,11 +109,28 @@
     DCHECK(size_ == requested_size);
 }
 
-std::unique_ptr<MetadataBuilder> MetadataBuilder::New(uint64_t blockdevice_size,
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& block_device,
+                                                      uint32_t slot_number) {
+    std::unique_ptr<LpMetadata> metadata = ReadMetadata(block_device.c_str(), slot_number);
+    if (!metadata) {
+        return nullptr;
+    }
+    std::unique_ptr<MetadataBuilder> builder = New(*metadata.get());
+    if (!builder) {
+        return nullptr;
+    }
+    BlockDeviceInfo device_info;
+    if (fs_mgr::GetBlockDeviceInfo(block_device, &device_info)) {
+        builder->set_block_device_info(device_info);
+    }
+    return builder;
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const BlockDeviceInfo& device_info,
                                                       uint32_t metadata_max_size,
                                                       uint32_t metadata_slot_count) {
     std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
-    if (!builder->Init(blockdevice_size, metadata_max_size, metadata_slot_count)) {
+    if (!builder->Init(device_info, metadata_max_size, metadata_slot_count)) {
         return nullptr;
     }
     return builder;
@@ -135,10 +179,13 @@
             }
         }
     }
+
+    device_info_.alignment = geometry_.alignment;
+    device_info_.alignment_offset = geometry_.alignment_offset;
     return true;
 }
 
-bool MetadataBuilder::Init(uint64_t blockdevice_size, uint32_t metadata_max_size,
+bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata_max_size,
                            uint32_t metadata_slot_count) {
     if (metadata_max_size < sizeof(LpMetadataHeader)) {
         LERROR << "Invalid metadata maximum size.";
@@ -150,7 +197,26 @@
     }
 
     // Align the metadata size up to the nearest sector.
-    metadata_max_size = AlignToSector(metadata_max_size);
+    metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
+
+    // Check that device properties are sane.
+    device_info_ = device_info;
+    if (device_info_.size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block device size must be a multiple of 512.";
+        return false;
+    }
+    if (device_info_.alignment_offset % LP_SECTOR_SIZE != 0) {
+        LERROR << "Alignment offset is not sector-aligned.";
+        return false;
+    }
+    if (device_info_.alignment % LP_SECTOR_SIZE != 0) {
+        LERROR << "Partition alignment is not sector-aligned.";
+        return false;
+    }
+    if (device_info_.alignment_offset > device_info_.alignment) {
+        LERROR << "Partition alignment offset is greater than its alignment.";
+        return false;
+    }
 
     // We reserve a geometry block (4KB) plus space for each copy of the
     // maximum size of a metadata blob. Then, we double that space since
@@ -158,20 +224,37 @@
     uint64_t reserved =
             LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
     uint64_t total_reserved = reserved * 2;
-
-    if (blockdevice_size < total_reserved || blockdevice_size - total_reserved < LP_SECTOR_SIZE) {
+    if (device_info_.size < total_reserved) {
         LERROR << "Attempting to create metadata on a block device that is too small.";
         return false;
     }
 
-    // The last sector is inclusive. We subtract one to make sure that logical
-    // partitions won't overlap with the same sector as the backup metadata,
-    // which could happen if the block device was not aligned to LP_SECTOR_SIZE.
-    geometry_.first_logical_sector = reserved / LP_SECTOR_SIZE;
-    geometry_.last_logical_sector = ((blockdevice_size - reserved) / LP_SECTOR_SIZE) - 1;
+    // Compute the first free sector, factoring in alignment.
+    uint64_t free_area = AlignTo(reserved, device_info_.alignment, device_info_.alignment_offset);
+    uint64_t first_sector = free_area / LP_SECTOR_SIZE;
+
+    // Compute the last free sector, which is inclusive. We subtract 1 to make
+    // sure that logical partitions won't overlap with the same sector as the
+    // backup metadata, which could happen if the block device was not aligned
+    // to LP_SECTOR_SIZE.
+    uint64_t last_sector = ((device_info_.size - reserved) / LP_SECTOR_SIZE) - 1;
+
+    // If this check fails, it means either (1) we did not have free space to
+    // allocate a single sector, or (2) we did, but the alignment was high
+    // enough to bump the first sector out of range. Either way, we cannot
+    // continue.
+    if (first_sector > last_sector) {
+        LERROR << "Not enough space to allocate any partition tables.";
+        return false;
+    }
+
+    geometry_.first_logical_sector = first_sector;
+    geometry_.last_logical_sector = last_sector;
     geometry_.metadata_max_size = metadata_max_size;
     geometry_.metadata_slot_count = metadata_slot_count;
-    DCHECK(geometry_.last_logical_sector >= geometry_.first_logical_sector);
+    geometry_.alignment = device_info_.alignment;
+    geometry_.alignment_offset = device_info_.alignment_offset;
+    geometry_.block_device_size = device_info_.size;
     return true;
 }
 
@@ -209,7 +292,7 @@
 
 bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_size) {
     // Align the space needed up to the nearest sector.
-    uint64_t aligned_size = AlignToSector(requested_size);
+    uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
     if (partition->size() >= aligned_size) {
         return true;
     }
@@ -259,10 +342,16 @@
             continue;
         }
 
+        uint64_t aligned = AlignSector(previous.end);
+        if (aligned >= current.start) {
+            // After alignment, this extent is not usable.
+            continue;
+        }
+
         // This gap is enough to hold the remainder of the space requested, so we
         // can allocate what we need and return.
-        if (current.start - previous.end >= sectors_needed) {
-            auto extent = std::make_unique<LinearExtent>(sectors_needed, previous.end);
+        if (current.start - aligned >= sectors_needed) {
+            auto extent = std::make_unique<LinearExtent>(sectors_needed, aligned);
             sectors_needed -= extent->num_sectors();
             new_extents.push_back(std::move(extent));
             break;
@@ -270,7 +359,7 @@
 
         // This gap is not big enough to fit the remainder of the space requested,
         // so consume the whole thing and keep looking for more.
-        auto extent = std::make_unique<LinearExtent>(current.start - previous.end, previous.end);
+        auto extent = std::make_unique<LinearExtent>(current.start - aligned, aligned);
         sectors_needed -= extent->num_sectors();
         new_extents.push_back(std::move(extent));
     }
@@ -286,8 +375,12 @@
         }
         DCHECK(first_sector <= geometry_.last_logical_sector);
 
+        // Note: After alignment, |first_sector| may be > the last usable sector.
+        first_sector = AlignSector(first_sector);
+
         // Note: the last usable sector is inclusive.
-        if (geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
+        if (first_sector > geometry_.last_logical_sector ||
+            geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
             LERROR << "Not enough free space to expand partition: " << partition->name();
             return false;
         }
@@ -351,5 +444,26 @@
     return (geometry_.last_logical_sector - geometry_.first_logical_sector + 1) * LP_SECTOR_SIZE;
 }
 
+uint64_t MetadataBuilder::AlignSector(uint64_t sector) {
+    // Note: when reading alignment info from the Kernel, we don't assume it
+    // is aligned to the sector size, so we round up to the nearest sector.
+    uint64_t lba = sector * LP_SECTOR_SIZE;
+    uint64_t aligned = AlignTo(lba, device_info_.alignment, device_info_.alignment_offset);
+    return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
+}
+
+void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info) {
+    device_info_.size = device_info.size;
+
+    // The kernel does not guarantee these values are present, so we only
+    // replace existing values if the new values are non-zero.
+    if (device_info.alignment) {
+        device_info_.alignment = device_info.alignment;
+    }
+    if (device_info.alignment_offset) {
+        device_info_.alignment_offset = device_info.alignment_offset;
+    }
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 2983f0f..b610fd4 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -16,6 +16,8 @@
 
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
+#include "fs_mgr.h"
+#include "utility.h"
 
 using namespace std;
 using namespace android::fs_mgr;
@@ -104,19 +106,9 @@
     static const uint32_t kMetadataSize = 1024;
     static const uint32_t kMetadataSlots = 2;
 
-    // If the disk size is not aligned to 512 bytes, make sure it still leaves
-    // space at the end for backup metadata, and that it doesn't overlap with
-    // the space for logical partitions.
     unique_ptr<MetadataBuilder> builder =
             MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
-    unique_ptr<LpMetadata> exported = builder->Export();
-    ASSERT_NE(exported, nullptr);
-
-    static const size_t kMetadataSpace =
-            (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE;
-    uint64_t space_at_end =
-            kDiskSize - (exported->geometry.last_logical_sector + 1) * LP_SECTOR_SIZE;
-    EXPECT_GE(space_at_end, kMetadataSpace);
+    ASSERT_EQ(builder, nullptr);
 }
 
 TEST(liblp, MetadataAlignment) {
@@ -127,6 +119,84 @@
     EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
 }
 
+TEST(liblp, InternalAlignment) {
+    // Test the metadata fitting within alignment.
+    BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->geometry.first_logical_sector, 1536);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
+
+    // Test a large alignment offset thrown in.
+    device_info.alignment_offset = 753664;
+    builder = MetadataBuilder::New(device_info, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+    exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->geometry.first_logical_sector, 1472);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
+
+    // Alignment offset without alignment doesn't mean anything.
+    device_info.alignment = 0;
+    builder = MetadataBuilder::New(device_info, 1024, 2);
+    ASSERT_EQ(builder, nullptr);
+
+    // Test a small alignment with an alignment offset.
+    device_info.alignment = 12 * 1024;
+    device_info.alignment_offset = 3 * 1024;
+    builder = MetadataBuilder::New(device_info, 16 * 1024, 2);
+    ASSERT_NE(builder, nullptr);
+    exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->geometry.first_logical_sector, 78);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
+
+    // Test a small alignment with no alignment offset.
+    device_info.alignment = 11 * 1024;
+    builder = MetadataBuilder::New(device_info, 16 * 1024, 2);
+    ASSERT_NE(builder, nullptr);
+    exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->geometry.first_logical_sector, 72);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
+}
+
+TEST(liblp, InternalPartitionAlignment) {
+    BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
+
+    Partition* a = builder->AddPartition("a", TEST_GUID, 0);
+    ASSERT_NE(a, nullptr);
+    Partition* b = builder->AddPartition("b", TEST_GUID2, 0);
+    ASSERT_NE(b, nullptr);
+
+    // Add a bunch of small extents to each, interleaving.
+    for (size_t i = 0; i < 10; i++) {
+        ASSERT_TRUE(builder->GrowPartition(a, a->size() + 4096));
+        ASSERT_TRUE(builder->GrowPartition(b, b->size() + 4096));
+    }
+    EXPECT_EQ(a->size(), 40960);
+    EXPECT_EQ(b->size(), 40960);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    // Check that each starting sector is aligned.
+    for (const auto& extent : exported->extents) {
+        ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
+        EXPECT_EQ(extent.num_sectors, 8);
+
+        uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
+        uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
+        EXPECT_EQ(lba, aligned_lba);
+    }
+
+    // Sanity check one extent.
+    EXPECT_EQ(exported->extents.back().target_data, 30656);
+}
+
 TEST(liblp, UseAllDiskSpace) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
     EXPECT_EQ(builder->AllocatableSpace(), 1036288);
@@ -312,15 +382,72 @@
     static const size_t kMetadataSize = 64 * 1024;
 
     // No space to store metadata + geometry.
-    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize, kMetadataSize, 1);
+    BlockDeviceInfo device_info(kDiskSize, 0, 0);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
     EXPECT_EQ(builder, nullptr);
 
     // No space to store metadata + geometry + one free sector.
-    builder = MetadataBuilder::New(kDiskSize + LP_METADATA_GEOMETRY_SIZE * 2, kMetadataSize, 1);
+    device_info.size += LP_METADATA_GEOMETRY_SIZE * 2;
+    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
     EXPECT_EQ(builder, nullptr);
 
     // Space for metadata + geometry + one free sector.
-    builder = MetadataBuilder::New(kDiskSize + LP_METADATA_GEOMETRY_SIZE * 2 + LP_SECTOR_SIZE,
-                                   kMetadataSize, 1);
+    device_info.size += LP_SECTOR_SIZE;
+    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
     EXPECT_NE(builder, nullptr);
+
+    // Test with alignment.
+    device_info.alignment = 131072;
+    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+    EXPECT_EQ(builder, nullptr);
+
+    device_info.alignment = 0;
+    device_info.alignment_offset = 32768 - LP_SECTOR_SIZE;
+    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+    EXPECT_EQ(builder, nullptr);
+}
+
+TEST(liblp, block_device_info) {
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                               fs_mgr_free_fstab);
+    ASSERT_NE(fstab, nullptr);
+
+    // This should read from the "super" partition once we have a well-defined
+    // way to access it.
+    struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), "/data");
+    ASSERT_NE(rec, nullptr);
+
+    BlockDeviceInfo device_info;
+    ASSERT_TRUE(GetBlockDeviceInfo(rec->blk_device, &device_info));
+
+    // Sanity check that the device doesn't give us some weird inefficient
+    // alignment.
+    ASSERT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
+    ASSERT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
+    ASSERT_LE(device_info.alignment_offset, INT_MAX);
+
+    // Having an alignment offset > alignment doesn't really make sense.
+    ASSERT_LT(device_info.alignment_offset, device_info.alignment);
+}
+
+TEST(liblp, UpdateBlockDeviceInfo) {
+    BlockDeviceInfo device_info(1024 * 1024, 4096, 1024);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    EXPECT_EQ(builder->block_device_info().size, device_info.size);
+    EXPECT_EQ(builder->block_device_info().alignment, device_info.alignment);
+    EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
+
+    device_info.alignment = 0;
+    device_info.alignment_offset = 2048;
+    builder->set_block_device_info(device_info);
+    EXPECT_EQ(builder->block_device_info().alignment, 4096);
+    EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
+
+    device_info.alignment = 8192;
+    device_info.alignment_offset = 0;
+    builder->set_block_device_info(device_info);
+    EXPECT_EQ(builder->block_device_info().alignment, 8192);
+    EXPECT_EQ(builder->block_device_info().alignment_offset, 2048);
 }
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
new file mode 100644
index 0000000..a361a5d
--- /dev/null
+++ b/fs_mgr/liblp/images.cpp
@@ -0,0 +1,355 @@
+/*
+ * 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 "images.h"
+
+#include <limits.h>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <sparse/sparse.h>
+
+#include "reader.h"
+#include "utility.h"
+#include "writer.h"
+
+namespace android {
+namespace fs_mgr {
+
+std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
+    LpMetadataGeometry geometry;
+    if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+        return nullptr;
+    }
+    if (SeekFile64(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE;
+        return nullptr;
+    }
+    std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
+    if (!metadata) {
+        return nullptr;
+    }
+    metadata->geometry = geometry;
+    return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
+    android::base::unique_fd fd(open(file, O_RDONLY));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+        return nullptr;
+    }
+    return ReadFromImageFile(fd);
+}
+
+bool WriteToImageFile(int fd, const LpMetadata& input) {
+    std::string geometry = SerializeGeometry(input.geometry);
+    std::string metadata = SerializeMetadata(input);
+
+    std::string everything = geometry + metadata;
+
+    if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed";
+        return false;
+    }
+    return true;
+}
+
+bool WriteToImageFile(const char* file, const LpMetadata& input) {
+    android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+        return false;
+    }
+    return WriteToImageFile(fd, input);
+}
+
+// We use an object to build the sparse file since it requires that data
+// pointers be held alive until the sparse file is destroyed. It's easier
+// to do this when the data pointers are all in one place.
+class SparseBuilder {
+  public:
+    SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
+                  const std::map<std::string, std::string>& images);
+
+    bool Build();
+    bool Export(const char* file);
+    bool IsValid() const { return file_ != nullptr; }
+
+  private:
+    bool AddData(const std::string& blob, uint64_t sector);
+    bool AddPartitionImage(const LpMetadataPartition& partition, const std::string& file);
+    int OpenImageFile(const std::string& file);
+    bool SectorToBlock(uint64_t sector, uint32_t* block);
+
+    const LpMetadata& metadata_;
+    const LpMetadataGeometry& geometry_;
+    uint32_t block_size_;
+    std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_;
+    std::string primary_blob_;
+    std::string backup_blob_;
+    std::map<std::string, std::string> images_;
+    std::vector<android::base::unique_fd> temp_fds_;
+};
+
+SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
+                             const std::map<std::string, std::string>& images)
+    : metadata_(metadata),
+      geometry_(metadata.geometry),
+      block_size_(block_size),
+      file_(sparse_file_new(block_size_, geometry_.block_device_size), sparse_file_destroy),
+      images_(images) {}
+
+bool SparseBuilder::Export(const char* file) {
+    android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+    if (fd < 0) {
+        PERROR << "open failed: " << file;
+        return false;
+    }
+    // No gzip compression; sparseify; no checksum.
+    int ret = sparse_file_write(file_.get(), fd, false, true, false);
+    if (ret != 0) {
+        LERROR << "sparse_file_write failed (error code " << ret << ")";
+        return false;
+    }
+    return true;
+}
+
+bool SparseBuilder::AddData(const std::string& blob, uint64_t sector) {
+    uint32_t block;
+    if (!SectorToBlock(sector, &block)) {
+        return false;
+    }
+    void* data = const_cast<char*>(blob.data());
+    int ret = sparse_file_add_data(file_.get(), data, blob.size(), block);
+    if (ret != 0) {
+        LERROR << "sparse_file_add_data failed (error code " << ret << ")";
+        return false;
+    }
+    return true;
+}
+
+bool SparseBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
+    // The caller must ensure that the metadata has an alignment that is a
+    // multiple of the block size. liblp will take care of the rest, ensuring
+    // that all partitions are on an aligned boundary. Therefore all writes
+    // should be block-aligned, and if they are not, the table was misconfigured.
+    // Note that the default alignment is 1MiB, which is a multiple of the
+    // default block size (4096).
+    if ((sector * LP_SECTOR_SIZE) % block_size_ != 0) {
+        LERROR << "sector " << sector << " is not aligned to block size " << block_size_;
+        return false;
+    }
+    *block = (sector * LP_SECTOR_SIZE) / block_size_;
+    return true;
+}
+
+bool SparseBuilder::Build() {
+    std::string geometry_blob = SerializeGeometry(geometry_);
+    std::string metadata_blob = SerializeMetadata(metadata_);
+    metadata_blob.resize(geometry_.metadata_max_size);
+
+    std::string all_metadata;
+    for (size_t i = 0; i < geometry_.metadata_slot_count; i++) {
+        all_metadata += metadata_blob;
+    }
+
+    // Metadata immediately follows geometry, and we write the same metadata
+    // to all slots. Note that we don't bother trying to write skip chunks
+    // here since it's a small amount of data.
+    primary_blob_ = geometry_blob + all_metadata;
+    if (!AddData(primary_blob_, 0)) {
+        return false;
+    }
+
+    for (const auto& partition : metadata_.partitions) {
+        auto iter = images_.find(GetPartitionName(partition));
+        if (iter == images_.end()) {
+            continue;
+        }
+        if (!AddPartitionImage(partition, iter->second)) {
+            return false;
+        }
+        images_.erase(iter);
+    }
+
+    if (!images_.empty()) {
+        LERROR << "Partition image was specified but no partition was found.";
+        return false;
+    }
+
+    // The backup area contains all metadata slots, and then geometry. Similar
+    // to before we write the metadata to every slot.
+    int64_t backup_offset = GetBackupMetadataOffset(geometry_, 0);
+    uint64_t backups_start = geometry_.block_device_size + backup_offset;
+    uint64_t backup_sector = backups_start / LP_SECTOR_SIZE;
+
+    backup_blob_ = all_metadata + geometry_blob;
+    if (!AddData(backup_blob_, backup_sector)) {
+        return false;
+    }
+    return true;
+}
+
+static inline bool HasFillValue(uint32_t* buffer, size_t count) {
+    uint32_t fill_value = buffer[0];
+    for (size_t i = 1; i < count; i++) {
+        if (fill_value != buffer[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool SparseBuilder::AddPartitionImage(const LpMetadataPartition& partition,
+                                      const std::string& file) {
+    if (partition.num_extents != 1) {
+        LERROR << "Partition for new tables should not have more than one extent: "
+               << GetPartitionName(partition);
+        return false;
+    }
+
+    const LpMetadataExtent& extent = metadata_.extents[partition.first_extent_index];
+    if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
+        LERROR << "Partition should only have linear extents: " << GetPartitionName(partition);
+        return false;
+    }
+
+    int fd = OpenImageFile(file);
+    if (fd < 0) {
+        LERROR << "Could not open image for partition: " << GetPartitionName(partition);
+        return false;
+    }
+
+    // Make sure the image does not exceed the partition size.
+    uint64_t file_length;
+    if (!GetDescriptorSize(fd, &file_length)) {
+        LERROR << "Could not compute image size";
+        return false;
+    }
+    if (file_length > extent.num_sectors * LP_SECTOR_SIZE) {
+        LERROR << "Image for partition '" << GetPartitionName(partition)
+               << "' is greater than its size";
+        return false;
+    }
+    if (SeekFile64(fd, 0, SEEK_SET)) {
+        PERROR << "lseek failed";
+        return false;
+    }
+
+    uint32_t output_block;
+    if (!SectorToBlock(extent.target_data, &output_block)) {
+        return false;
+    }
+
+    uint64_t pos = 0;
+    uint64_t remaining = file_length;
+    while (remaining) {
+        uint32_t buffer[block_size_ / sizeof(uint32_t)];
+        size_t read_size = remaining >= sizeof(buffer) ? sizeof(buffer) : size_t(remaining);
+        if (!android::base::ReadFully(fd, buffer, sizeof(buffer))) {
+            PERROR << "read failed";
+            return false;
+        }
+        if (read_size != sizeof(buffer) || !HasFillValue(buffer, read_size / sizeof(uint32_t))) {
+            int rv = sparse_file_add_fd(file_.get(), fd, pos, read_size, output_block);
+            if (rv) {
+                LERROR << "sparse_file_add_fd failed with code: " << rv;
+                return false;
+            }
+        } else {
+            int rv = sparse_file_add_fill(file_.get(), buffer[0], read_size, output_block);
+            if (rv) {
+                LERROR << "sparse_file_add_fill failed with code: " << rv;
+                return false;
+            }
+        }
+        pos += read_size;
+        remaining -= read_size;
+        output_block++;
+    }
+
+    return true;
+}
+
+int SparseBuilder::OpenImageFile(const std::string& file) {
+    android::base::unique_fd source_fd(open(file.c_str(), O_RDONLY));
+    if (source_fd < 0) {
+        PERROR << "open image file failed: " << file;
+        return -1;
+    }
+
+    std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> source(
+            sparse_file_import(source_fd, true, true), sparse_file_destroy);
+    if (!source) {
+        int fd = source_fd.get();
+        temp_fds_.push_back(std::move(source_fd));
+        return fd;
+    }
+
+    char temp_file[PATH_MAX];
+    snprintf(temp_file, sizeof(temp_file), "%s/imageXXXXXX", P_tmpdir);
+    android::base::unique_fd temp_fd(mkstemp(temp_file));
+    if (temp_fd < 0) {
+        PERROR << "mkstemp failed";
+        return -1;
+    }
+    if (unlink(temp_file) < 0) {
+        PERROR << "unlink failed";
+        return -1;
+    }
+
+    // We temporarily unsparse the file, rather than try to merge its chunks.
+    int rv = sparse_file_write(source.get(), temp_fd, false, false, false);
+    if (rv) {
+        LERROR << "sparse_file_write failed with code: " << rv;
+        return -1;
+    }
+    temp_fds_.push_back(std::move(temp_fd));
+    return temp_fds_.back().get();
+}
+
+bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+                       const std::map<std::string, std::string>& images) {
+    if (block_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
+        return false;
+    }
+    if (metadata.geometry.block_device_size % block_size != 0) {
+        LERROR << "Device size must be a multiple of the block size, " << block_size;
+        return false;
+    }
+    uint64_t num_blocks = metadata.geometry.block_device_size % block_size;
+    if (num_blocks >= UINT_MAX) {
+        // libsparse counts blocks in unsigned 32-bit integers, so we check to
+        // make sure we're not going to overflow.
+        LERROR << "Block device is too large to encode with libsparse.";
+        return false;
+    }
+
+    SparseBuilder builder(metadata, block_size, images);
+    if (!builder.IsValid()) {
+        LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size;
+        return false;
+    }
+    if (!builder.Build()) {
+        return false;
+    }
+    return builder.Export(file);
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
new file mode 100644
index 0000000..3a999b8
--- /dev/null
+++ b/fs_mgr/liblp/images.h
@@ -0,0 +1,29 @@
+/*
+ * 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 <liblp/liblp.h>
+
+namespace android {
+namespace fs_mgr {
+
+// Helper function to serialize geometry and metadata to a normal file, for
+// flashing or debugging.
+std::unique_ptr<LpMetadata> ReadFromImageFile(int fd);
+bool WriteToImageFile(const char* file, const LpMetadata& metadata);
+bool WriteToImageFile(int fd, const LpMetadata& metadata);
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 671a3bd..8bde313 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -23,13 +23,31 @@
 #include <map>
 #include <memory>
 
-#include "metadata_format.h"
+#include "liblp.h"
 
 namespace android {
 namespace fs_mgr {
 
 class LinearExtent;
 
+// By default, partitions are aligned on a 1MiB boundary.
+static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
+
+struct BlockDeviceInfo {
+    BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0) {}
+    BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset)
+        : size(size), alignment(alignment), alignment_offset(alignment_offset) {}
+    // Size of the block device, in bytes.
+    uint64_t size;
+    // Optimal target alignment, in bytes. Partition extents will be aligned to
+    // this value by default. This value must be 0 or a multiple of 512.
+    uint32_t alignment;
+    // Alignment offset to parent device (if any), in bytes. The sector at
+    // |alignment_offset| on the target device is correctly aligned on its
+    // parent device. This value must be 0 or a multiple of 512.
+    uint32_t alignment_offset;
+};
+
 // Abstraction around dm-targets that can be encoded into logical partition tables.
 class Extent {
   public:
@@ -107,14 +125,29 @@
     // If the parameters would yield invalid metadata, nullptr is returned. This
     // could happen if the block device size is too small to store the metadata
     // and backup copies.
-    static std::unique_ptr<MetadataBuilder> New(uint64_t blockdevice_size,
+    static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
                                                 uint32_t metadata_max_size,
                                                 uint32_t metadata_slot_count);
 
+    // Import an existing table for modification. This reads metadata off the
+    // given block device and imports it. It also adjusts alignment information
+    // based on run-time values in the operating system.
+    static std::unique_ptr<MetadataBuilder> New(const std::string& block_device,
+                                                uint32_t slot_number);
+
     // Import an existing table for modification. If the table is not valid, for
     // example it contains duplicate partition names, then nullptr is returned.
+    // This method is for testing or changing off-line tables.
     static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
 
+    // Wrapper around New() with a BlockDeviceInfo that only specifies a device
+    // size. This is a convenience method for tests.
+    static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size,
+                                                uint32_t metadata_slot_count) {
+        BlockDeviceInfo device_info(blockdev_size, 0, 0);
+        return New(device_info, metadata_max_size, metadata_slot_count);
+    }
+
     // Export metadata so it can be serialized to an image, to disk, or mounted
     // via device-mapper.
     std::unique_ptr<LpMetadata> Export();
@@ -156,16 +189,28 @@
     // Amount of space that can be allocated to logical partitions.
     uint64_t AllocatableSpace() const;
 
+    // Merge new block device information into previous values. Alignment values
+    // are only overwritten if the new values are non-zero.
+    void set_block_device_info(const BlockDeviceInfo& device_info);
+    const BlockDeviceInfo& block_device_info() const { return device_info_; }
+
   private:
     MetadataBuilder();
-    bool Init(uint64_t blockdevice_size, uint32_t metadata_max_size, uint32_t metadata_slot_count);
+    bool Init(const BlockDeviceInfo& info, uint32_t metadata_max_size, uint32_t metadata_slot_count);
     bool Init(const LpMetadata& metadata);
 
+    uint64_t AlignSector(uint64_t sector);
+
     LpMetadataGeometry geometry_;
     LpMetadataHeader header_;
     std::vector<std::unique_ptr<Partition>> partitions_;
+    BlockDeviceInfo device_info_;
 };
 
+// Read BlockDeviceInfo for a given block device. This always returns false
+// for non-Linux operating systems.
+bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info);
+
 }  // namespace fs_mgr
 }  // namespace android
 
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
new file mode 100644
index 0000000..627aa8c
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -0,0 +1,78 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef LIBLP_LIBLP_H
+#define LIBLP_LIBLP_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Helper structure for easily interpreting deserialized metadata, or
+// re-serializing metadata.
+struct LpMetadata {
+    LpMetadataGeometry geometry;
+    LpMetadataHeader header;
+    std::vector<LpMetadataPartition> partitions;
+    std::vector<LpMetadataExtent> extents;
+};
+
+// Place an initial partition table on the device. This will overwrite the
+// existing geometry, and should not be used for normal partition table
+// updates. False can be returned if the geometry is incompatible with the
+// block device or an I/O error occurs.
+bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                         uint32_t slot_number);
+
+// Update the partition table for a given metadata slot number. False is
+// returned if an error occurs, which can include:
+//  - Invalid slot number.
+//  - I/O error.
+//  - Corrupt or missing metadata geometry on disk.
+//  - Incompatible geometry.
+bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                          uint32_t slot_number);
+
+// Read logical partition metadata from its predetermined location on a block
+// device. If readback fails, we also attempt to load from a backup copy.
+std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
+
+// Read/Write logical partition metadata to an image file, for diagnostics or
+// flashing.
+bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+                       const std::map<std::string, std::string>& images);
+bool WriteToImageFile(const char* file, const LpMetadata& metadata);
+std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
+
+// Helper to extract safe C++ strings from partition info.
+std::string GetPartitionName(const LpMetadataPartition& partition);
+std::string GetPartitionGuid(const LpMetadataPartition& partition);
+
+// Helper to return a slot number for a slot suffix.
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif  // LIBLP_LIBLP_H
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 8522435..e1323e1 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -67,7 +67,7 @@
  *     | Geometry Backup    |
  *     +--------------------+
  */
-#define LP_METADATA_PARTITION_NAME "android"
+#define LP_METADATA_PARTITION_NAME "super"
 
 /* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
 #define LP_SECTOR_SIZE 512
@@ -86,7 +86,9 @@
     /*  8: SHA256 checksum of this struct, with this field set to 0. */
     uint8_t checksum[32];
 
-    /* 40: Maximum amount of space a single copy of the metadata can use. */
+    /* 40: Maximum amount of space a single copy of the metadata can use. This
+     * must be a multiple of LP_SECTOR_SIZE.
+     */
     uint32_t metadata_max_size;
 
     /* 44: Number of copies of the metadata to keep. For A/B devices, this
@@ -107,6 +109,33 @@
      * backup geometry block at the very end.
      */
     uint64_t last_logical_sector;
+
+    /* 64: Alignment for defining partitions or partition extents. For example,
+     * an alignment of 1MiB will require that all partitions have a size evenly
+     * divisible by 1MiB, and that the smallest unit the partition can grow by
+     * is 1MiB.
+     *
+     * Alignment is normally determined at runtime when growing or adding
+     * partitions. If for some reason the alignment cannot be determined, then
+     * this predefined alignment in the geometry is used instead. By default
+     * it is set to 1MiB.
+     */
+    uint32_t alignment;
+
+    /* 68: Alignment offset for "stacked" devices. For example, if the "super"
+     * partition itself is not aligned within the parent block device's
+     * partition table, then we adjust for this in deciding where to place
+     * |first_logical_sector|.
+     *
+     * Similar to |alignment|, this will be derived from the operating system.
+     * If it cannot be determined, it is assumed to be 0.
+     */
+    uint32_t alignment_offset;
+
+    /* 72: Block device size, as specified when the metadata was created. This
+     * can be used to verify the geometry against a target device.
+     */
+    uint64_t block_device_size;
 } __attribute__((packed)) LpMetadataGeometry;
 
 /* The logical partition metadata has a number of tables; they are described
@@ -240,28 +269,4 @@
 } /* extern "C" */
 #endif
 
-#ifdef __cplusplus
-namespace android {
-namespace fs_mgr {
-
-// Helper structure for easily interpreting deserialized metadata, or
-// re-serializing metadata.
-struct LpMetadata {
-    LpMetadataGeometry geometry;
-    LpMetadataHeader header;
-    std::vector<LpMetadataPartition> partitions;
-    std::vector<LpMetadataExtent> extents;
-};
-
-// Helper to extract safe C++ strings from partition info.
-std::string GetPartitionName(const LpMetadataPartition& partition);
-std::string GetPartitionGuid(const LpMetadataPartition& partition);
-
-// Helper to return a slot number for a slot suffix.
-uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
-
-}  // namespace fs_mgr
-}  // namespace android
-#endif
-
 #endif /* LOGICAL_PARTITION_METADATA_FORMAT_H_ */
diff --git a/fs_mgr/liblp/include/liblp/reader.h b/fs_mgr/liblp/include/liblp/reader.h
deleted file mode 100644
index 982fe65..0000000
--- a/fs_mgr/liblp/include/liblp/reader.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LIBLP_READER_H_
-#define LIBLP_READER_H_
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "metadata_format.h"
-
-namespace android {
-namespace fs_mgr {
-
-// Read logical partition metadata from its predetermined location on a block
-// device. If readback fails, we also attempt to load from a backup copy.
-std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
-std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
-
-// Read and validate the logical partition geometry from a block device.
-bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry);
-bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
-
-// Read logical partition metadata from an image file that was created with
-// WriteToImageFile().
-std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
-std::unique_ptr<LpMetadata> ReadFromImageFile(int fd);
-
-}  // namespace fs_mgr
-}  // namespace android
-
-#endif /* LIBLP_READER_H_ */
diff --git a/fs_mgr/liblp/include/liblp/writer.h b/fs_mgr/liblp/include/liblp/writer.h
deleted file mode 100644
index efa409d..0000000
--- a/fs_mgr/liblp/include/liblp/writer.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LIBLP_WRITER_H
-#define LIBLP_WRITER_H
-
-#include "metadata_format.h"
-
-namespace android {
-namespace fs_mgr {
-
-// When flashing the initial logical partition layout, we also write geometry
-// information at the start and end of the big physical partition. This helps
-// locate metadata and backup metadata in the case of corruption or a failed
-// update. For normal changes to the metadata, we never modify the geometry.
-enum class SyncMode {
-    // Write geometry information.
-    Flash,
-    // Normal update of a single slot.
-    Update
-};
-
-// Write the given partition table to the given block device, writing only
-// copies according to the given sync mode.
-//
-// This will perform some verification, such that the device has enough space
-// to store the metadata as well as all of its extents.
-//
-// The slot number indicates which metadata slot to use.
-bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
-                         uint32_t slot_number);
-bool WritePartitionTable(int fd, const LpMetadata& metadata, SyncMode sync_mode,
-                         uint32_t slot_number);
-
-// Helper function to serialize geometry and metadata to a normal file, for
-// flashing or debugging.
-bool WriteToImageFile(const char* file, const LpMetadata& metadata);
-bool WriteToImageFile(int fd, const LpMetadata& metadata);
-
-}  // namespace fs_mgr
-}  // namespace android
-
-#endif /* LIBLP_WRITER_H */
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 2595654..bbbedc7 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -23,10 +23,11 @@
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
-#include <liblp/reader.h>
-#include <liblp/writer.h>
 
+#include "images.h"
+#include "reader.h"
 #include "utility.h"
+#include "writer.h"
 
 using namespace std;
 using namespace android::fs_mgr;
@@ -102,7 +103,7 @@
     if (!exported) {
         return {};
     }
-    if (!WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0)) {
+    if (!FlashPartitionTable(fd, *exported.get(), 0)) {
         return {};
     }
     return fd;
@@ -131,7 +132,7 @@
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
-    EXPECT_FALSE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
+    EXPECT_FALSE(FlashPartitionTable(fd, *exported.get(), 0));
 }
 
 // Test the basics of flashing a partition and reading it back.
@@ -146,7 +147,7 @@
     // Export and flash.
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
+    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get(), 0));
 
     // Read back. Note that some fields are only filled in during
     // serialization, so exported and imported will not be identical. For
@@ -195,7 +196,7 @@
 
     // Change the name before writing to the next slot.
     strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
-    ASSERT_TRUE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
 
     // Read back the original slot, make sure it hasn't changed.
     imported = ReadMetadata(fd, 0);
@@ -231,7 +232,7 @@
     unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
     ASSERT_NE(metadata, nullptr);
     for (uint32_t i = 1; i < kMetadataSlots; i++) {
-        ASSERT_TRUE(WritePartitionTable(fd, *metadata.get(), SyncMode::Update, i));
+        ASSERT_TRUE(UpdatePartitionTable(fd, *metadata.get(), i));
     }
 
     // Verify that we can't read unavailable slots.
@@ -246,25 +247,25 @@
 
     unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
     ASSERT_NE(imported, nullptr);
-    ASSERT_TRUE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
 
     imported->geometry.metadata_max_size += LP_SECTOR_SIZE;
-    ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
 
     imported = ReadMetadata(fd, 0);
     ASSERT_NE(imported, nullptr);
     imported->geometry.metadata_slot_count++;
-    ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
 
     imported = ReadMetadata(fd, 0);
     ASSERT_NE(imported, nullptr);
     imported->geometry.first_logical_sector++;
-    ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
 
     imported = ReadMetadata(fd, 0);
     ASSERT_NE(imported, nullptr);
     imported->geometry.last_logical_sector--;
-    ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
 }
 
 // Test that changing one bit of metadata is enough to break the checksum.
@@ -353,8 +354,8 @@
     ASSERT_GE(fd, 0);
 
     // Check that we are able to write our table.
-    ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
-    ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Update, 1));
+    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get(), 0));
+    ASSERT_TRUE(UpdatePartitionTable(fd, *exported.get(), 1));
 
     // Check that adding one more partition overflows the metadata allotment.
     partition = builder->AddPartition("final", TEST_GUID, LP_PARTITION_ATTR_NONE);
@@ -364,7 +365,7 @@
     ASSERT_NE(exported, nullptr);
 
     // The new table should be too large to be written.
-    ASSERT_FALSE(WritePartitionTable(fd, *exported.get(), SyncMode::Update, 1));
+    ASSERT_FALSE(UpdatePartitionTable(fd, *exported.get(), 1));
 
     // Check that the first and last logical sectors weren't touched when we
     // wrote this almost-full metadata.
@@ -393,3 +394,130 @@
     unique_ptr<LpMetadata> imported = ReadFromImageFile(fd);
     ASSERT_NE(imported, nullptr);
 }
+
+class BadWriter {
+  public:
+    // When requested, write garbage instead of the requested bytes, then
+    // return false.
+    bool operator()(int fd, const std::string& blob) {
+        write_count_++;
+        if (write_count_ == fail_on_write_) {
+            std::unique_ptr<char[]> new_data = std::make_unique<char[]>(blob.size());
+            memset(new_data.get(), 0xe5, blob.size());
+            EXPECT_TRUE(android::base::WriteFully(fd, new_data.get(), blob.size()));
+            return false;
+        } else {
+            if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+                return false;
+            }
+            return fail_after_write_ != write_count_;
+        }
+    }
+    void Reset() {
+        fail_on_write_ = 0;
+        fail_after_write_ = 0;
+        write_count_ = 0;
+    }
+    void FailOnWrite(int number) {
+        Reset();
+        fail_on_write_ = number;
+    }
+    void FailAfterWrite(int number) {
+        Reset();
+        fail_after_write_ = number;
+    }
+
+  private:
+    int fail_on_write_ = 0;
+    int fail_after_write_ = 0;
+    int write_count_ = 0;
+};
+
+// Test that an interrupted flash operation on the "primary" copy of metadata
+// is not fatal.
+TEST(liblp, UpdatePrimaryMetadataFailure) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    BadWriter writer;
+
+    // Read and write it back.
+    writer.FailOnWrite(1);
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+
+    // We should still be able to read the backup copy.
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+
+    // Flash again, this time fail the backup copy. We should still be able
+    // to read the primary.
+    writer.FailOnWrite(3);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+}
+
+// Test that an interrupted flash operation on the "backup" copy of metadata
+// is not fatal.
+TEST(liblp, UpdateBackupMetadataFailure) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    BadWriter writer;
+
+    // Read and write it back.
+    writer.FailOnWrite(2);
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+
+    // We should still be able to read the primary copy.
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+
+    // Flash again, this time fail the primary copy. We should still be able
+    // to read the primary.
+    writer.FailOnWrite(2);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+}
+
+// Test that an interrupted write *in between* writing metadata will read
+// the correct metadata copy. The primary is always considered newer than
+// the backup.
+TEST(liblp, UpdateMetadataCleanFailure) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    BadWriter writer;
+
+    // Change the name of the existing partition.
+    unique_ptr<LpMetadata> new_table = ReadMetadata(fd, 0);
+    ASSERT_NE(new_table, nullptr);
+    ASSERT_GE(new_table->partitions.size(), 1);
+    new_table->partitions[0].name[0]++;
+
+    // Flash it, but fail to write the backup copy.
+    writer.FailAfterWrite(2);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+
+    // When we read back, we should get the updated primary copy.
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_GE(new_table->partitions.size(), 1);
+    ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
+
+    // Flash again. After, the backup and primary copy should be coherent.
+    // Note that the sync step should have used the primary to sync, not
+    // the backup.
+    writer.Reset();
+    ASSERT_TRUE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_GE(new_table->partitions.size(), 1);
+    ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
+}
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 7938186..117da59 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "liblp/reader.h"
+#include "reader.h"
 
 #include <stddef.h>
 #include <stdlib.h>
@@ -41,11 +41,18 @@
         LERROR << "Logical partition metadata has invalid geometry magic signature.";
         return false;
     }
+    // Reject if the struct size is larger than what we compiled. This is so we
+    // can compute a checksum with the |struct_size| field rather than using
+    // sizeof.
+    if (geometry->struct_size > sizeof(LpMetadataGeometry)) {
+        LERROR << "Logical partition metadata has unrecognized fields.";
+        return false;
+    }
     // Recompute and check the CRC32.
     {
         LpMetadataGeometry temp = *geometry;
         memset(&temp.checksum, 0, sizeof(temp.checksum));
-        SHA256(&temp, sizeof(temp), temp.checksum);
+        SHA256(&temp, temp.struct_size, temp.checksum);
         if (memcmp(temp.checksum, geometry->checksum, sizeof(temp.checksum)) != 0) {
             LERROR << "Logical partition metadata has invalid geometry checksum.";
             return false;
@@ -61,6 +68,10 @@
         LERROR << "Logical partition metadata has invalid slot count.";
         return false;
     }
+    if (geometry->metadata_max_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Metadata max size is not sector-aligned.";
+        return false;
+    }
 
     // Check that the metadata area and logical partition areas don't overlap.
     int64_t end_of_metadata =
@@ -104,16 +115,6 @@
     return ParseGeometry(buffer.get(), geometry);
 }
 
-// Helper function to read geometry from a device without an open descriptor.
-bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry) {
-    android::base::unique_fd fd(open(block_device, O_RDONLY));
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
-        return false;
-    }
-    return ReadLogicalPartitionGeometry(fd, geometry);
-}
-
 static bool ValidateTableBounds(const LpMetadataHeader& header,
                                 const LpMetadataTableDescriptor& table) {
     if (table.offset > header.tables_size) {
@@ -168,11 +169,9 @@
     return true;
 }
 
-using ReadMetadataFn = std::function<bool(void* buffer, size_t num_bytes)>;
-
 // Parse and validate all metadata at the current position in the given file
 // descriptor.
-static std::unique_ptr<LpMetadata> ParseMetadata(int fd) {
+std::unique_ptr<LpMetadata> ParseMetadata(int fd) {
     // First read and validate the header.
     std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
     if (!android::base::ReadFully(fd, &metadata->header, sizeof(metadata->header))) {
@@ -236,6 +235,26 @@
     return metadata;
 }
 
+std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
+                                                uint32_t slot_number) {
+    int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
+    if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+        return nullptr;
+    }
+    return ParseMetadata(fd);
+}
+
+std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
+                                               uint32_t slot_number) {
+    int64_t offset = GetBackupMetadataOffset(geometry, slot_number);
+    if (SeekFile64(fd, offset, SEEK_END) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+        return nullptr;
+    }
+    return ParseMetadata(fd);
+}
+
 std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
     LpMetadataGeometry geometry;
     if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
@@ -247,24 +266,11 @@
         return nullptr;
     }
 
-    // First try the primary copy.
-    int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
-    if (SeekFile64(fd, offset, SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
-        return nullptr;
-    }
-    std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
-
-    // If the primary copy failed, try the backup copy.
+    // Read the priamry copy, and if that fails, try the backup.
+    std::unique_ptr<LpMetadata> metadata = ReadPrimaryMetadata(fd, geometry, slot_number);
     if (!metadata) {
-        offset = GetBackupMetadataOffset(geometry, slot_number);
-        if (SeekFile64(fd, offset, SEEK_END) < 0) {
-            PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
-            return nullptr;
-        }
-        metadata = ParseMetadata(fd);
+        metadata = ReadBackupMetadata(fd, geometry, slot_number);
     }
-
     if (metadata) {
         metadata->geometry = geometry;
     }
@@ -280,32 +286,6 @@
     return ReadMetadata(fd, slot_number);
 }
 
-std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
-    LpMetadataGeometry geometry;
-    if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
-        return nullptr;
-    }
-    if (SeekFile64(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE;
-        return nullptr;
-    }
-    std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
-    if (!metadata) {
-        return nullptr;
-    }
-    metadata->geometry = geometry;
-    return metadata;
-}
-
-std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
-    android::base::unique_fd fd(open(file, O_RDONLY));
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
-        return nullptr;
-    }
-    return ReadFromImageFile(fd);
-}
-
 static std::string NameFromFixedArray(const char* name, size_t buffer_size) {
     // If the end of the buffer has a null character, it's safe to assume the
     // buffer is null terminated. Otherwise, we cap the string to the input
diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h
new file mode 100644
index 0000000..843b2f2
--- /dev/null
+++ b/fs_mgr/liblp/reader.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBLP_READER_H_
+#define LIBLP_READER_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include <liblp/liblp.h>
+
+namespace android {
+namespace fs_mgr {
+
+std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
+
+// Helper functions for manually reading geometry and metadata.
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
+std::unique_ptr<LpMetadata> ParseMetadata(int fd);
+
+// These functions assume a valid geometry and slot number.
+std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
+                                                uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
+                                               uint32_t slot_number);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif /* LIBLP_READER_H_ */
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 5310cab..a590037 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -38,7 +38,8 @@
     }
 
     if (S_ISBLK(s.st_mode)) {
-        return get_block_device_size(fd);
+        *size = get_block_device_size(fd);
+        return *size != 0;
     }
 
     int64_t result = SeekFile64(fd, 0, SEEK_END);
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 09ed314..4522275 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -50,6 +50,30 @@
 // Compute a SHA256 hash.
 void SHA256(const void* data, size_t length, uint8_t out[32]);
 
+// Align |base| such that it is evenly divisible by |alignment|, which does not
+// have to be a power of two.
+constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment) {
+    if (!alignment) {
+        return base;
+    }
+    uint64_t remainder = base % alignment;
+    if (remainder == 0) {
+        return base;
+    }
+    return base + (alignment - remainder);
+}
+
+// Same as the above |AlignTo|, except that |base| is only aligned when added to
+// |alignment_offset|.
+constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment, uint32_t alignment_offset) {
+    uint64_t aligned = AlignTo(base, alignment) + alignment_offset;
+    if (aligned - alignment >= base) {
+        // We overaligned (base < alignment_offset).
+        return aligned - alignment;
+    }
+    return aligned;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
 
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index 25e8a25..092dbf1 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-#include "utility.h"
 #include <gtest/gtest.h>
+#include <liblp/liblp.h>
+
+#include "utility.h"
 
 using namespace android;
 using namespace android::fs_mgr;
@@ -29,8 +31,16 @@
 }
 
 TEST(liblp, GetMetadataOffset) {
-    LpMetadataGeometry geometry = {
-            LP_METADATA_GEOMETRY_MAGIC, sizeof(geometry), {0}, 16384, 4, 10000, 80000};
+    LpMetadataGeometry geometry = {LP_METADATA_GEOMETRY_MAGIC,
+                                   sizeof(geometry),
+                                   {0},
+                                   16384,
+                                   4,
+                                   10000,
+                                   80000,
+                                   0,
+                                   0,
+                                   1024 * 1024};
     EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096);
     EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384);
     EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2);
@@ -41,3 +51,14 @@
     EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), -4096 - 16384 * 3);
     EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), -4096 - 16384 * 4);
 }
+
+TEST(liblp, AlignTo) {
+    EXPECT_EQ(AlignTo(37, 0), 37);
+    EXPECT_EQ(AlignTo(1024, 1024), 1024);
+    EXPECT_EQ(AlignTo(555, 1024), 1024);
+    EXPECT_EQ(AlignTo(555, 1000), 1000);
+    EXPECT_EQ(AlignTo(0, 1024), 0);
+    EXPECT_EQ(AlignTo(54, 32, 30), 62);
+    EXPECT_EQ(AlignTo(32, 32, 30), 62);
+    EXPECT_EQ(AlignTo(17, 32, 30), 30);
+}
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 89cbabd..156319b 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "writer.h"
+
 #include <inttypes.h>
 #include <unistd.h>
 
@@ -22,18 +24,20 @@
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
 
-#include "liblp/reader.h"
-#include "liblp/writer.h"
+#include "reader.h"
 #include "utility.h"
 
 namespace android {
 namespace fs_mgr {
 
-static std::string SerializeGeometry(const LpMetadataGeometry& input) {
+std::string SerializeGeometry(const LpMetadataGeometry& input) {
     LpMetadataGeometry geometry = input;
     memset(geometry.checksum, 0, sizeof(geometry.checksum));
     SHA256(&geometry, sizeof(geometry), geometry.checksum);
-    return std::string(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
+
+    std::string blob(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
+    blob.resize(LP_METADATA_GEOMETRY_SIZE);
+    return blob;
 }
 
 static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {
@@ -43,7 +47,7 @@
            g1.last_logical_sector == g2.last_logical_sector;
 }
 
-static std::string SerializeMetadata(const LpMetadata& input) {
+std::string SerializeMetadata(const LpMetadata& input) {
     LpMetadata metadata = input;
     LpMetadataHeader& header = metadata.header;
 
@@ -73,8 +77,14 @@
 
 // Perform sanity checks so we don't accidentally overwrite valid metadata
 // with potentially invalid metadata, or random partition data with metadata.
-static bool ValidateGeometryAndMetadata(const LpMetadata& metadata, uint64_t blockdevice_size,
-                                        uint64_t metadata_size) {
+static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std::string* blob) {
+    uint64_t blockdevice_size;
+    if (!GetDescriptorSize(fd, &blockdevice_size)) {
+        return false;
+    }
+
+    *blob = SerializeMetadata(metadata);
+
     const LpMetadataHeader& header = metadata.header;
     const LpMetadataGeometry& geometry = metadata.geometry;
     // Validate the usable sector range.
@@ -83,7 +93,7 @@
         return false;
     }
     // Make sure we're writing within the space reserved.
-    if (metadata_size > geometry.metadata_max_size) {
+    if (blob->size() > geometry.metadata_max_size) {
         LERROR << "Logical partition metadata is too large.";
         return false;
     }
@@ -101,6 +111,11 @@
         LERROR << "Not enough space to backup all logical partition metadata slots.";
         return false;
     }
+    if (blockdevice_size != metadata.geometry.block_device_size) {
+        LERROR << "Block device size " << blockdevice_size
+               << " does not match metadata requested size " << metadata.geometry.block_device_size;
+        return false;
+    }
 
     // Make sure all partition entries reference valid extents.
     for (const auto& partition : metadata.partitions) {
@@ -124,75 +139,24 @@
     return true;
 }
 
-bool WritePartitionTable(int fd, const LpMetadata& metadata, SyncMode sync_mode,
-                         uint32_t slot_number) {
-    uint64_t size;
-    if (!GetDescriptorSize(fd, &size)) {
-        return false;
-    }
-
-    const LpMetadataGeometry& geometry = metadata.geometry;
-    if (sync_mode != SyncMode::Flash) {
-        // Verify that the old geometry is identical. If it's not, then we've
-        // based this new metadata on invalid assumptions.
-        LpMetadataGeometry old_geometry;
-        if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
-            return false;
-        }
-        if (!CompareGeometry(geometry, old_geometry)) {
-            LERROR << "Incompatible geometry in new logical partition metadata";
-            return false;
-        }
-    }
-
-    // Make sure we're writing to a valid metadata slot.
-    if (slot_number >= geometry.metadata_slot_count) {
-        LERROR << "Invalid logical partition metadata slot number.";
-        return false;
-    }
-
-    // Before writing geometry and/or logical partition tables, perform some
-    // basic checks that the geometry and tables are coherent, and will fit
-    // on the given block device.
-    std::string blob = SerializeMetadata(metadata);
-    if (!ValidateGeometryAndMetadata(metadata, size, blob.size())) {
-        return false;
-    }
-
-    // First write geometry if this is a flash operation. It gets written to
-    // the first and last 4096-byte regions of the device.
-    if (sync_mode == SyncMode::Flash) {
-        std::string blob = SerializeGeometry(metadata.geometry);
-        if (SeekFile64(fd, 0, SEEK_SET) < 0) {
-            PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
-            return false;
-        }
-        if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
-            PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
-            return false;
-        }
-        if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
-            PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
-            return false;
-        }
-        if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
-            PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
-            return false;
-        }
-    }
-
-    // Write the primary copy of the metadata.
+static bool WritePrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+                                 const std::string& blob,
+                                 const std::function<bool(int, const std::string&)>& writer) {
     int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
     if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
         PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset;
         return false;
     }
-    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+    if (!writer(fd, blob)) {
         PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
         return false;
     }
+    return true;
+}
 
-    // Write the backup copy of the metadata.
+static bool WriteBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+                                const std::string& blob,
+                                const std::function<bool(int, const std::string&)>& writer) {
     int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number);
     int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_END);
     if (abs_offset == (int64_t)-1) {
@@ -204,44 +168,157 @@
                << " is within logical partition bounds, sector " << geometry.last_logical_sector;
         return false;
     }
-    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+    if (!writer(fd, blob)) {
         PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
         return false;
     }
     return true;
 }
 
-bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
-                         uint32_t slot_number) {
-    android::base::unique_fd fd(open(block_device, O_RDWR | O_SYNC));
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+                          const std::string& blob,
+                          const std::function<bool(int, const std::string&)>& writer) {
+    // Make sure we're writing to a valid metadata slot.
+    if (slot_number >= geometry.metadata_slot_count) {
+        LERROR << "Invalid logical partition metadata slot number.";
         return false;
     }
-    return WritePartitionTable(fd, metadata, sync_mode, slot_number);
-}
-
-bool WriteToImageFile(int fd, const LpMetadata& input) {
-    std::string geometry = SerializeGeometry(input.geometry);
-    std::string padding(LP_METADATA_GEOMETRY_SIZE - geometry.size(), '\0');
-    std::string metadata = SerializeMetadata(input);
-
-    std::string everything = geometry + padding + metadata;
-
-    if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
-        PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed";
+    if (!WritePrimaryMetadata(fd, geometry, slot_number, blob, writer)) {
+        return false;
+    }
+    if (!WriteBackupMetadata(fd, geometry, slot_number, blob, writer)) {
         return false;
     }
     return true;
 }
 
-bool WriteToImageFile(const char* file, const LpMetadata& input) {
-    android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+static bool DefaultWriter(int fd, const std::string& blob) {
+    return android::base::WriteFully(fd, blob.data(), blob.size());
+}
+
+bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
+    // Before writing geometry and/or logical partition tables, perform some
+    // basic checks that the geometry and tables are coherent, and will fit
+    // on the given block device.
+    std::string metadata_blob;
+    if (!ValidateAndSerializeMetadata(fd, metadata, &metadata_blob)) {
         return false;
     }
-    return WriteToImageFile(fd, input);
+
+    // Write geometry to the first and last 4096 bytes of the device.
+    std::string blob = SerializeGeometry(metadata.geometry);
+    if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
+        return false;
+    }
+    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+        return false;
+    }
+    if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
+        return false;
+    }
+    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+        return false;
+    }
+
+    // Write metadata to the correct slot, now that geometry is in place.
+    return WriteMetadata(fd, metadata.geometry, slot_number, metadata_blob, DefaultWriter);
+}
+
+static bool CompareMetadata(const LpMetadata& a, const LpMetadata& b) {
+    return !memcmp(a.header.header_checksum, b.header.header_checksum,
+                   sizeof(a.header.header_checksum));
+}
+
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+                          const std::function<bool(int, const std::string&)>& writer) {
+    // Before writing geometry and/or logical partition tables, perform some
+    // basic checks that the geometry and tables are coherent, and will fit
+    // on the given block device.
+    std::string blob;
+    if (!ValidateAndSerializeMetadata(fd, metadata, &blob)) {
+        return false;
+    }
+
+    // Verify that the old geometry is identical. If it's not, then we might be
+    // writing a table that was built for a different device, so we must reject
+    // it.
+    const LpMetadataGeometry& geometry = metadata.geometry;
+    LpMetadataGeometry old_geometry;
+    if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
+        return false;
+    }
+    if (!CompareGeometry(geometry, old_geometry)) {
+        LERROR << "Incompatible geometry in new logical partition metadata";
+        return false;
+    }
+
+    // Validate the slot number now, before we call Read*Metadata.
+    if (slot_number >= geometry.metadata_slot_count) {
+        LERROR << "Invalid logical partition metadata slot number.";
+        return false;
+    }
+
+    // Try to read both existing copies of the metadata, if any.
+    std::unique_ptr<LpMetadata> primary = ReadPrimaryMetadata(fd, geometry, slot_number);
+    std::unique_ptr<LpMetadata> backup = ReadBackupMetadata(fd, geometry, slot_number);
+
+    if (primary && (!backup || !CompareMetadata(*primary.get(), *backup.get()))) {
+        // If the backup copy does not match the primary copy, we first
+        // synchronize the backup copy. This guarantees that a partial write
+        // still leaves one copy intact.
+        std::string old_blob;
+        if (!ValidateAndSerializeMetadata(fd, *primary.get(), &old_blob)) {
+            LERROR << "Error serializing primary metadata to repair corrupted backup";
+            return false;
+        }
+        if (!WriteBackupMetadata(fd, geometry, slot_number, old_blob, writer)) {
+            LERROR << "Error writing primary metadata to repair corrupted backup";
+            return false;
+        }
+    } else if (backup && !primary) {
+        // The backup copy is coherent, and the primary is not. Sync it for
+        // safety.
+        std::string old_blob;
+        if (!ValidateAndSerializeMetadata(fd, *backup.get(), &old_blob)) {
+            LERROR << "Error serializing primary metadata to repair corrupted backup";
+            return false;
+        }
+        if (!WritePrimaryMetadata(fd, geometry, slot_number, old_blob, writer)) {
+            LERROR << "Error writing primary metadata to repair corrupted backup";
+            return false;
+        }
+    }
+
+    // Both copies should now be in sync, so we can continue the update.
+    return WriteMetadata(fd, geometry, slot_number, blob, writer);
+}
+
+bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                         uint32_t slot_number) {
+    android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        return false;
+    }
+    return FlashPartitionTable(fd, metadata, slot_number);
+}
+
+bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                          uint32_t slot_number) {
+    android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        return false;
+    }
+    return UpdatePartitionTable(fd, metadata, slot_number);
+}
+
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
+    return UpdatePartitionTable(fd, metadata, slot_number, DefaultWriter);
 }
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/liblp/writer.h b/fs_mgr/liblp/writer.h
new file mode 100644
index 0000000..adbbebf
--- /dev/null
+++ b/fs_mgr/liblp/writer.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBLP_WRITER_H
+#define LIBLP_WRITER_H
+
+#include <functional>
+#include <string>
+
+#include <liblp/liblp.h>
+
+namespace android {
+namespace fs_mgr {
+
+std::string SerializeGeometry(const LpMetadataGeometry& input);
+std::string SerializeMetadata(const LpMetadata& input);
+
+// These variants are for testing only. The path-based functions should be used
+// for actual operation, so that open() is called with the correct flags.
+bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
+
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+                          const std::function<bool(int, const std::string&)>& writer);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif /* LIBLP_WRITER_H */
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 2e76752..8b1c55a 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -14,13 +14,18 @@
  * limitations under the License.
  */
 
+#include <linux/fs.h>
+#include <mntent.h>
+
 #include <algorithm>
 #include <iterator>
+#include <set>
 #include <string>
 #include <utility>
 #include <vector>
 
 #include <android-base/strings.h>
+#include <fstab/fstab.h>
 #include <gtest/gtest.h>
 
 #include "../fs_mgr_priv_boot_config.h"
@@ -129,3 +134,70 @@
     EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "nospace", &content));
     EXPECT_TRUE(content.empty()) << content;
 }
+
+TEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {
+    auto fstab = fs_mgr_read_fstab("/proc/mounts");
+    ASSERT_NE(fstab, nullptr);
+
+    std::unique_ptr<std::FILE, int (*)(std::FILE*)> mounts(setmntent("/proc/mounts", "r"),
+                                                           endmntent);
+    ASSERT_NE(mounts, nullptr);
+
+    mntent* mentry;
+    int i = 0;
+    while ((mentry = getmntent(mounts.get())) != nullptr) {
+        ASSERT_LT(i, fstab->num_entries);
+        auto fsrec = &fstab->recs[i];
+
+        std::string mnt_fsname(mentry->mnt_fsname ?: "nullptr");
+        std::string blk_device(fsrec->blk_device ?: "nullptr");
+        EXPECT_EQ(mnt_fsname, blk_device);
+
+        std::string mnt_dir(mentry->mnt_dir ?: "nullptr");
+        std::string mount_point(fsrec->mount_point ?: "nullptr");
+        EXPECT_EQ(mnt_dir, mount_point);
+
+        std::string mnt_type(mentry->mnt_type ?: "nullptr");
+        std::string fs_type(fsrec->fs_type ?: "nullptr");
+        EXPECT_EQ(mnt_type, fs_type);
+
+        std::set<std::string> mnt_opts;
+        for (auto& s : android::base::Split(mentry->mnt_opts ?: "nullptr", ",")) {
+            mnt_opts.emplace(s);
+        }
+        std::set<std::string> fs_options;
+        for (auto& s : android::base::Split(fsrec->fs_options ?: "nullptr", ",")) {
+            fs_options.emplace(s);
+        }
+        // matches private content in fs_mgr_fstab.c
+        static struct flag_list {
+            const char* name;
+            unsigned int flag;
+        } mount_flags[] = {
+                {"noatime", MS_NOATIME},
+                {"noexec", MS_NOEXEC},
+                {"nosuid", MS_NOSUID},
+                {"nodev", MS_NODEV},
+                {"nodiratime", MS_NODIRATIME},
+                {"ro", MS_RDONLY},
+                {"rw", 0},
+                {"remount", MS_REMOUNT},
+                {"bind", MS_BIND},
+                {"rec", MS_REC},
+                {"unbindable", MS_UNBINDABLE},
+                {"private", MS_PRIVATE},
+                {"slave", MS_SLAVE},
+                {"shared", MS_SHARED},
+                {"defaults", 0},
+                {0, 0},
+        };
+        for (auto f = 0; mount_flags[f].name; ++f) {
+            if (mount_flags[f].flag & fsrec->flags) {
+                fs_options.emplace(mount_flags[f].name);
+            }
+        }
+        if (!(fsrec->flags & MS_RDONLY)) fs_options.emplace("rw");
+        EXPECT_EQ(mnt_opts, fs_options);
+        ++i;
+    }
+}
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 9d48b8c..5e11c84 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -50,6 +50,7 @@
     std::cerr << "  delete <dm-name>" << std::endl;
     std::cerr << "  list <devices | targets>" << std::endl;
     std::cerr << "  getpath <dm-name>" << std::endl;
+    std::cerr << "  table <dm-name>" << std::endl;
     std::cerr << "  help" << std::endl;
     std::cerr << std::endl;
     std::cerr << "Target syntax:" << std::endl;
@@ -258,6 +259,31 @@
     return 0;
 }
 
+static int TableCmdHandler(int argc, char** argv) {
+    if (argc != 1) {
+        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    std::vector<DeviceMapper::TargetInfo> table;
+    if (!dm.GetTableStatus(argv[0], &table)) {
+        std::cerr << "Could not query table status of device \"" << argv[0] << "\"." << std::endl;
+        return -EINVAL;
+    }
+    std::cout << "Targets in the device-mapper table for " << argv[0] << ":" << std::endl;
+    for (const auto& target : table) {
+        std::cout << target.spec.sector_start << "-"
+                  << (target.spec.sector_start + target.spec.length) << ": "
+                  << target.spec.target_type;
+        if (!target.data.empty()) {
+            std::cout << ", " << target.data;
+        }
+        std::cout << std::endl;
+    }
+    return 0;
+}
+
 static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
         // clang-format off
         {"create", DmCreateCmdHandler},
@@ -265,6 +291,7 @@
         {"list", DmListCmdHandler},
         {"help", HelpCmdHandler},
         {"getpath", GetPathCmdHandler},
+        {"table", TableCmdHandler},
         // clang-format on
 };
 
diff --git a/gatekeeperd/OWNERS b/gatekeeperd/OWNERS
new file mode 100644
index 0000000..9c99c6e
--- /dev/null
+++ b/gatekeeperd/OWNERS
@@ -0,0 +1,2 @@
+swillden@google.com
+jdanis@google.com
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 61c8804..abb387c 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -234,11 +234,13 @@
     virtual int verify(uint32_t uid,
             const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
             const uint8_t *provided_password, uint32_t provided_password_length, bool *request_reenroll) {
-        uint8_t *auth_token;
+        uint8_t *auth_token = nullptr;
         uint32_t auth_token_length;
-        return verifyChallenge(uid, 0, enrolled_password_handle, enrolled_password_handle_length,
+        int ret = verifyChallenge(uid, 0, enrolled_password_handle, enrolled_password_handle_length,
                 provided_password, provided_password_length,
                 &auth_token, &auth_token_length, request_reenroll);
+        delete [] auth_token;
+        return ret;
     }
 
     virtual int verifyChallenge(uint32_t uid, uint64_t challenge,
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 56f5148..7269b62 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -5,3 +5,16 @@
     header_libs: ["libbatteryservice_headers"],
     export_header_lib_headers: ["libbatteryservice_headers"],
 }
+
+cc_library_static {
+    name: "libbatterymonitor",
+    srcs: ["BatteryMonitor.cpp"],
+    vendor_available: true,
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libutils",
+        "libbase",
+    ],
+    header_libs: ["libhealthd_headers"],
+    export_header_lib_headers: ["libhealthd_headers"],
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 6c8fecf..1244903 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -3,14 +3,6 @@
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := BatteryMonitor.cpp
-LOCAL_MODULE := libbatterymonitor
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libutils libbase libbinder
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
 LOCAL_SRC_FILES := \
     healthd_mode_android.cpp \
     BatteryPropertiesRegistrar.cpp
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 194e667..97435c7 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -18,7 +18,6 @@
 #define HEALTHD_BATTERYMONITOR_H
 
 #include <batteryservice/BatteryService.h>
-#include <binder/IInterface.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
diff --git a/init/Android.bp b/init/Android.bp
index cf7637f..c02b8d1 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -104,11 +104,10 @@
         "devices.cpp",
         "epoll.cpp",
         "firmware_handler.cpp",
+        "first_stage_mount.cpp",
         "import_parser.cpp",
         "init.cpp",
-        "init_first_stage.cpp",
         "keychords.cpp",
-        "log.cpp",
         "parser.cpp",
         "persistent_properties.cpp",
         "persistent_properties.proto",
@@ -179,6 +178,8 @@
         "rlimit_parser_test.cpp",
         "service_test.cpp",
         "subcontext_test.cpp",
+        "tokenizer_test.cpp",
+        "ueventd_parser_test.cpp",
         "ueventd_test.cpp",
         "util_test.cpp",
     ],
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index 8a4b518..1481162 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -60,7 +60,7 @@
     prop_name.erase(equal_pos);
 
     if (!IsActionableProperty(subcontext, prop_name)) {
-        return Error() << "unexported property tigger found: " << prop_name;
+        return Error() << "unexported property trigger found: " << prop_name;
     }
 
     if (auto [it, inserted] = property_triggers->emplace(prop_name, prop_value); !inserted) {
diff --git a/init/devices.cpp b/init/devices.cpp
index ada1e28..ed4a739 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -31,7 +31,6 @@
 #include <selinux/selinux.h>
 
 #include "selinux.h"
-#include "ueventd.h"
 #include "util.h"
 
 #ifdef _INIT_INIT_H
diff --git a/init/devices.h b/init/devices.h
index f9035da..0be660f 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -35,6 +35,8 @@
 
 class Permissions {
   public:
+    friend void TestPermissions(const Permissions& expected, const Permissions& test);
+
     Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid);
 
     bool Match(const std::string& path) const;
@@ -57,6 +59,8 @@
 
 class SysfsPermissions : public Permissions {
   public:
+    friend void TestSysfsPermissions(const SysfsPermissions& expected, const SysfsPermissions& test);
+
     SysfsPermissions(const std::string& name, const std::string& attribute, mode_t perm, uid_t uid,
                      gid_t gid)
         : Permissions(name, perm, uid, gid), attribute_(attribute) {}
@@ -71,16 +75,24 @@
 class Subsystem {
   public:
     friend class SubsystemParser;
+    friend void TestSubsystems(const Subsystem& expected, const Subsystem& test);
+
+    enum DevnameSource {
+        DEVNAME_UEVENT_DEVNAME,
+        DEVNAME_UEVENT_DEVPATH,
+    };
 
     Subsystem() {}
-    Subsystem(std::string name) : name_(std::move(name)) {}
+    Subsystem(const std::string& name) : name_(name) {}
+    Subsystem(const std::string& name, DevnameSource source, const std::string& dir_name)
+        : name_(name), devname_source_(source), dir_name_(dir_name) {}
 
     // Returns the full path for a uevent of a device that is a member of this subsystem,
     // according to the rules parsed from ueventd.rc
     std::string ParseDevPath(const Uevent& uevent) const {
-        std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
-                                  ? uevent.device_name
-                                  : android::base::Basename(uevent.path);
+        std::string devname = devname_source_ == DEVNAME_UEVENT_DEVNAME
+                                      ? uevent.device_name
+                                      : android::base::Basename(uevent.path);
 
         return dir_name_ + "/" + devname;
     }
@@ -88,14 +100,9 @@
     bool operator==(const std::string& string_name) const { return name_ == string_name; }
 
   private:
-    enum class DevnameSource {
-        DEVNAME_UEVENT_DEVNAME,
-        DEVNAME_UEVENT_DEVPATH,
-    };
-
     std::string name_;
+    DevnameSource devname_source_ = DEVNAME_UEVENT_DEVNAME;
     std::string dir_name_ = "/dev";
-    DevnameSource devname_source_;
 };
 
 class DeviceHandler {
@@ -106,7 +113,6 @@
     DeviceHandler(std::vector<Permissions> dev_permissions,
                   std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> subsystems,
                   std::set<std::string> boot_devices, bool skip_restorecon);
-    ~DeviceHandler(){};
 
     void HandleDeviceEvent(const Uevent& uevent);
 
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index 8c8d9f2..28bda34 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -21,7 +21,6 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include <string>
 #include <thread>
 
 #include <android-base/chrono_utils.h>
@@ -36,6 +35,8 @@
 namespace android {
 namespace init {
 
+std::vector<std::string> firmware_directories;
+
 static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
                          int loading_fd, int data_fd) {
     // Start transfer.
@@ -78,12 +79,9 @@
         return;
     }
 
-    static const char* firmware_dirs[] = {"/etc/firmware/", "/odm/firmware/",
-                                          "/vendor/firmware/", "/firmware/image/"};
-
 try_loading_again:
-    for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
-        std::string file = firmware_dirs[i] + uevent.firmware;
+    for (const auto& firmware_directory : firmware_directories) {
+        std::string file = firmware_directory + uevent.firmware;
         unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
         struct stat sb;
         if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index e456ac4..6081511 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -17,11 +17,16 @@
 #ifndef _INIT_FIRMWARE_HANDLER_H
 #define _INIT_FIRMWARE_HANDLER_H
 
+#include <string>
+#include <vector>
+
 #include "uevent.h"
 
 namespace android {
 namespace init {
 
+extern std::vector<std::string> firmware_directories;
+
 void HandleFirmwareEvent(const Uevent& uevent);
 
 }  // namespace init
diff --git a/init/init_first_stage.cpp b/init/first_stage_mount.cpp
similarity index 92%
rename from init/init_first_stage.cpp
rename to init/first_stage_mount.cpp
index 2bc9f3a..8ded873 100644
--- a/init/init_first_stage.cpp
+++ b/init/first_stage_mount.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "init_first_stage.h"
+#include "first_stage_mount.h"
 
 #include <stdlib.h>
 #include <unistd.h>
@@ -40,7 +40,6 @@
 #include "util.h"
 
 using android::base::Timer;
-using android::fs_mgr::LogicalPartitionTable;
 
 namespace android {
 namespace init {
@@ -75,7 +74,6 @@
     bool need_dm_verity_;
 
     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
-    std::unique_ptr<LogicalPartitionTable> dm_linear_table_;
     std::string lp_metadata_partition_;
     std::vector<fstab_rec*> mount_fstab_recs_;
     std::set<std::string> required_devices_partition_names_;
@@ -127,11 +125,12 @@
     if (checked) {
         return enabled;
     }
-    import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) {
-        if (key == "androidboot.logical_partitions" && value == "1") {
-            enabled = true;
-        }
-    });
+    import_kernel_cmdline(false,
+                          [](const std::string& key, const std::string& value, bool in_qemu) {
+                              if (key == "androidboot.logical_partitions" && value == "1") {
+                                  enabled = true;
+                              }
+                          });
     checked = true;
     return enabled;
 }
@@ -150,14 +149,10 @@
         LOG(INFO) << "Failed to read fstab from device tree";
     }
 
-    if (IsDmLinearEnabled()) {
-        dm_linear_table_ = android::fs_mgr::LoadPartitionsFromDeviceTree();
-    }
-
     auto boot_devices = fs_mgr_get_boot_devices();
-    device_handler_ =
-        std::make_unique<DeviceHandler>(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
-                                        std::vector<Subsystem>{}, std::move(boot_devices), false);
+    device_handler_ = std::make_unique<DeviceHandler>(
+            std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
+            std::move(boot_devices), false);
 }
 
 std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
@@ -195,15 +190,6 @@
     }
 
     required_devices_partition_names_.emplace(LP_METADATA_PARTITION_NAME);
-
-    if (dm_linear_table_) {
-        for (const auto& partition : dm_linear_table_->partitions) {
-            for (const auto& extent : partition.extents) {
-                const std::string& partition_name = android::base::Basename(extent.block_device());
-                required_devices_partition_names_.emplace(partition_name);
-            }
-        }
-    }
     return true;
 }
 
@@ -272,11 +258,6 @@
                    << LP_METADATA_PARTITION_NAME;
         return false;
     }
-    if (dm_linear_table_) {
-        if (!android::fs_mgr::CreateLogicalPartitions(*dm_linear_table_.get())) {
-            return false;
-        }
-    }
     return android::fs_mgr::CreateLogicalPartitions(lp_metadata_partition_);
 }
 
@@ -456,12 +437,19 @@
 bool FirstStageMountVBootV2::GetDmVerityDevices() {
     need_dm_verity_ = false;
 
+    std::set<std::string> logical_partitions;
+
     // fstab_rec->blk_device has A/B suffix.
     for (auto fstab_rec : mount_fstab_recs_) {
         if (fs_mgr_is_avb(fstab_rec)) {
             need_dm_verity_ = true;
         }
-        required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+        if (fs_mgr_is_logical(fstab_rec)) {
+            // Don't try to find logical partitions via uevent regeneration.
+            logical_partitions.emplace(basename(fstab_rec->blk_device));
+        } else {
+            required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+        }
     }
 
     // libavb verifies AVB metadata on all verified partitions at once.
@@ -476,11 +464,15 @@
         std::vector<std::string> partitions = android::base::Split(device_tree_vbmeta_parts_, ",");
         std::string ab_suffix = fs_mgr_get_slot_suffix();
         for (const auto& partition : partitions) {
+            std::string partition_name = partition + ab_suffix;
+            if (logical_partitions.count(partition_name)) {
+                continue;
+            }
             // required_devices_partition_names_ is of type std::set so it's not an issue
             // to emplace a partition twice. e.g., /vendor might be in both places:
             //   - device_tree_vbmeta_parts_ = "vbmeta,boot,system,vendor"
             //   - mount_fstab_recs_: /vendor_a
-            required_devices_partition_names_.emplace(partition + ab_suffix);
+            required_devices_partition_names_.emplace(partition_name);
         }
     }
     return true;
@@ -493,7 +485,7 @@
     // as it finds them, so this must happen first.
     if (!uevent.partition_name.empty() &&
         required_devices_partition_names_.find(uevent.partition_name) !=
-            required_devices_partition_names_.end()) {
+                required_devices_partition_names_.end()) {
         // GetBlockDeviceSymlinks() will return three symlinks at most, depending on
         // the content of uevent. by-name symlink will be at [0] if uevent->partition_name
         // is not empty. e.g.,
@@ -501,7 +493,7 @@
         //   - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
         std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
         if (!links.empty()) {
-            auto[it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]);
+            auto [it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]);
             if (!inserted) {
                 LOG(ERROR) << "Partition '" << uevent.partition_name
                            << "' already existed in the by-name symlink map with a value of '"
@@ -517,7 +509,7 @@
     if (fs_mgr_is_avb(fstab_rec)) {
         if (!InitAvbHandle()) return false;
         SetUpAvbHashtreeResult hashtree_result =
-            avb_handle_->SetUpAvbHashtree(fstab_rec, false /* wait_for_verity_dev */);
+                avb_handle_->SetUpAvbHashtree(fstab_rec, false /* wait_for_verity_dev */);
         switch (hashtree_result) {
             case SetUpAvbHashtreeResult::kDisabled:
                 return true;  // Returns true to mount the partition.
@@ -594,7 +586,7 @@
     }
 
     FsManagerAvbUniquePtr avb_handle =
-        FsManagerAvbHandle::Open(std::move(avb_first_mount.by_name_symlink_map_));
+            FsManagerAvbHandle::Open(std::move(avb_first_mount.by_name_symlink_map_));
     if (!avb_handle) {
         PLOG(ERROR) << "Failed to open FsManagerAvbHandle for INIT_AVB_VERSION";
         return;
diff --git a/init/init_first_stage.h b/init/first_stage_mount.h
similarity index 91%
rename from init/init_first_stage.h
rename to init/first_stage_mount.h
index c7a3867..21d87fd 100644
--- a/init/init_first_stage.h
+++ b/init/first_stage_mount.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_FIRST_STAGE_H
-#define _INIT_FIRST_STAGE_H
+#pragma once
 
 namespace android {
 namespace init {
@@ -25,5 +24,3 @@
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/init.cpp b/init/init.cpp
index 77c4fc4..6569871 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -48,10 +48,9 @@
 
 #include "action_parser.h"
 #include "epoll.h"
+#include "first_stage_mount.h"
 #include "import_parser.h"
-#include "init_first_stage.h"
 #include "keychords.h"
-#include "log.h"
 #include "property_service.h"
 #include "reboot.h"
 #include "security.h"
@@ -582,6 +581,34 @@
     }
 }
 
+static void InitAborter(const char* abort_message) {
+    // When init forks, it continues to use this aborter for LOG(FATAL), but we want children to
+    // simply abort instead of trying to reboot the system.
+    if (getpid() != 1) {
+        android::base::DefaultAborter(abort_message);
+        return;
+    }
+
+    RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+}
+
+static void InitKernelLogging(char* argv[]) {
+    // Make stdin/stdout/stderr all point to /dev/null.
+    int fd = open("/sys/fs/selinux/null", O_RDWR);
+    if (fd == -1) {
+        int saved_errno = errno;
+        android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
+        errno = saved_errno;
+        PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";
+    }
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    if (fd > 2) close(fd);
+
+    android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
+}
+
 int main(int argc, char** argv) {
     if (!strcmp(basename(argv[0]), "ueventd")) {
         return ueventd_main(argc, argv);
@@ -592,7 +619,7 @@
     }
 
     if (argc > 1 && !strcmp(argv[1], "subcontext")) {
-        InitKernelLogging(argv);
+        android::base::InitLogging(argv, &android::base::KernelLogger);
         const BuiltinFunctionMap function_map;
         return SubcontextMain(argc, argv, &function_map);
     }
@@ -647,6 +674,9 @@
         // /mnt/vendor is used to mount vendor-specific partitions that can not be
         // part of the vendor partition, e.g. because they are mounted read-write.
         CHECKCALL(mkdir("/mnt/vendor", 0755));
+        // /mnt/product is used to mount product-specific partitions that can not be
+        // part of the product partition, e.g. because they are mounted read-write.
+        CHECKCALL(mkdir("/mnt/product", 0755));
 
 #undef CHECKCALL
 
diff --git a/init/log.cpp b/init/log.cpp
deleted file mode 100644
index 6198fc2..0000000
--- a/init/log.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2015 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 "log.h"
-
-#include <fcntl.h>
-#include <linux/audit.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <cutils/android_reboot.h>
-#include <selinux/selinux.h>
-
-#include "reboot.h"
-
-namespace android {
-namespace init {
-
-static void InitAborter(const char* abort_message) {
-    // When init forks, it continues to use this aborter for LOG(FATAL), but we want children to
-    // simply abort instead of trying to reboot the system.
-    if (getpid() != 1) {
-        android::base::DefaultAborter(abort_message);
-        return;
-    }
-
-    // DoReboot() does a lot to try to shutdown the system cleanly.  If something happens to call
-    // LOG(FATAL) in the shutdown path, we want to catch this and immediately use the syscall to
-    // reboot instead of recursing here.
-    static bool has_aborted = false;
-    if (!has_aborted) {
-        has_aborted = true;
-        // Do not queue "shutdown" trigger since we want to shutdown immediately and it's not likely
-        // that we can even run the ActionQueue at this point.
-        DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
-    } else {
-        RebootSystem(ANDROID_RB_RESTART2, "bootloader");
-    }
-}
-
-void InitKernelLogging(char* argv[]) {
-    // Make stdin/stdout/stderr all point to /dev/null.
-    int fd = open("/sys/fs/selinux/null", O_RDWR);
-    if (fd == -1) {
-        int saved_errno = errno;
-        android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
-        errno = saved_errno;
-        PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";
-    }
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
-    if (fd > 2) close(fd);
-
-    android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
-}
-
-int selinux_klog_callback(int type, const char *fmt, ...) {
-    android::base::LogSeverity severity = android::base::ERROR;
-    if (type == SELINUX_WARNING) {
-        severity = android::base::WARNING;
-    } else if (type == SELINUX_INFO) {
-        severity = android::base::INFO;
-    }
-    char buf[1024];
-    va_list ap;
-    va_start(ap, fmt);
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-    va_end(ap);
-    android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
-    return 0;
-}
-
-}  // namespace init
-}  // namespace android
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 0ba5c4a..94f206e 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -59,7 +59,6 @@
 #include <android-base/unique_fd.h>
 #include <selinux/android.h>
 
-#include "log.h"
 #include "util.h"
 
 using android::base::ParseInt;
@@ -448,10 +447,26 @@
     selinux_android_restorecon("/sbin/sload.f2fs", 0);
 }
 
+int SelinuxKlogCallback(int type, const char* fmt, ...) {
+    android::base::LogSeverity severity = android::base::ERROR;
+    if (type == SELINUX_WARNING) {
+        severity = android::base::WARNING;
+    } else if (type == SELINUX_INFO) {
+        severity = android::base::INFO;
+    }
+    char buf[1024];
+    va_list ap;
+    va_start(ap, fmt);
+    vsnprintf(buf, sizeof(buf), fmt, ap);
+    va_end(ap);
+    android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
+    return 0;
+}
+
 // This function sets up SELinux logging to be written to kmsg, to match init's logging.
 void SelinuxSetupKernelLogging() {
     selinux_callback cb;
-    cb.func_log = selinux_klog_callback;
+    cb.func_log = SelinuxKlogCallback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 }
 
diff --git a/init/service.cpp b/init/service.cpp
index 95b37ab..4c2747e 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -130,7 +130,7 @@
         if (umount2("/sys", MNT_DETACH) == -1) {
             return ErrnoError() << "Could not umount(/sys)";
         }
-        if (mount("", "/sys", "sys", kSafeFlags, "") == -1) {
+        if (mount("", "/sys", "sysfs", kSafeFlags, "") == -1) {
             return ErrnoError() << "Could not mount(/sys)";
         }
     }
diff --git a/init/tokenizer.cpp b/init/tokenizer.cpp
index f8d9b6b..bb143f1 100644
--- a/init/tokenizer.cpp
+++ b/init/tokenizer.cpp
@@ -85,15 +85,19 @@
                 goto textdone;
             case 'n':
                 *s++ = '\n';
+                x++;
                 break;
             case 'r':
                 *s++ = '\r';
+                x++;
                 break;
             case 't':
                 *s++ = '\t';
+                x++;
                 break;
             case '\\':
                 *s++ = '\\';
+                x++;
                 break;
             case '\r':
                     /* \ <cr> <lf> -> line continuation */
@@ -101,6 +105,7 @@
                     x++;
                     continue;
                 }
+                x++;
             case '\n':
                     /* \ <lf> -> line continuation */
                 state->line++;
diff --git a/init/tokenizer_test.cpp b/init/tokenizer_test.cpp
new file mode 100644
index 0000000..acfc7c7
--- /dev/null
+++ b/init/tokenizer_test.cpp
@@ -0,0 +1,163 @@
+//
+// 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 "tokenizer.h"
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace init {
+
+namespace {
+
+void RunTest(const std::string& data, const std::vector<std::vector<std::string>>& expected_tokens) {
+    auto data_copy = std::string{data};
+    data_copy.push_back('\n');  // TODO: fix tokenizer
+    data_copy.push_back('\0');
+
+    parse_state state;
+    state.line = 0;
+    state.ptr = data_copy.data();
+    state.nexttoken = 0;
+
+    std::vector<std::string> current_line;
+    std::vector<std::vector<std::string>> tokens;
+
+    while (true) {
+        switch (next_token(&state)) {
+            case T_EOF:
+                EXPECT_EQ(expected_tokens, tokens) << data;
+                return;
+            case T_NEWLINE:
+                tokens.emplace_back(std::move(current_line));
+                break;
+            case T_TEXT:
+                current_line.emplace_back(state.text);
+                break;
+        }
+    }
+}
+
+}  // namespace
+
+TEST(tokenizer, null) {
+    RunTest("", {{}});
+}
+
+TEST(tokenizer, simple_oneline) {
+    RunTest("one two\tthree\rfour", {{"one", "two", "three", "four"}});
+}
+
+TEST(tokenizer, simple_multiline) {
+    RunTest("1 2 3\n4 5 6\n7 8 9", {{"1", "2", "3"}, {"4", "5", "6"}, {"7", "8", "9"}});
+}
+
+TEST(tokenizer, preceding_space) {
+    // Preceding spaces are ignored.
+    RunTest("    1 2 3\n\t\t\t\t4 5 6\n\r\r\r\r7 8 9",
+            {{"1", "2", "3"}, {"4", "5", "6"}, {"7", "8", "9"}});
+}
+
+TEST(tokenizer, comments) {
+    // Entirely commented lines still produce a T_NEWLINE token for tracking line count.
+    RunTest("1 2 3\n#4 5 6\n7 8 9", {{"1", "2", "3"}, {}, {"7", "8", "9"}});
+
+    RunTest("#1 2 3\n4 5 6\n7 8 9", {{}, {"4", "5", "6"}, {"7", "8", "9"}});
+
+    RunTest("1 2 3\n4 5 6\n#7 8 9", {{"1", "2", "3"}, {"4", "5", "6"}, {}});
+
+    RunTest("1 2 #3\n4 #5 6\n#7 8 9", {{"1", "2"}, {"4"}, {}});
+}
+
+TEST(tokenizer, control_chars) {
+    // Literal \n, \r, \t, and \\ produce the control characters \n, \r, \t, and \\ respectively.
+    // Literal \? produces ? for all other character '?'
+
+    RunTest(R"(1 token\ntoken 2)", {{"1", "token\ntoken", "2"}});
+    RunTest(R"(1 token\rtoken 2)", {{"1", "token\rtoken", "2"}});
+    RunTest(R"(1 token\ttoken 2)", {{"1", "token\ttoken", "2"}});
+    RunTest(R"(1 token\\token 2)", {{"1", "token\\token", "2"}});
+    RunTest(R"(1 token\btoken 2)", {{"1", "tokenbtoken", "2"}});
+
+    RunTest(R"(1 token\n 2)", {{"1", "token\n", "2"}});
+    RunTest(R"(1 token\r 2)", {{"1", "token\r", "2"}});
+    RunTest(R"(1 token\t 2)", {{"1", "token\t", "2"}});
+    RunTest(R"(1 token\\ 2)", {{"1", "token\\", "2"}});
+    RunTest(R"(1 token\b 2)", {{"1", "tokenb", "2"}});
+
+    RunTest(R"(1 \ntoken 2)", {{"1", "\ntoken", "2"}});
+    RunTest(R"(1 \rtoken 2)", {{"1", "\rtoken", "2"}});
+    RunTest(R"(1 \ttoken 2)", {{"1", "\ttoken", "2"}});
+    RunTest(R"(1 \\token 2)", {{"1", "\\token", "2"}});
+    RunTest(R"(1 \btoken 2)", {{"1", "btoken", "2"}});
+
+    RunTest(R"(1 \n 2)", {{"1", "\n", "2"}});
+    RunTest(R"(1 \r 2)", {{"1", "\r", "2"}});
+    RunTest(R"(1 \t 2)", {{"1", "\t", "2"}});
+    RunTest(R"(1 \\ 2)", {{"1", "\\", "2"}});
+    RunTest(R"(1 \b 2)", {{"1", "b", "2"}});
+}
+
+TEST(tokenizer, cr_lf) {
+    // \ before \n, \r, or \r\n is interpreted as a line continuation
+    // Extra whitespace on the next line is eaten, except \r unlike in the above tests.
+
+    RunTest("lf\\\ncont", {{"lfcont"}});
+    RunTest("lf\\\n    \t\t\t\tcont", {{"lfcont"}});
+
+    RunTest("crlf\\\r\ncont", {{"crlfcont"}});
+    RunTest("crlf\\\r\n    \t\t\t\tcont", {{"crlfcont"}});
+
+    RunTest("cr\\\rcont", {{"crcont"}});
+
+    RunTest("lfspace \\\ncont", {{"lfspace", "cont"}});
+    RunTest("lfspace \\\n    \t\t\t\tcont", {{"lfspace", "cont"}});
+
+    RunTest("crlfspace \\\r\ncont", {{"crlfspace", "cont"}});
+    RunTest("crlfspace \\\r\n    \t\t\t\tcont", {{"crlfspace", "cont"}});
+
+    RunTest("crspace \\\rcont", {{"crspace", "cont"}});
+}
+
+TEST(tokenizer, quoted) {
+    RunTest("\"quoted simple string\"", {{"quoted simple string"}});
+
+    // Unterminated quotes just return T_EOF without any T_NEWLINE.
+    RunTest("\"unterminated quoted string", {});
+
+    RunTest("\"1 2 3\"\n \"unterminated quoted string", {{"1 2 3"}});
+
+    // Escaping quotes is not allowed and are treated as an unterminated quoted string.
+    RunTest("\"quoted escaped quote\\\"\"", {});
+    RunTest("\"quoted escaped\\\" quote\"", {});
+    RunTest("\"\\\"quoted escaped quote\"", {});
+
+    RunTest("\"quoted control characters \\n \\r \\t \\\\ \\b \\\r \\\n \r \n\"",
+            {{"quoted control characters \\n \\r \\t \\\\ \\b \\\r \\\n \r \n"}});
+
+    RunTest("\"quoted simple string\" \"second quoted string\"",
+            {{"quoted simple string", "second quoted string"}});
+
+    RunTest("\"# comment quoted string\"", {{"# comment quoted string"}});
+
+    RunTest("\"Adjacent \"\"quoted strings\"", {{"Adjacent quoted strings"}});
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index a284203..cd45a3f 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -36,7 +36,6 @@
 
 #include "devices.h"
 #include "firmware_handler.h"
-#include "log.h"
 #include "selinux.h"
 #include "uevent_listener.h"
 #include "ueventd_parser.h"
@@ -215,39 +214,6 @@
     LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
 }
 
-DeviceHandler CreateDeviceHandler() {
-    Parser parser;
-
-    std::vector<Subsystem> subsystems;
-    parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>(&subsystems));
-
-    using namespace std::placeholders;
-    std::vector<SysfsPermissions> sysfs_permissions;
-    std::vector<Permissions> dev_permissions;
-    parser.AddSingleLineParser("/sys/",
-                               std::bind(ParsePermissionsLine, _1, &sysfs_permissions, nullptr));
-    parser.AddSingleLineParser("/dev/",
-                               std::bind(ParsePermissionsLine, _1, nullptr, &dev_permissions));
-
-    parser.ParseConfig("/ueventd.rc");
-    parser.ParseConfig("/vendor/ueventd.rc");
-    parser.ParseConfig("/odm/ueventd.rc");
-
-    /*
-     * keep the current product name base configuration so
-     * we remain backwards compatible and allow it to override
-     * everything
-     * TODO: cleanup platform ueventd.rc to remove vendor specific
-     * device node entries (b/34968103)
-     */
-    std::string hardware = android::base::GetProperty("ro.hardware", "");
-    parser.ParseConfig("/ueventd." + hardware + ".rc");
-
-    auto boot_devices = fs_mgr_get_boot_devices();
-    return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
-                         std::move(subsystems), std::move(boot_devices), true);
-}
-
 int ueventd_main(int argc, char** argv) {
     /*
      * init sets the umask to 077 for forked processes. We need to
@@ -256,16 +222,34 @@
      */
     umask(000);
 
-    InitKernelLogging(argv);
+    android::base::InitLogging(argv, &android::base::KernelLogger);
 
     LOG(INFO) << "ueventd started!";
 
     SelinuxSetupKernelLogging();
     SelabelInitialize();
 
-    DeviceHandler device_handler = CreateDeviceHandler();
+    DeviceHandler device_handler;
     UeventListener uevent_listener;
 
+    {
+        // Keep the current product name base configuration so we remain backwards compatible and
+        // allow it to override everything.
+        // TODO: cleanup platform ueventd.rc to remove vendor specific device node entries (b/34968103)
+        auto hardware = android::base::GetProperty("ro.hardware", "");
+
+        auto ueventd_configuration =
+                ParseConfig({"/ueventd.rc", "/vendor/ueventd.rc", "/odm/ueventd.rc",
+                             "/ueventd." + hardware + ".rc"});
+
+        device_handler = DeviceHandler{std::move(ueventd_configuration.dev_permissions),
+                                       std::move(ueventd_configuration.sysfs_permissions),
+                                       std::move(ueventd_configuration.subsystems),
+                                       fs_mgr_get_boot_devices(), true};
+
+        firmware_directories = ueventd_configuration.firmware_directories;
+    }
+
     if (access(COLDBOOT_DONE, F_OK) != 0) {
         ColdBoot cold_boot(uevent_listener, device_handler);
         cold_boot.Run();
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index f74c878..54b0d16 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -20,6 +20,7 @@
 #include <pwd.h>
 
 #include "keyword_map.h"
+#include "parser.h"
 
 namespace android {
 namespace init {
@@ -72,6 +73,33 @@
     return Success();
 }
 
+Result<Success> ParseFirmwareDirectoriesLine(std::vector<std::string>&& args,
+                                             std::vector<std::string>* firmware_directories) {
+    if (args.size() < 2) {
+        return Error() << "firmware_directories must have at least 1 entry";
+    }
+
+    std::move(std::next(args.begin()), args.end(), std::back_inserter(*firmware_directories));
+
+    return Success();
+}
+
+class SubsystemParser : public SectionParser {
+  public:
+    SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
+    Result<Success> EndSection() override;
+
+  private:
+    Result<Success> ParseDevName(std::vector<std::string>&& args);
+    Result<Success> ParseDirName(std::vector<std::string>&& args);
+
+    Subsystem subsystem_;
+    std::vector<Subsystem>* subsystems_;
+};
+
 Result<Success> SubsystemParser::ParseSection(std::vector<std::string>&& args,
                                               const std::string& filename, int line) {
     if (args.size() != 2) {
@@ -89,11 +117,11 @@
 
 Result<Success> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
     if (args[1] == "uevent_devname") {
-        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
+        subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVNAME;
         return Success();
     }
     if (args[1] == "uevent_devpath") {
-        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
+        subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVPATH;
         return Success();
     }
 
@@ -138,5 +166,29 @@
     return Success();
 }
 
+UeventdConfiguration ParseConfig(const std::vector<std::string>& configs) {
+    Parser parser;
+    UeventdConfiguration ueventd_configuration;
+
+    parser.AddSectionParser("subsystem",
+                            std::make_unique<SubsystemParser>(&ueventd_configuration.subsystems));
+
+    using namespace std::placeholders;
+    parser.AddSingleLineParser(
+            "/sys/",
+            std::bind(ParsePermissionsLine, _1, &ueventd_configuration.sysfs_permissions, nullptr));
+    parser.AddSingleLineParser("/dev/", std::bind(ParsePermissionsLine, _1, nullptr,
+                                                  &ueventd_configuration.dev_permissions));
+    parser.AddSingleLineParser("firmware_directories",
+                               std::bind(ParseFirmwareDirectoriesLine, _1,
+                                         &ueventd_configuration.firmware_directories));
+
+    for (const auto& config : configs) {
+        parser.ParseConfig(config);
+    }
+
+    return ueventd_configuration;
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 83684f3..343d58b 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -21,30 +21,18 @@
 #include <vector>
 
 #include "devices.h"
-#include "parser.h"
 
 namespace android {
 namespace init {
 
-class SubsystemParser : public SectionParser {
-  public:
-    SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
-    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                 int line) override;
-    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
-    Result<Success> EndSection() override;
-
-  private:
-    Result<Success> ParseDevName(std::vector<std::string>&& args);
-    Result<Success> ParseDirName(std::vector<std::string>&& args);
-
-    Subsystem subsystem_;
-    std::vector<Subsystem>* subsystems_;
+struct UeventdConfiguration {
+    std::vector<Subsystem> subsystems;
+    std::vector<SysfsPermissions> sysfs_permissions;
+    std::vector<Permissions> dev_permissions;
+    std::vector<std::string> firmware_directories;
 };
 
-Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
-                                     std::vector<SysfsPermissions>* out_sysfs_permissions,
-                                     std::vector<Permissions>* out_dev_permissions);
+UeventdConfiguration ParseConfig(const std::vector<std::string>& configs);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
new file mode 100644
index 0000000..31208b9
--- /dev/null
+++ b/init/ueventd_parser_test.cpp
@@ -0,0 +1,224 @@
+/*
+ * 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 "ueventd_parser.h"
+
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+
+namespace android {
+namespace init {
+
+void TestSubsystems(const Subsystem& expected, const Subsystem& test) {
+    EXPECT_EQ(expected.name_, test.name_);
+    EXPECT_EQ(expected.devname_source_, test.devname_source_) << expected.name_;
+    EXPECT_EQ(expected.dir_name_, test.dir_name_) << expected.name_;
+}
+
+void TestPermissions(const Permissions& expected, const Permissions& test) {
+    EXPECT_EQ(expected.name_, test.name_);
+    EXPECT_EQ(expected.perm_, test.perm_) << expected.name_;
+    EXPECT_EQ(expected.uid_, test.uid_) << expected.name_;
+    EXPECT_EQ(expected.gid_, test.gid_) << expected.name_;
+    EXPECT_EQ(expected.prefix_, test.prefix_) << expected.name_;
+    EXPECT_EQ(expected.wildcard_, test.wildcard_) << expected.name_;
+}
+
+void TestSysfsPermissions(const SysfsPermissions& expected, const SysfsPermissions& test) {
+    TestPermissions(expected, test);
+    EXPECT_EQ(expected.attribute_, test.attribute_);
+}
+
+template <typename T, typename F>
+void TestVector(const T& expected, const T& test, F function) {
+    ASSERT_EQ(expected.size(), test.size());
+    auto expected_it = expected.begin();
+    auto test_it = test.begin();
+
+    for (; expected_it != expected.end(); ++expected_it, ++test_it) {
+        function(*expected_it, *test_it);
+    }
+}
+
+void TestUeventdFile(const std::string& content, const UeventdConfiguration& expected) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd(content, tf.fd));
+
+    auto result = ParseConfig({tf.path});
+
+    TestVector(expected.subsystems, result.subsystems, TestSubsystems);
+    TestVector(expected.sysfs_permissions, result.sysfs_permissions, TestSysfsPermissions);
+    TestVector(expected.dev_permissions, result.dev_permissions, TestPermissions);
+    EXPECT_EQ(expected.firmware_directories, result.firmware_directories);
+}
+
+TEST(ueventd_parser, EmptyFile) {
+    TestUeventdFile("", {});
+}
+
+TEST(ueventd_parser, Subsystems) {
+    auto ueventd_file = R"(
+subsystem test_devname
+    devname uevent_devname
+
+subsystem test_devpath_no_dirname
+    devname uevent_devpath
+
+subsystem test_devname2
+    devname uevent_devname
+
+subsystem test_devpath_dirname
+    devname uevent_devpath
+    dirname /dev/graphics
+)";
+
+    auto subsystems = std::vector<Subsystem>{
+            {"test_devname", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
+            {"test_devpath_no_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev"},
+            {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
+            {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
+
+    TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}});
+}
+
+TEST(ueventd_parser, Permissions) {
+    auto ueventd_file = R"(
+/dev/rtc0                 0640   system     system
+/dev/graphics/*           0660   root       graphics
+/dev/*/test               0660   root       system
+
+/sys/devices/platform/trusty.*      trusty_version        0440  root   log
+/sys/devices/virtual/input/input   enable      0660  root   input
+/sys/devices/virtual/*/input   poll_delay  0660  root   input
+)";
+
+    auto permissions = std::vector<Permissions>{
+            {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM},
+            {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS},
+            {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM},
+    };
+
+    auto sysfs_permissions = std::vector<SysfsPermissions>{
+            {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG},
+            {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT},
+            {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT},
+    };
+
+    TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}});
+}
+
+TEST(ueventd_parser, FirmwareDirectories) {
+    auto ueventd_file = R"(
+firmware_directories /first/ /second /third
+firmware_directories /more
+)";
+
+    auto firmware_directories = std::vector<std::string>{
+            "/first/",
+            "/second",
+            "/third",
+            "/more",
+    };
+
+    TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories});
+}
+
+TEST(ueventd_parser, AllTogether) {
+    auto ueventd_file = R"(
+
+/dev/rtc0                 0640   system     system
+firmware_directories /first/ /second /third
+/sys/devices/platform/trusty.*      trusty_version        0440  root   log
+
+subsystem test_devname
+    devname uevent_devname
+
+/dev/graphics/*           0660   root       graphics
+
+subsystem test_devpath_no_dirname
+    devname uevent_devpath
+
+/sys/devices/virtual/input/input   enable      0660  root   input
+
+## this is a comment
+
+subsystem test_devname2
+## another comment
+    devname uevent_devname
+
+subsystem test_devpath_dirname
+    devname uevent_devpath
+    dirname /dev/graphics
+
+/dev/*/test               0660   root       system
+/sys/devices/virtual/*/input   poll_delay  0660  root   input
+firmware_directories /more
+
+#ending comment
+)";
+
+    auto subsystems = std::vector<Subsystem>{
+            {"test_devname", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
+            {"test_devpath_no_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev"},
+            {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
+            {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
+
+    auto permissions = std::vector<Permissions>{
+            {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM},
+            {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS},
+            {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM},
+    };
+
+    auto sysfs_permissions = std::vector<SysfsPermissions>{
+            {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG},
+            {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT},
+            {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT},
+    };
+
+    auto firmware_directories = std::vector<std::string>{
+            "/first/",
+            "/second",
+            "/third",
+            "/more",
+    };
+
+    TestUeventdFile(ueventd_file,
+                    {subsystems, sysfs_permissions, permissions, firmware_directories});
+}
+
+// All of these lines are ill-formed, so test that there is 0 output.
+TEST(ueventd_parser, ParseErrors) {
+    auto ueventd_file = R"(
+
+/dev/rtc0                 badmode   baduidbad     system
+/dev/rtc0                 0640   baduidbad     system
+/dev/rtc0                 0640   system     baduidbad
+firmware_directories #no directory listed
+/sys/devices/platform/trusty.*      trusty_version        badmode  root   log
+/sys/devices/platform/trusty.*      trusty_version        0440  baduidbad   log
+/sys/devices/platform/trusty.*      trusty_version        0440  root   baduidbad
+
+subsystem #no name
+
+)";
+
+    TestUeventdFile(ueventd_file, {});
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/util.cpp b/init/util.cpp
index 5f2b87d..7735717 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -37,12 +37,9 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#include <cutils/android_reboot.h>
 #include <cutils/sockets.h>
 #include <selinux/android.h>
 
-#include "reboot.h"
-
 #if defined(__ANDROID__)
 #include "selinux.h"
 #else
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
index e0164b4..e03a2c3 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -23,8 +23,6 @@
 
 #include <android-base/logging.h>
 
-#include "log.h"
-
 #ifdef _INIT_INIT_H
 #error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
 #endif
@@ -35,7 +33,7 @@
 namespace init {
 
 int watchdogd_main(int argc, char **argv) {
-    InitKernelLogging(argv);
+    android::base::InitLogging(argv, &android::base::KernelLogger);
 
     int interval = 10;
     if (argc >= 2) interval = atoi(argv[1]);
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index b4bf35f..a10e636 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -42,7 +42,6 @@
     "Backtrace.cpp",
     "BacktraceCurrent.cpp",
     "BacktracePtrace.cpp",
-    "thread_utils.c",
     "ThreadEntry.cpp",
     "UnwindStack.cpp",
     "UnwindStackMap.cpp",
@@ -94,7 +93,6 @@
             ],
 
             static_libs: [
-                "libcutils",
                 "libprocinfo",
             ],
 
@@ -145,7 +143,6 @@
         "backtrace_offline_test.cpp",
         "backtrace_test.cpp",
         "GetPss.cpp",
-        "thread_utils.c",
     ],
 
     cflags: [
@@ -159,7 +156,6 @@
         "libbacktrace",
         "libdexfile",
         "libbase",
-        "libcutils",
         "liblog",
         "libunwindstack",
     ],
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 6445a7c..6bec63c 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -23,6 +23,7 @@
 #include <string>
 
 #include <android-base/stringprintf.h>
+#include <android-base/threads.h>
 
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
@@ -31,7 +32,6 @@
 
 #include "BacktraceLog.h"
 #include "UnwindStack.h"
-#include "thread_utils.h"
 
 using android::base::StringPrintf;
 
@@ -124,7 +124,7 @@
   if (pid == BACKTRACE_CURRENT_PROCESS) {
     pid = getpid();
     if (tid == BACKTRACE_CURRENT_THREAD) {
-      tid = gettid();
+      tid = android::base::GetThreadId();
     }
   } else if (tid == BACKTRACE_CURRENT_THREAD) {
     tid = pid;
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index f6f4423..39cb995 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -28,13 +28,13 @@
 
 #include <string>
 
+#include <android-base/threads.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
 #include "BacktraceAsyncSafeLog.h"
 #include "BacktraceCurrent.h"
 #include "ThreadEntry.h"
-#include "thread_utils.h"
 
 bool BacktraceCurrent::ReadWord(uint64_t ptr, word_t* out_value) {
   if (!VerifyReadWordArgs(ptr, out_value)) {
@@ -76,7 +76,7 @@
     return UnwindFromContext(num_ignore_frames, ucontext);
   }
 
-  if (Tid() != gettid()) {
+  if (Tid() != android::base::GetThreadId()) {
     return UnwindThread(num_ignore_frames);
   }
 
@@ -114,16 +114,17 @@
 static void SignalLogOnly(int, siginfo_t*, void*) {
   ErrnoRestorer restore;
 
-  BACK_ASYNC_SAFE_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(), gettid(),
-                       THREAD_SIGNAL);
+  BACK_ASYNC_SAFE_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(),
+                       static_cast<int>(android::base::GetThreadId()), THREAD_SIGNAL);
 }
 
 static void SignalHandler(int, siginfo_t*, void* sigcontext) {
   ErrnoRestorer restore;
 
-  ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
+  ThreadEntry* entry = ThreadEntry::Get(getpid(), android::base::GetThreadId(), false);
   if (!entry) {
-    BACK_ASYNC_SAFE_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
+    BACK_ASYNC_SAFE_LOGE("pid %d, tid %d entry not found", getpid(),
+                         static_cast<int>(android::base::GetThreadId()));
     return;
   }
 
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 399721d..6a967f7 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -32,8 +32,6 @@
 #include <procinfo/process_map.h>
 #endif
 
-#include "thread_utils.h"
-
 using android::base::StringPrintf;
 
 std::string backtrace_map_t::Name() const {
diff --git a/libbacktrace/BacktracePtrace.cpp b/libbacktrace/BacktracePtrace.cpp
index bf6b16f..9da457d 100644
--- a/libbacktrace/BacktracePtrace.cpp
+++ b/libbacktrace/BacktracePtrace.cpp
@@ -28,7 +28,6 @@
 
 #include "BacktraceLog.h"
 #include "BacktracePtrace.h"
-#include "thread_utils.h"
 
 #if !defined(__APPLE__)
 static bool PtraceRead(pid_t tid, uint64_t addr, word_t* out_value) {
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index e087b2e..4e7f761 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -23,10 +23,6 @@
 #include <set>
 #include <string>
 
-#if !defined(__ANDROID__)
-#include <cutils/threads.h>
-#endif
-
 #include <backtrace/Backtrace.h>
 #include <demangle.h>
 #include <unwindstack/Elf.h>
diff --git a/libbacktrace/backtrace_benchmarks.cpp b/libbacktrace/backtrace_benchmarks.cpp
index a23e3b4..099ac60 100644
--- a/libbacktrace/backtrace_benchmarks.cpp
+++ b/libbacktrace/backtrace_benchmarks.cpp
@@ -27,6 +27,7 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/threads.h>
 
 #include <benchmark/benchmark.h>
 
@@ -154,7 +155,7 @@
 
 static void CreateBacktrace(benchmark::State& state, BacktraceMap* map, BacktraceCreateFn fn) {
   while (state.KeepRunning()) {
-    std::unique_ptr<Backtrace> backtrace(fn(getpid(), gettid(), map));
+    std::unique_ptr<Backtrace> backtrace(fn(getpid(), android::base::GetThreadId(), map));
     backtrace->Unwind(0);
   }
 }
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index 9877f29..7d1027e 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -31,9 +31,9 @@
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/threads.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
-#include <cutils/threads.h>
 
 #include <gtest/gtest.h>
 
@@ -99,7 +99,7 @@
 
 static void* OfflineThreadFunc(void* arg) {
   OfflineThreadArg* fn_arg = reinterpret_cast<OfflineThreadArg*>(arg);
-  fn_arg->tid = gettid();
+  fn_arg->tid = android::base::GetThreadId();
   test_get_context_and_wait(&fn_arg->ucontext, &fn_arg->exit_flag);
   return nullptr;
 }
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index f78a31f..06a32c7 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -47,16 +47,15 @@
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <android-base/test_utils.h>
+#include <android-base/threads.h>
 #include <android-base/unique_fd.h>
 #include <cutils/atomic.h>
-#include <cutils/threads.h>
 
 #include <gtest/gtest.h>
 
 // For the THREAD_SIGNAL definition.
 #include "BacktraceCurrent.h"
 #include "backtrace_testlib.h"
-#include "thread_utils.h"
 
 // Number of microseconds per milliseconds.
 #define US_PER_MSEC             1000
@@ -525,7 +524,7 @@
 }
 
 void VerifyLevelThread(void*) {
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), android::base::GetThreadId()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
   VERIFY_NO_ERROR(backtrace->GetError().error_code);
@@ -538,7 +537,7 @@
 }
 
 static void VerifyMaxThread(void*) {
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), android::base::GetThreadId()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
   ASSERT_EQ(BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT, backtrace->GetError().error_code);
@@ -553,7 +552,7 @@
 static void* ThreadLevelRun(void* data) {
   thread_t* thread = reinterpret_cast<thread_t*>(data);
 
-  thread->tid = gettid();
+  thread->tid = android::base::GetThreadId();
   EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0);
   return nullptr;
 }
@@ -644,7 +643,7 @@
 static void* ThreadMaxRun(void* data) {
   thread_t* thread = reinterpret_cast<thread_t*>(data);
 
-  thread->tid = gettid();
+  thread->tid = android::base::GetThreadId();
   EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0);
   return nullptr;
 }
@@ -994,7 +993,7 @@
 static void* ThreadReadTest(void* data) {
   thread_t* thread_data = reinterpret_cast<thread_t*>(data);
 
-  thread_data->tid = gettid();
+  thread_data->tid = android::base::GetThreadId();
 
   // Create two map pages.
   // Mark the second page as not-readable.
@@ -1816,7 +1815,8 @@
 
 static void TestFrameSkipNumbering(create_func_t create_func, map_create_func_t map_create_func) {
   std::unique_ptr<BacktraceMap> map(map_create_func(getpid(), false));
-  std::unique_ptr<Backtrace> backtrace(create_func(getpid(), gettid(), map.get()));
+  std::unique_ptr<Backtrace> backtrace(
+      create_func(getpid(), android::base::GetThreadId(), map.get()));
   backtrace->Unwind(1);
   ASSERT_NE(0U, backtrace->NumFrames());
   ASSERT_EQ(0U, backtrace->GetFrame(0)->num);
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 735a2f3..10e790b 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -122,7 +122,7 @@
   // Tracing a thread in a different process is not supported.
   // If map is NULL, then create the map and manage it internally.
   // If map is not NULL, the map is still owned by the caller.
-  static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
+  static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = nullptr);
 
   // Create an offline Backtrace object that can be used to do an unwind without a process
   // that is still running. By default, information is only cached in the map
@@ -145,7 +145,7 @@
   virtual ~Backtrace();
 
   // Get the current stack trace and store in the backtrace_ structure.
-  virtual bool Unwind(size_t num_ignore_frames, void* context = NULL) = 0;
+  virtual bool Unwind(size_t num_ignore_frames, void* context = nullptr) = 0;
 
   static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
                      std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
@@ -160,7 +160,7 @@
   // If the string is empty, then no valid function name was found,
   // or the pc is not in any valid map.
   virtual std::string GetFunctionName(uint64_t pc, uint64_t* offset,
-                                      const backtrace_map_t* map = NULL);
+                                      const backtrace_map_t* map = nullptr);
 
   // Fill in the map data associated with the given pc.
   virtual void FillInMap(uint64_t pc, backtrace_map_t* map);
@@ -185,7 +185,7 @@
 
   const backtrace_frame_data_t* GetFrame(size_t frame_num) {
     if (frame_num >= frames_.size()) {
-      return NULL;
+      return nullptr;
     }
     return &frames_[frame_num];
   }
diff --git a/libbacktrace/thread_utils.c b/libbacktrace/thread_utils.c
deleted file mode 100644
index e75f56e..0000000
--- a/libbacktrace/thread_utils.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2013 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 "thread_utils.h"
-
-#if !defined(__BIONIC__)
-
-// glibc doesn't implement or export tgkill.
-#include <unistd.h>
-#include <sys/syscall.h>
-
-int tgkill(int tgid, int tid, int sig) {
-  return syscall(__NR_tgkill, tgid, tid, sig);
-}
-
-#endif
diff --git a/libbacktrace/thread_utils.h b/libbacktrace/thread_utils.h
deleted file mode 100644
index 9590657..0000000
--- a/libbacktrace/thread_utils.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBBACKTRACE_THREAD_UTILS_H
-#define _LIBBACKTRACE_THREAD_UTILS_H
-
-#include <unistd.h>
-
-#if !defined(__ANDROID__)
-#include <cutils/threads.h>
-#endif
-
-__BEGIN_DECLS
-
-int tgkill(int tgid, int tid, int sig);
-
-__END_DECLS
-
-#endif /* _LIBBACKTRACE_THREAD_UTILS_H */
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index cdbb65f..37afb98 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -20,6 +20,7 @@
 libcutils_nonwindows_sources = [
     "android_get_control_file.cpp",
     "fs.cpp",
+    "hashmap.cpp",
     "multiuser.cpp",
     "socket_inaddr_any_server_unix.cpp",
     "socket_local_client_unix.cpp",
@@ -61,11 +62,9 @@
         "config_utils.cpp",
         "fs_config.cpp",
         "canned_fs_config.cpp",
-        "hashmap.cpp",
         "iosched_policy.cpp",
         "load_file.cpp",
         "native_handle.cpp",
-        "open_memstream.c",
         "record_stream.cpp",
         "sched_policy.cpp",
         "sockets.cpp",
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 15ace0e..0cc4fc0 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -90,7 +90,7 @@
     dev_t rdev;
     struct stat st;
 
-    if (TEMP_FAILURE_RETRY(fstat(fd, &st)) < 0) {
+    if (fstat(fd, &st) < 0) {
         return -1;
     }
 
@@ -135,6 +135,12 @@
     return -1;
 }
 
+static int __ashmem_check_failure(int fd, int result)
+{
+    if (result == -1 && errno == ENOTTY) __ashmem_is_ashmem(fd, 1);
+    return result;
+}
+
 int ashmem_valid(int fd)
 {
     return __ashmem_is_ashmem(fd, 0) >= 0;
@@ -182,12 +188,7 @@
 
 int ashmem_set_prot_region(int fd, int prot)
 {
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
-    }
-
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot));
+    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot)));
 }
 
 int ashmem_pin_region(int fd, size_t offset, size_t len)
@@ -195,12 +196,7 @@
     // TODO: should LP64 reject too-large offset/len?
     ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
 
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
-    }
-
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin));
+    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin)));
 }
 
 int ashmem_unpin_region(int fd, size_t offset, size_t len)
@@ -208,20 +204,10 @@
     // TODO: should LP64 reject too-large offset/len?
     ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
 
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
-    }
-
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin));
+    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin)));
 }
 
 int ashmem_get_size_region(int fd)
 {
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
-    }
-
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
+    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL)));
 }
diff --git a/libcutils/hashmap.cpp b/libcutils/hashmap.cpp
index 10e3b25..57d6006 100644
--- a/libcutils/hashmap.cpp
+++ b/libcutils/hashmap.cpp
@@ -18,7 +18,7 @@
 
 #include <assert.h>
 #include <errno.h>
-#include <cutils/threads.h>
+#include <pthread.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
@@ -36,7 +36,7 @@
     size_t bucketCount;
     int (*hash)(void* key);
     bool (*equals)(void* keyA, void* keyB);
-    mutex_t lock; 
+    pthread_mutex_t lock;
     size_t size;
 };
 
@@ -44,18 +44,18 @@
         int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) {
     assert(hash != NULL);
     assert(equals != NULL);
-    
+
     Hashmap* map = static_cast<Hashmap*>(malloc(sizeof(Hashmap)));
     if (map == NULL) {
         return NULL;
     }
-    
+
     // 0.75 load factor.
     size_t minimumBucketCount = initialCapacity * 4 / 3;
     map->bucketCount = 1;
     while (map->bucketCount <= minimumBucketCount) {
         // Bucket count must be power of 2.
-        map->bucketCount <<= 1; 
+        map->bucketCount <<= 1;
     }
 
     map->buckets = static_cast<Entry**>(calloc(map->bucketCount, sizeof(Entry*)));
@@ -63,14 +63,14 @@
         free(map);
         return NULL;
     }
-    
+
     map->size = 0;
 
     map->hash = hash;
     map->equals = equals;
-    
-    mutex_init(&map->lock);
-    
+
+    pthread_mutex_init(&map->lock, nullptr);
+
     return map;
 }
 
@@ -89,12 +89,8 @@
     h ^= (((unsigned int) h) >> 14);
     h += (h << 4);
     h ^= (((unsigned int) h) >> 10);
-       
-    return h;
-}
 
-size_t hashmapSize(Hashmap* map) {
-    return map->size;
+    return h;
 }
 
 static inline size_t calculateIndex(size_t bucketCount, int hash) {
@@ -111,7 +107,7 @@
             // Abort expansion.
             return;
         }
-        
+
         // Move over existing entries.
         size_t i;
         for (i = 0; i < map->bucketCount; i++) {
@@ -133,11 +129,11 @@
 }
 
 void hashmapLock(Hashmap* map) {
-    mutex_lock(&map->lock);
+    pthread_mutex_lock(&map->lock);
 }
 
 void hashmapUnlock(Hashmap* map) {
-    mutex_unlock(&map->lock);
+    pthread_mutex_unlock(&map->lock);
 }
 
 void hashmapFree(Hashmap* map) {
@@ -151,7 +147,7 @@
         }
     }
     free(map->buckets);
-    mutex_destroy(&map->lock);
+    pthread_mutex_destroy(&map->lock);
     free(map);
 }
 
@@ -240,54 +236,6 @@
     return NULL;
 }
 
-bool hashmapContainsKey(Hashmap* map, void* key) {
-    int hash = hashKey(map, key);
-    size_t index = calculateIndex(map->bucketCount, hash);
-
-    Entry* entry = map->buckets[index];
-    while (entry != NULL) {
-        if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
-            return true;
-        }
-        entry = entry->next;
-    }
-
-    return false;
-}
-
-void* hashmapMemoize(Hashmap* map, void* key, 
-        void* (*initialValue)(void* key, void* context), void* context) {
-    int hash = hashKey(map, key);
-    size_t index = calculateIndex(map->bucketCount, hash);
-
-    Entry** p = &(map->buckets[index]);
-    while (true) {
-        Entry* current = *p;
-
-        // Add a new entry.
-        if (current == NULL) {
-            *p = createEntry(key, hash, NULL);
-            if (*p == NULL) {
-                errno = ENOMEM;
-                return NULL;
-            }
-            void* value = initialValue(key, context);
-            (*p)->value = value;
-            map->size++;
-            expandIfNecessary(map);
-            return value;
-        }
-
-        // Return existing value.
-        if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
-            return current->value;
-        }
-
-        // Move to next entry.
-        p = &current->next;
-    }
-}
-
 void* hashmapRemove(Hashmap* map, void* key) {
     int hash = hashKey(map, key);
     size_t index = calculateIndex(map->bucketCount, hash);
@@ -310,9 +258,8 @@
     return NULL;
 }
 
-void hashmapForEach(Hashmap* map, 
-        bool (*callback)(void* key, void* value, void* context),
-        void* context) {
+void hashmapForEach(Hashmap* map, bool (*callback)(void* key, void* value, void* context),
+                    void* context) {
     size_t i;
     for (i = 0; i < map->bucketCount; i++) {
         Entry* entry = map->buckets[i];
@@ -325,34 +272,3 @@
         }
     }
 }
-
-size_t hashmapCurrentCapacity(Hashmap* map) {
-    size_t bucketCount = map->bucketCount;
-    return bucketCount * 3 / 4;
-}
-
-size_t hashmapCountCollisions(Hashmap* map) {
-    size_t collisions = 0;
-    size_t i;
-    for (i = 0; i < map->bucketCount; i++) {
-        Entry* entry = map->buckets[i];
-        while (entry != NULL) {
-            if (entry->next != NULL) {
-                collisions++;
-            }
-            entry = entry->next;
-        }
-    }
-    return collisions;
-}
-
-int hashmapIntHash(void* key) {
-    // Return the key value itself.
-    return *((int*) key);
-}
-
-bool hashmapIntEquals(void* keyA, void* keyB) {
-    int a = *((int*) keyA);
-    int b = *((int*) keyB);
-    return a == b;
-}
diff --git a/libcutils/include/cutils/hashmap.h b/libcutils/include/cutils/hashmap.h
index 5cb344c..9cfd669 100644
--- a/libcutils/include/cutils/hashmap.h
+++ b/libcutils/include/cutils/hashmap.h
@@ -16,6 +16,9 @@
 
 /**
  * Hash map.
+ *
+ * Use std::map or std::unordered_map instead.
+ * https://en.cppreference.com/w/cpp/container
  */
 
 #ifndef __HASHMAP_H
@@ -68,38 +71,17 @@
 void* hashmapGet(Hashmap* map, void* key);
 
 /**
- * Returns true if the map contains an entry for the given key.
- */
-bool hashmapContainsKey(Hashmap* map, void* key);
-
-/**
- * Gets the value for a key. If a value is not found, this function gets a 
- * value and creates an entry using the given callback.
- *
- * If memory allocation fails, the callback is not called, this function
- * returns NULL, and errno is set to ENOMEM.
- */
-void* hashmapMemoize(Hashmap* map, void* key, 
-        void* (*initialValue)(void* key, void* context), void* context);
-
-/**
  * Removes an entry from the map. Returns the removed value or NULL if no
  * entry was present.
  */
 void* hashmapRemove(Hashmap* map, void* key);
 
 /**
- * Gets the number of entries in this map.
- */
-size_t hashmapSize(Hashmap* map);
-
-/**
  * Invokes the given callback on each entry in the map. Stops iterating if
  * the callback returns false.
  */
-void hashmapForEach(Hashmap* map, 
-        bool (*callback)(void* key, void* value, void* context),
-        void* context);
+void hashmapForEach(Hashmap* map, bool (*callback)(void* key, void* value, void* context),
+                    void* context);
 
 /**
  * Concurrency support.
@@ -115,36 +97,8 @@
  */
 void hashmapUnlock(Hashmap* map);
 
-/**
- * Key utilities.
- */
-
-/**
- * Hashes int keys. 'key' is a pointer to int.
- */
-int hashmapIntHash(void* key);
-
-/**
- * Compares two int keys for equality.
- */
-bool hashmapIntEquals(void* keyA, void* keyB);
-
-/**
- * For debugging.
- */
-
-/**
- * Gets current capacity.
- */
-size_t hashmapCurrentCapacity(Hashmap* map);
-
-/**
- * Counts the number of entry collisions.
- */
-size_t hashmapCountCollisions(Hashmap* map);
-
 #ifdef __cplusplus
 }
 #endif
 
-#endif /* __HASHMAP_H */ 
+#endif /* __HASHMAP_H */
diff --git a/libcutils/include/cutils/open_memstream.h b/libcutils/include/cutils/open_memstream.h
deleted file mode 100644
index c1a81eb..0000000
--- a/libcutils/include/cutils/open_memstream.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CUTILS_OPEN_MEMSTREAM_H__
-#define __CUTILS_OPEN_MEMSTREAM_H__
-
-#include <stdio.h>
-
-#if defined(__APPLE__)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-FILE* open_memstream(char** bufp, size_t* sizep);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __APPLE__ */
-
-#endif /*__CUTILS_OPEN_MEMSTREAM_H__*/
diff --git a/libcutils/include/cutils/threads.h b/libcutils/include/cutils/threads.h
index 5727494..ba4846e 100644
--- a/libcutils/include/cutils/threads.h
+++ b/libcutils/include/cutils/threads.h
@@ -29,16 +29,16 @@
 extern "C" {
 #endif
 
-/***********************************************************************/
-/***********************************************************************/
-/*****                                                             *****/
-/*****         local thread storage                                *****/
-/*****                                                             *****/
-/***********************************************************************/
-/***********************************************************************/
+//
+// Deprecated: use android::base::GetThreadId instead, which doesn't truncate on Mac/Windows.
+//
 
 extern pid_t gettid();
 
+//
+// Deprecated: use `_Thread_local` in C or `thread_local` in C++.
+//
+
 #if !defined(_WIN32)
 
 typedef struct {
@@ -70,77 +70,6 @@
                                void*                    value,
                                thread_store_destruct_t  destroy);
 
-/***********************************************************************/
-/***********************************************************************/
-/*****                                                             *****/
-/*****         mutexes                                             *****/
-/*****                                                             *****/
-/***********************************************************************/
-/***********************************************************************/
-
-#if !defined(_WIN32)
-
-typedef pthread_mutex_t   mutex_t;
-
-#define  MUTEX_INITIALIZER  PTHREAD_MUTEX_INITIALIZER
-
-static __inline__ void  mutex_lock(mutex_t*  lock)
-{
-    pthread_mutex_lock(lock);
-}
-static __inline__ void  mutex_unlock(mutex_t*  lock)
-{
-    pthread_mutex_unlock(lock);
-}
-static __inline__ int  mutex_init(mutex_t*  lock)
-{
-    return pthread_mutex_init(lock, NULL);
-}
-static __inline__ void mutex_destroy(mutex_t*  lock)
-{
-    pthread_mutex_destroy(lock);
-}
-
-#else // !defined(_WIN32)
-
-typedef struct {
-    int                init;
-    CRITICAL_SECTION   lock[1];
-} mutex_t;
-
-#define  MUTEX_INITIALIZER  { 0, {{ NULL, 0, 0, NULL, NULL, 0 }} }
-
-static __inline__ void  mutex_lock(mutex_t*  lock)
-{
-    if (!lock->init) {
-        lock->init = 1;
-        InitializeCriticalSection( lock->lock );
-        lock->init = 2;
-    } else while (lock->init != 2)
-        Sleep(10);
-
-    EnterCriticalSection(lock->lock);
-}
-
-static __inline__ void  mutex_unlock(mutex_t*  lock)
-{
-    LeaveCriticalSection(lock->lock);
-}
-static __inline__ int  mutex_init(mutex_t*  lock)
-{
-    InitializeCriticalSection(lock->lock);
-    lock->init = 2;
-    return 0;
-}
-static __inline__ void  mutex_destroy(mutex_t*  lock)
-{
-    if (lock->init) {
-        lock->init = 0;
-        DeleteCriticalSection(lock->lock);
-    }
-}
-#endif // !defined(_WIN32)
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/libcutils/include_vndk/cutils/open_memstream.h b/libcutils/include_vndk/cutils/open_memstream.h
deleted file mode 120000
index c894084..0000000
--- a/libcutils/include_vndk/cutils/open_memstream.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/cutils/open_memstream.h
\ No newline at end of file
diff --git a/libcutils/open_memstream.c b/libcutils/open_memstream.c
deleted file mode 100644
index 9183266..0000000
--- a/libcutils/open_memstream.c
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#if defined(__APPLE__)
-
-/*
- * Implementation of the POSIX open_memstream() function, which Linux has
- * but BSD lacks.
- *
- * Summary:
- * - Works like a file-backed FILE* opened with fopen(name, "w"), but the
- *   backing is a chunk of memory rather than a file.
- * - The buffer expands as you write more data.  Seeking past the end
- *   of the file and then writing to it zero-fills the gap.
- * - The values at "*bufp" and "*sizep" should be considered read-only,
- *   and are only valid immediately after an fflush() or fclose().
- * - A '\0' is maintained just past the end of the file. This is not included
- *   in "*sizep".  (The behavior w.r.t. fseek() is not clearly defined.
- *   The spec says the null byte is written when a write() advances EOF,
- *   but it looks like glibc ensures the null byte is always found at EOF,
- *   even if you just seeked backwards.  The example on the opengroup.org
- *   page suggests that this is the expected behavior.  The null must be
- *   present after a no-op fflush(), which we can't see, so we have to save
- *   and restore it.  Annoying, but allows file truncation.)
- * - After fclose(), the caller must eventually free(*bufp).
- *
- * This is built out of funopen(), which BSD has but Linux lacks.  There is
- * no flush() operator, so we need to keep the user pointers up to date
- * after each operation.
- *
- * I don't think Windows has any of the above, but we don't need to use
- * them there, so we just supply a stub.
- */
-#include <cutils/open_memstream.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-
-#if 0
-# define DBUG(x) printf x
-#else
-# define DBUG(x) ((void)0)
-#endif
-
-/*
- * Definition of a seekable, write-only memory stream.
- */
-typedef struct {
-    char**      bufp;       /* pointer to buffer pointer */
-    size_t*     sizep;      /* pointer to eof */
-
-    size_t      allocSize;  /* size of buffer */
-    size_t      eof;        /* furthest point we've written to */
-    size_t      offset;     /* current write offset */
-    char        saved;      /* required by NUL handling */
-} MemStream;
-
-#define kInitialSize    1024
-
-/*
- * Ensure that we have enough storage to write "size" bytes at the
- * current offset.  We also have to take into account the extra '\0'
- * that we maintain just past EOF.
- *
- * Returns 0 on success.
- */
-static int ensureCapacity(MemStream* stream, int writeSize)
-{
-    DBUG(("+++ ensureCap off=%d size=%d\n", stream->offset, writeSize));
-
-    size_t neededSize = stream->offset + writeSize + 1;
-    if (neededSize <= stream->allocSize)
-        return 0;
-
-    size_t newSize;
-
-    if (stream->allocSize == 0) {
-        newSize = kInitialSize;
-    } else {
-        newSize = stream->allocSize;
-        newSize += newSize / 2;             /* expand by 3/2 */
-    }
-
-    if (newSize < neededSize)
-        newSize = neededSize;
-    DBUG(("+++ realloc %p->%p to size=%d\n",
-        stream->bufp, *stream->bufp, newSize));
-    char* newBuf = (char*) realloc(*stream->bufp, newSize);
-    if (newBuf == NULL)
-        return -1;
-
-    *stream->bufp = newBuf;
-    stream->allocSize = newSize;
-    return 0;
-}
-
-/*
- * Write data to a memstream, expanding the buffer if necessary.
- *
- * If we previously seeked beyond EOF, zero-fill the gap.
- *
- * Returns the number of bytes written.
- */
-static int write_memstream(void* cookie, const char* buf, int size)
-{
-    MemStream* stream = (MemStream*) cookie;
-
-    if (ensureCapacity(stream, size) < 0)
-        return -1;
-
-    /* seeked past EOF earlier? */
-    if (stream->eof < stream->offset) {
-        DBUG(("+++ zero-fill gap from %d to %d\n",
-            stream->eof, stream->offset-1));
-        memset(*stream->bufp + stream->eof, '\0',
-            stream->offset - stream->eof);
-    }
-
-    /* copy data, advance write pointer */
-    memcpy(*stream->bufp + stream->offset, buf, size);
-    stream->offset += size;
-
-    if (stream->offset > stream->eof) {
-        /* EOF has advanced, update it and append null byte */
-        DBUG(("+++ EOF advanced to %d, appending nul\n", stream->offset));
-        assert(stream->offset < stream->allocSize);
-        stream->eof = stream->offset;
-    } else {
-        /* within previously-written area; save char we're about to stomp */
-        DBUG(("+++ within written area, saving '%c' at %d\n",
-            *(*stream->bufp + stream->offset), stream->offset));
-        stream->saved = *(*stream->bufp + stream->offset);
-    }
-    *(*stream->bufp + stream->offset) = '\0';
-    *stream->sizep = stream->offset;
-
-    return size;
-}
-
-/*
- * Seek within a memstream.
- *
- * Returns the new offset, or -1 on failure.
- */
-static fpos_t seek_memstream(void* cookie, fpos_t offset, int whence)
-{
-    MemStream* stream = (MemStream*) cookie;
-    off_t newPosn = (off_t) offset;
-
-    if (whence == SEEK_CUR) {
-        newPosn += stream->offset;
-    } else if (whence == SEEK_END) {
-        newPosn += stream->eof;
-    }
-
-    if (newPosn < 0 || ((fpos_t)((size_t) newPosn)) != newPosn) {
-        /* bad offset - negative or huge */
-        DBUG(("+++ bogus seek offset %ld\n", (long) newPosn));
-        errno = EINVAL;
-        return (fpos_t) -1;
-    }
-
-    if (stream->offset < stream->eof) {
-        /*
-         * We were pointing to an area we'd already written to, which means
-         * we stomped on a character and must now restore it.
-         */
-        DBUG(("+++ restoring char '%c' at %d\n",
-            stream->saved, stream->offset));
-        *(*stream->bufp + stream->offset) = stream->saved;
-    }
-
-    stream->offset = (size_t) newPosn;
-
-    if (stream->offset < stream->eof) {
-        /*
-         * We're seeked backward into the stream.  Preserve the character
-         * at EOF and stomp it with a NUL.
-         */
-        stream->saved = *(*stream->bufp + stream->offset);
-        *(*stream->bufp + stream->offset) = '\0';
-        *stream->sizep = stream->offset;
-    } else {
-        /*
-         * We're positioned at, or possibly beyond, the EOF.  We want to
-         * publish the current EOF, not the current position.
-         */
-        *stream->sizep = stream->eof;
-    }
-
-    return newPosn;
-}
-
-/*
- * Close the memstream.  We free everything but the data buffer.
- */
-static int close_memstream(void* cookie)
-{
-    free(cookie);
-    return 0;
-}
-
-/*
- * Prepare a memstream.
- */
-FILE* open_memstream(char** bufp, size_t* sizep)
-{
-    FILE* fp;
-    MemStream* stream;
-
-    if (bufp == NULL || sizep == NULL) {
-        errno = EINVAL;
-        return NULL;
-    }
-
-    stream = (MemStream*) calloc(1, sizeof(MemStream));
-    if (stream == NULL)
-        return NULL;
-
-    fp = funopen(stream,
-        NULL, write_memstream, seek_memstream, close_memstream);
-    if (fp == NULL) {
-        free(stream);
-        return NULL;
-    }
-
-    *sizep = 0;
-    *bufp = NULL;
-    stream->bufp = bufp;
-    stream->sizep = sizep;
-
-    return fp;
-}
-
-
-
-
-#if 0
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-/*
- * Simple regression test.
- *
- * To test on desktop Linux with valgrind, it's possible to make a simple
- * change to open_memstream() to use fopencookie instead:
- *
- *  cookie_io_functions_t iofuncs =
- *      { NULL, write_memstream, seek_memstream, close_memstream };
- *  fp = fopencookie(stream, "w", iofuncs);
- *
- * (Some tweaks to seek_memstream are also required, as that takes a
- * pointer to an offset rather than an offset, and returns 0 or -1.)
- */
-int testMemStream(void)
-{
-    FILE *stream;
-    char *buf;
-    size_t len;
-    off_t eob;
-
-    printf("Test1\n");
-
-    /* std example */
-    stream = open_memstream(&buf, &len);
-    fprintf(stream, "hello my world");
-    fflush(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    eob = ftello(stream);
-    fseeko(stream, 0, SEEK_SET);
-    fprintf(stream, "good-bye");
-    fseeko(stream, eob, SEEK_SET);
-    fclose(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    free(buf);
-
-    printf("Test2\n");
-
-    /* std example without final seek-to-end */
-    stream = open_memstream(&buf, &len);
-    fprintf(stream, "hello my world");
-    fflush(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    eob = ftello(stream);
-    fseeko(stream, 0, SEEK_SET);
-    fprintf(stream, "good-bye");
-    //fseeko(stream, eob, SEEK_SET);
-    fclose(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    free(buf);
-
-    printf("Test3\n");
-
-    /* fancy example; should expand buffer with writes */
-    static const int kCmpLen = 1024 + 128;
-    char* cmp = malloc(kCmpLen);
-    memset(cmp, 0, 1024);
-    memset(cmp+1024, 0xff, kCmpLen-1024);
-    sprintf(cmp, "This-is-a-tes1234");
-    sprintf(cmp + 1022, "abcdef");
-
-    stream = open_memstream (&buf, &len);
-    setvbuf(stream, NULL, _IONBF, 0);   /* note: crashes in glibc with this */
-    fprintf(stream, "This-is-a-test");
-    fseek(stream, -1, SEEK_CUR);    /* broken in glibc; can use {13,SEEK_SET} */
-    fprintf(stream, "1234");
-    fseek(stream, 1022, SEEK_SET);
-    fputc('a', stream);
-    fputc('b', stream);
-    fputc('c', stream);
-    fputc('d', stream);
-    fputc('e', stream);
-    fputc('f', stream);
-    fflush(stream);
-
-    if (memcmp(buf, cmp, len+1) != 0) {
-        printf("mismatch\n");
-    } else {
-        printf("match\n");
-    }
-
-    printf("Test4\n");
-    stream = open_memstream (&buf, &len);
-    fseek(stream, 5000, SEEK_SET);
-    fseek(stream, 4096, SEEK_SET);
-    fseek(stream, -1, SEEK_SET);        /* should have no effect */
-    fputc('x', stream);
-    if (ftell(stream) == 4097)
-        printf("good\n");
-    else
-        printf("BAD: offset is %ld\n", ftell(stream));
-
-    printf("DONE\n");
-
-    return 0;
-}
-
-/* expected output:
-Test1
-buf=hello my world, len=14
-buf=good-bye world, len=14
-Test2
-buf=hello my world, len=14
-buf=good-bye, len=8
-Test3
-match
-Test4
-good
-DONE
-*/
-
-#endif
-
-#endif /* __APPLE__ */
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index 2849aa8..0cb8a4d 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -32,10 +32,6 @@
 
 #include "android_get_control_env.h"
 
-#ifndef TEMP_FAILURE_RETRY
-#define TEMP_FAILURE_RETRY(exp) (exp) // KISS implementation
-#endif
-
 #if defined(__ANDROID__)
 /* For the socket trust (credentials) check */
 #include <private/android_filesystem_config.h>
@@ -102,15 +98,15 @@
     // Compare to UNIX domain socket name, must match!
     struct sockaddr_un addr;
     socklen_t addrlen = sizeof(addr);
-    int ret = TEMP_FAILURE_RETRY(getsockname(fd, (struct sockaddr *)&addr, &addrlen));
+    int ret = getsockname(fd, (struct sockaddr*)&addr, &addrlen);
     if (ret < 0) return -1;
-    char *path = NULL;
-    if (asprintf(&path, ANDROID_SOCKET_DIR "/%s", name) < 0) return -1;
-    if (!path) return -1;
-    int cmp = strcmp(addr.sun_path, path);
-    free(path);
-    if (cmp != 0) return -1;
 
-    // It is what we think it is
-    return fd;
+    constexpr char prefix[] = ANDROID_SOCKET_DIR "/";
+    constexpr size_t prefix_size = sizeof(prefix) - sizeof('\0');
+    if ((strncmp(addr.sun_path, prefix, prefix_size) == 0) &&
+        (strcmp(addr.sun_path + prefix_size, name) == 0)) {
+        // It is what we think it is
+        return fd;
+    }
+    return -1;
 }
diff --git a/libcutils/str_parms.cpp b/libcutils/str_parms.cpp
index f5a52a7..d818c51 100644
--- a/libcutils/str_parms.cpp
+++ b/libcutils/str_parms.cpp
@@ -354,12 +354,8 @@
 char *str_parms_to_str(struct str_parms *str_parms)
 {
     char *str = NULL;
-
-    if (hashmapSize(str_parms->map) > 0)
-        hashmapForEach(str_parms->map, combine_strings, &str);
-    else
-        str = strdup("");
-    return str;
+    hashmapForEach(str_parms->map, combine_strings, &str);
+    return (str != NULL) ? str : strdup("");
 }
 
 static bool dump_entry(void* key, void* value, void* /*context*/) {
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index 52cbe8b..ee9220d 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -115,16 +115,8 @@
  */
 int __android_log_print(int prio, const char* tag, const char* fmt, ...)
 #if defined(__GNUC__)
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-    __attribute__((__format__(gnu_printf, 3, 4)))
-#else
     __attribute__((__format__(printf, 3, 4)))
 #endif
-#else
-    __attribute__((__format__(printf, 3, 4)))
-#endif
-#endif
     ;
 
 /**
@@ -133,16 +125,8 @@
  */
 int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap)
 #if defined(__GNUC__)
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-    __attribute__((__format__(gnu_printf, 3, 0)))
-#else
     __attribute__((__format__(printf, 3, 0)))
 #endif
-#else
-    __attribute__((__format__(printf, 3, 0)))
-#endif
-#endif
     ;
 
 /**
@@ -164,16 +148,8 @@
                           ...)
 #if defined(__GNUC__)
     __attribute__((__noreturn__))
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-    __attribute__((__format__(gnu_printf, 3, 4)))
-#else
     __attribute__((__format__(printf, 3, 4)))
 #endif
-#else
-    __attribute__((__format__(printf, 3, 4)))
-#endif
-#endif
     ;
 
 #ifndef log_id_t_defined
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index 21fc7cc..1314330 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -40,6 +40,19 @@
 #endif
 #endif
 
+/*
+ * 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.
+ */
+#ifdef __clang_analyzer__
+extern void __fake_use_va_args(int, ...);
+#define __FAKE_USE_VA_ARGS(...) __fake_use_va_args(0, ##__VA_ARGS__)
+#else
+#define __FAKE_USE_VA_ARGS(...) ((void)(0))
+#endif
+
 /* --------------------------------------------------------------------- */
 
 /*
@@ -112,7 +125,7 @@
 #define LOG_ALWAYS_FATAL_IF(cond, ...)                              \
   ((__predict_false(cond))                                          \
        ? ((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__)) \
-       : (void)0)
+       : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 
 #ifndef LOG_ALWAYS_FATAL
@@ -128,10 +141,10 @@
 #if LOG_NDEBUG
 
 #ifndef LOG_FATAL_IF
-#define LOG_FATAL_IF(cond, ...) ((void)0)
+#define LOG_FATAL_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
 #endif
 #ifndef LOG_FATAL
-#define LOG_FATAL(...) ((void)0)
+#define LOG_FATAL(...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
 #endif
 
 #else
@@ -175,11 +188,12 @@
 #ifndef ALOGV
 #define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
 #if LOG_NDEBUG
-#define ALOGV(...)          \
-  do {                      \
-    if (false) {            \
-      __ALOGV(__VA_ARGS__); \
-    }                       \
+#define ALOGV(...)                   \
+  do {                               \
+    __FAKE_USE_VA_ARGS(__VA_ARGS__); \
+    if (false) {                     \
+      __ALOGV(__VA_ARGS__);          \
+    }                                \
   } while (false)
 #else
 #define ALOGV(...) __ALOGV(__VA_ARGS__)
@@ -188,11 +202,11 @@
 
 #ifndef ALOGV_IF
 #if LOG_NDEBUG
-#define ALOGV_IF(cond, ...) ((void)0)
+#define ALOGV_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
 #else
 #define ALOGV_IF(cond, ...)                                                  \
   ((__predict_false(cond)) ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
-                           : (void)0)
+                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 #endif
 
@@ -206,7 +220,7 @@
 #ifndef ALOGD_IF
 #define ALOGD_IF(cond, ...)                                                \
   ((__predict_false(cond)) ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
-                           : (void)0)
+                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 
 /*
@@ -219,7 +233,7 @@
 #ifndef ALOGI_IF
 #define ALOGI_IF(cond, ...)                                               \
   ((__predict_false(cond)) ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
-                           : (void)0)
+                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 
 /*
@@ -232,7 +246,7 @@
 #ifndef ALOGW_IF
 #define ALOGW_IF(cond, ...)                                               \
   ((__predict_false(cond)) ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
-                           : (void)0)
+                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 
 /*
@@ -245,7 +259,7 @@
 #ifndef ALOGE_IF
 #define ALOGE_IF(cond, ...)                                                \
   ((__predict_false(cond)) ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
-                           : (void)0)
+                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 
 /* --------------------------------------------------------------------- */
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index d118563..93b9d4e 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -184,7 +184,7 @@
       hdr_size = sizeof(entry_v1);
     }
     if ((hdr_size < sizeof(entry_v1)) || (hdr_size > sizeof(entry))) {
-      return NULL;
+      return nullptr;
     }
     return reinterpret_cast<char*>(buf) + hdr_size;
   }
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
index 965de37..b927b46 100644
--- a/liblog/include/private/android_logger.h
+++ b/liblog/include/private/android_logger.h
@@ -173,7 +173,7 @@
 #if defined(_USING_LIBCXX)
   operator std::string() {
     if (ret) return std::string("");
-    const char* cp = NULL;
+    const char* cp = nullptr;
     ssize_t len = android_log_write_list_buffer(ctx, &cp);
     if (len < 0) ret = len;
     if (!cp || (len <= 0)) return std::string("");
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
index 4fbf729..933d65a 100644
--- a/libmemunreachable/tests/ThreadCapture_test.cpp
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -32,6 +32,8 @@
 #include "ScopedDisableMalloc.h"
 #include "ScopedPipe.h"
 
+#include <android-base/threads.h>
+
 using namespace std::chrono_literals;
 
 namespace android {
@@ -260,7 +262,7 @@
 
       ThreadCapture thread_capture(ret, heap);
       thread_capture.InjectTestFunc([&](pid_t tid) {
-        syscall(SYS_tgkill, ret, tid, SIGKILL);
+        tgkill(ret, tid, SIGKILL);
         usleep(10000);
       });
       auto list_tids = allocator::vector<pid_t>(heap);
@@ -319,7 +321,7 @@
 
           ThreadCapture thread_capture(child, heap);
           thread_capture.InjectTestFunc([&](pid_t tid) {
-            syscall(SYS_tgkill, child, tid, sig);
+            tgkill(child, tid, sig);
             usleep(10000);
           });
           auto list_tids = allocator::vector<pid_t>(heap);
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index 3563fc1..19a1783 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -53,8 +53,21 @@
 #if defined(__ANDROID__)
 // Look up linker namespace by class_loader. Returns nullptr if
 // there is no namespace associated with the class_loader.
+// TODO(b/79940628): move users to FindNativeLoaderNamespaceByClassLoader and remove this function.
 __attribute__((visibility("default")))
 android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+// That version works with native bridge namespaces, but requires use of OpenNativeLibrary.
+class NativeLoaderNamespace;
+__attribute__((visibility("default")))
+NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(
+    JNIEnv* env, jobject class_loader);
+// Load library.  Unlinke OpenNativeLibrary above couldn't create namespace on demand, but does
+// not require access to JNIEnv either.
+__attribute__((visibility("default")))
+void* OpenNativeLibrary(NativeLoaderNamespace* ns,
+                        const char* path,
+                        bool* needs_native_bridge,
+                        std::string* error_msg);
 #endif
 
 __attribute__((visibility("default")))
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 7fef106..67c1c10 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -29,6 +29,7 @@
 #include "nativebridge/native_bridge.h"
 
 #include <algorithm>
+#include <list>
 #include <memory>
 #include <mutex>
 #include <string>
@@ -150,15 +151,14 @@
  public:
   LibraryNamespaces() : initialized_(false) { }
 
-  bool Create(JNIEnv* env,
-              uint32_t target_sdk_version,
-              jobject class_loader,
-              bool is_shared,
-              bool is_for_vendor,
-              jstring java_library_path,
-              jstring java_permitted_path,
-              NativeLoaderNamespace* ns,
-              std::string* error_msg) {
+  NativeLoaderNamespace* Create(JNIEnv* env,
+                                uint32_t target_sdk_version,
+                                jobject class_loader,
+                                bool is_shared,
+                                bool is_for_vendor,
+                                jstring java_library_path,
+                                jstring java_permitted_path,
+                                std::string* error_msg) {
     std::string library_path; // empty string by default.
 
     if (java_library_path != nullptr) {
@@ -182,10 +182,10 @@
     }
 
     if (!initialized_ && !InitPublicNamespace(library_path.c_str(), error_msg)) {
-      return false;
+      return nullptr;
     }
 
-    bool found = FindNamespaceByClassLoader(env, class_loader, nullptr);
+    bool found = FindNamespaceByClassLoader(env, class_loader);
 
     LOG_ALWAYS_FATAL_IF(found,
                         "There is already a namespace associated with this classloader");
@@ -199,13 +199,12 @@
       namespace_type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
     }
 
-    NativeLoaderNamespace parent_ns;
-    bool found_parent_namespace = FindParentNamespaceByClassLoader(env, class_loader, &parent_ns);
+    NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
 
     bool is_native_bridge = false;
 
-    if (found_parent_namespace) {
-      is_native_bridge = !parent_ns.is_android_namespace();
+    if (parent_ns != nullptr) {
+      is_native_bridge = !parent_ns->is_android_namespace();
     } else if (!library_path.empty()) {
       is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
     }
@@ -251,15 +250,17 @@
 
     NativeLoaderNamespace native_loader_ns;
     if (!is_native_bridge) {
+      android_namespace_t* android_parent_ns =
+          parent_ns == nullptr ? nullptr : parent_ns->get_android_ns();
       android_namespace_t* ns = android_create_namespace(namespace_name,
                                                          nullptr,
                                                          library_path.c_str(),
                                                          namespace_type,
                                                          permitted_path.c_str(),
-                                                         parent_ns.get_android_ns());
+                                                         android_parent_ns);
       if (ns == nullptr) {
         *error_msg = dlerror();
-        return false;
+        return nullptr;
       }
 
       // Note that when vendor_ns is not configured this function will return nullptr
@@ -269,49 +270,50 @@
 
       if (!android_link_namespaces(ns, nullptr, system_exposed_libraries.c_str())) {
         *error_msg = dlerror();
-        return false;
+        return nullptr;
       }
 
       if (vndk_ns != nullptr && !system_vndksp_libraries_.empty()) {
         // vendor apks are allowed to use VNDK-SP libraries.
         if (!android_link_namespaces(ns, vndk_ns, system_vndksp_libraries_.c_str())) {
           *error_msg = dlerror();
-          return false;
+          return nullptr;
         }
       }
 
       if (!vendor_public_libraries_.empty()) {
         if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
           *error_msg = dlerror();
-          return false;
+          return nullptr;
         }
       }
 
       native_loader_ns = NativeLoaderNamespace(ns);
     } else {
+      native_bridge_namespace_t* native_bridge_parent_namespace =
+          parent_ns == nullptr ? nullptr : parent_ns->get_native_bridge_ns();
       native_bridge_namespace_t* ns = NativeBridgeCreateNamespace(namespace_name,
                                                                   nullptr,
                                                                   library_path.c_str(),
                                                                   namespace_type,
                                                                   permitted_path.c_str(),
-                                                                  parent_ns.get_native_bridge_ns());
-
+                                                                  native_bridge_parent_namespace);
       if (ns == nullptr) {
         *error_msg = NativeBridgeGetError();
-        return false;
+        return nullptr;
       }
 
       native_bridge_namespace_t* vendor_ns = NativeBridgeGetVendorNamespace();
 
       if (!NativeBridgeLinkNamespaces(ns, nullptr, system_exposed_libraries.c_str())) {
         *error_msg = NativeBridgeGetError();
-        return false;
+        return nullptr;
       }
 
       if (!vendor_public_libraries_.empty()) {
         if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
           *error_msg = NativeBridgeGetError();
-          return false;
+          return nullptr;
         }
       }
 
@@ -320,24 +322,19 @@
 
     namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
 
-    *ns = native_loader_ns;
-    return true;
+    return &(namespaces_.back().second);
   }
 
-  bool FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader, NativeLoaderNamespace* ns) {
+  NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
     auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
                 [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
                   return env->IsSameObject(value.first, class_loader);
                 });
     if (it != namespaces_.end()) {
-      if (ns != nullptr) {
-        *ns = it->second;
-      }
-
-      return true;
+      return &it->second;
     }
 
-    return false;
+    return nullptr;
   }
 
   void Initialize() {
@@ -557,24 +554,23 @@
     return env->CallObjectMethod(class_loader, get_parent);
   }
 
-  bool FindParentNamespaceByClassLoader(JNIEnv* env,
-                                        jobject class_loader,
-                                        NativeLoaderNamespace* ns) {
+  NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
     jobject parent_class_loader = GetParentClassLoader(env, class_loader);
 
     while (parent_class_loader != nullptr) {
-      if (FindNamespaceByClassLoader(env, parent_class_loader, ns)) {
-        return true;
+      NativeLoaderNamespace* ns;
+      if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
+        return ns;
       }
 
       parent_class_loader = GetParentClassLoader(env, parent_class_loader);
     }
 
-    return false;
+    return nullptr;
   }
 
   bool initialized_;
-  std::vector<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
+  std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
   std::string system_public_libraries_;
   std::string vendor_public_libraries_;
   std::string oem_public_libraries_;
@@ -614,7 +610,6 @@
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
 
   std::string error_msg;
-  NativeLoaderNamespace ns;
   bool success = g_namespaces->Create(env,
                                       target_sdk_version,
                                       class_loader,
@@ -622,8 +617,7 @@
                                       is_for_vendor,
                                       library_path,
                                       permitted_path,
-                                      &ns,
-                                      &error_msg);
+                                      &error_msg) != nullptr;
   if (!success) {
     return env->NewStringUTF(error_msg.c_str());
   }
@@ -649,43 +643,24 @@
   }
 
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-  NativeLoaderNamespace ns;
+  NativeLoaderNamespace* ns;
 
-  if (!g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
+  if ((ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader)) == nullptr) {
     // This is the case where the classloader was not created by ApplicationLoaders
     // In this case we create an isolated not-shared namespace for it.
-    if (!g_namespaces->Create(env,
-                              target_sdk_version,
-                              class_loader,
-                              false /* is_shared */,
-                              false /* is_for_vendor */,
-                              library_path,
-                              nullptr,
-                              &ns,
-                              error_msg)) {
+    if ((ns = g_namespaces->Create(env,
+                                   target_sdk_version,
+                                   class_loader,
+                                   false /* is_shared */,
+                                   false /* is_for_vendor */,
+                                   library_path,
+                                   nullptr,
+                                   error_msg)) == nullptr) {
       return nullptr;
     }
   }
 
-  if (ns.is_android_namespace()) {
-    android_dlextinfo extinfo;
-    extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
-    extinfo.library_namespace = ns.get_android_ns();
-
-    void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
-    if (handle == nullptr) {
-      *error_msg = dlerror();
-    }
-    *needs_native_bridge = false;
-    return handle;
-  } else {
-    void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns.get_native_bridge_ns());
-    if (handle == nullptr) {
-      *error_msg = NativeBridgeGetError();
-    }
-    *needs_native_bridge = true;
-    return handle;
-  }
+  return OpenNativeLibrary(ns, path, needs_native_bridge, error_msg);
 #else
   UNUSED(env, target_sdk_version, class_loader);
 
@@ -741,18 +716,45 @@
 }
 
 #if defined(__ANDROID__)
+void* OpenNativeLibrary(NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge,
+                        std::string* error_msg) {
+  if (ns->is_android_namespace()) {
+    android_dlextinfo extinfo;
+    extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+    extinfo.library_namespace = ns->get_android_ns();
+
+    void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
+    if (handle == nullptr) {
+      *error_msg = dlerror();
+    }
+    *needs_native_bridge = false;
+    return handle;
+  } else {
+    void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns->get_native_bridge_ns());
+    if (handle == nullptr) {
+      *error_msg = NativeBridgeGetError();
+    }
+    *needs_native_bridge = true;
+    return handle;
+  }
+}
+
 // native_bridge_namespaces are not supported for callers of this function.
 // This function will return nullptr in the case when application is running
 // on native bridge.
 android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-  NativeLoaderNamespace ns;
-  if (g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
-    return ns.is_android_namespace() ? ns.get_android_ns() : nullptr;
+  NativeLoaderNamespace* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+  if (ns != nullptr) {
+    return ns->is_android_namespace() ? ns->get_android_ns() : nullptr;
   }
 
   return nullptr;
 }
+NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+  return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+}
 #endif
 
 }; //  android namespace
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index b0bc497..c38279d 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -2,6 +2,7 @@
     srcs: ["processgroup.cpp"],
     name: "libprocessgroup",
     host_supported: true,
+    recovery_available: true,
     shared_libs: ["libbase"],
     export_include_dirs: ["include"],
     cflags: [
diff --git a/libprocinfo/include/procinfo/process.h b/libprocinfo/include/procinfo/process.h
index db56fc1..9278e18 100644
--- a/libprocinfo/include/procinfo/process.h
+++ b/libprocinfo/include/procinfo/process.h
@@ -56,23 +56,25 @@
 };
 
 // Parse the contents of /proc/<tid>/status into |process_info|.
-bool GetProcessInfo(pid_t tid, ProcessInfo* process_info);
+bool GetProcessInfo(pid_t tid, ProcessInfo* process_info, std::string* error = nullptr);
 
 // Parse the contents of <fd>/status into |process_info|.
 // |fd| should be an fd pointing at a /proc/<pid> directory.
-bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info);
+bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info, std::string* error = nullptr);
 
 // Fetch the list of threads from a given process's /proc/<pid> directory.
 // |fd| should be an fd pointing at a /proc/<pid> directory.
 template <typename Collection>
-auto GetProcessTidsFromProcPidFd(int fd, Collection* out) ->
+auto GetProcessTidsFromProcPidFd(int fd, Collection* out, std::string* error = nullptr) ->
     typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
   out->clear();
 
   int task_fd = openat(fd, "task", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
   std::unique_ptr<DIR, int (*)(DIR*)> dir(fdopendir(task_fd), closedir);
   if (!dir) {
-    PLOG(ERROR) << "failed to open task directory";
+    if (error != nullptr) {
+      *error = "failed to open task directory";
+    }
     return false;
   }
 
@@ -81,7 +83,9 @@
     if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) {
       pid_t tid;
       if (!android::base::ParseInt(dent->d_name, &tid, 1, std::numeric_limits<pid_t>::max())) {
-        LOG(ERROR) << "failed to parse task id: " << dent->d_name;
+        if (error != nullptr) {
+          *error = std::string("failed to parse task id: ") + dent->d_name;
+        }
         return false;
       }
 
@@ -93,21 +97,25 @@
 }
 
 template <typename Collection>
-auto GetProcessTids(pid_t pid, Collection* out) ->
+auto GetProcessTids(pid_t pid, Collection* out, std::string* error = nullptr) ->
     typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
   char task_path[PATH_MAX];
   if (snprintf(task_path, PATH_MAX, "/proc/%d", pid) >= PATH_MAX) {
-    LOG(ERROR) << "task path overflow (pid = " << pid << ")";
+    if (error != nullptr) {
+      *error = "task path overflow (pid = " + std::to_string(pid) + ")";
+    }
     return false;
   }
 
   android::base::unique_fd fd(open(task_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
   if (fd == -1) {
-    PLOG(ERROR) << "failed to open " << task_path;
+    if (error != nullptr) {
+      *error = std::string("failed to open ") + task_path;
+    }
     return false;
   }
 
-  return GetProcessTidsFromProcPidFd(fd.get(), out);
+  return GetProcessTidsFromProcPidFd(fd.get(), out, error);
 }
 
 #endif
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp
index 6e5be6e..9194cf3 100644
--- a/libprocinfo/process.cpp
+++ b/libprocinfo/process.cpp
@@ -31,17 +31,19 @@
 namespace android {
 namespace procinfo {
 
-bool GetProcessInfo(pid_t tid, ProcessInfo* process_info) {
+bool GetProcessInfo(pid_t tid, ProcessInfo* process_info, std::string* error) {
   char path[PATH_MAX];
   snprintf(path, sizeof(path), "/proc/%d", tid);
 
   unique_fd dirfd(open(path, O_DIRECTORY | O_RDONLY));
   if (dirfd == -1) {
-    PLOG(ERROR) << "failed to open " << path;
+    if (error != nullptr) {
+      *error = std::string("failed to open ") + path;
+    }
     return false;
   }
 
-  return GetProcessInfoFromProcPidFd(dirfd.get(), process_info);
+  return GetProcessInfoFromProcPidFd(dirfd.get(), process_info, error);
 }
 
 static ProcessState parse_state(const char* state) {
@@ -62,17 +64,21 @@
   }
 }
 
-bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info) {
+bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info, std::string* error) {
   int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC);
 
   if (status_fd == -1) {
-    PLOG(ERROR) << "failed to open status fd in GetProcessInfoFromProcPidFd";
+    if (error != nullptr) {
+      *error = "failed to open status fd in GetProcessInfoFromProcPidFd";
+    }
     return false;
   }
 
   std::unique_ptr<FILE, decltype(&fclose)> fp(fdopen(status_fd, "r"), fclose);
   if (!fp) {
-    PLOG(ERROR) << "failed to open status file in GetProcessInfoFromProcPidFd";
+    if (error != nullptr) {
+      *error = "failed to open status file in GetProcessInfoFromProcPidFd";
+    }
     close(status_fd);
     return false;
   }
diff --git a/libsparse/backed_block.cpp b/libsparse/backed_block.cpp
index 7f5632e..f3d8022 100644
--- a/libsparse/backed_block.cpp
+++ b/libsparse/backed_block.cpp
@@ -133,7 +133,7 @@
                             struct backed_block* start, struct backed_block* end) {
   struct backed_block* bb;
 
-  if (start == NULL) {
+  if (start == nullptr) {
     start = from->data_blocks;
   }
 
@@ -142,12 +142,12 @@
       ;
   }
 
-  if (start == NULL || end == NULL) {
+  if (start == nullptr || end == nullptr) {
     return;
   }
 
-  from->last_used = NULL;
-  to->last_used = NULL;
+  from->last_used = nullptr;
+  to->last_used = nullptr;
   if (from->data_blocks == start) {
     from->data_blocks = end->next;
   } else {
@@ -161,7 +161,7 @@
 
   if (!to->data_blocks) {
     to->data_blocks = start;
-    end->next = NULL;
+    end->next = nullptr;
   } else {
     for (bb = to->data_blocks; bb; bb = bb->next) {
       if (!bb->next || bb->next->block > start->block) {
@@ -230,7 +230,7 @@
 static int queue_bb(struct backed_block_list* bbl, struct backed_block* new_bb) {
   struct backed_block* bb;
 
-  if (bbl->data_blocks == NULL) {
+  if (bbl->data_blocks == nullptr) {
     bbl->data_blocks = new_bb;
     return 0;
   }
@@ -253,7 +253,7 @@
   for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
     ;
 
-  if (bb->next == NULL) {
+  if (bb->next == nullptr) {
     bb->next = new_bb;
   } else {
     new_bb->next = bb->next;
@@ -273,7 +273,7 @@
 int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
                           unsigned int block) {
   struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
-  if (bb == NULL) {
+  if (bb == nullptr) {
     return -ENOMEM;
   }
 
@@ -281,7 +281,7 @@
   bb->len = len;
   bb->type = BACKED_BLOCK_FILL;
   bb->fill.val = fill_val;
-  bb->next = NULL;
+  bb->next = nullptr;
 
   return queue_bb(bbl, bb);
 }
@@ -290,7 +290,7 @@
 int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
                           unsigned int block) {
   struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
-  if (bb == NULL) {
+  if (bb == nullptr) {
     return -ENOMEM;
   }
 
@@ -298,7 +298,7 @@
   bb->len = len;
   bb->type = BACKED_BLOCK_DATA;
   bb->data.data = data;
-  bb->next = NULL;
+  bb->next = nullptr;
 
   return queue_bb(bbl, bb);
 }
@@ -307,7 +307,7 @@
 int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
                           unsigned int len, unsigned int block) {
   struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
-  if (bb == NULL) {
+  if (bb == nullptr) {
     return -ENOMEM;
   }
 
@@ -316,7 +316,7 @@
   bb->type = BACKED_BLOCK_FILE;
   bb->file.filename = strdup(filename);
   bb->file.offset = offset;
-  bb->next = NULL;
+  bb->next = nullptr;
 
   return queue_bb(bbl, bb);
 }
@@ -325,7 +325,7 @@
 int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
                         unsigned int block) {
   struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
-  if (bb == NULL) {
+  if (bb == nullptr) {
     return -ENOMEM;
   }
 
@@ -334,7 +334,7 @@
   bb->type = BACKED_BLOCK_FD;
   bb->fd.fd = fd;
   bb->fd.offset = offset;
-  bb->next = NULL;
+  bb->next = nullptr;
 
   return queue_bb(bbl, bb);
 }
@@ -350,7 +350,7 @@
   }
 
   new_bb = reinterpret_cast<backed_block*>(malloc(sizeof(struct backed_block)));
-  if (new_bb == NULL) {
+  if (new_bb == nullptr) {
     return -ENOMEM;
   }
 
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 3d5fb0c..5865786 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -210,6 +210,37 @@
 		int (*write)(void *priv, const void *data, size_t len), void *priv);
 
 /**
+ * sparse_file_callback_typed - call a callback for blocks based on type
+ *
+ * @s - sparse file cookie
+ * @sparse - write in the Android sparse file format
+ * @data_write - function to call for data blocks. must not be null
+ * @fd_write - function to call for fd blocks
+ * @fill_write - function to call for fill blocks
+ * @skip_write - function to call for skip blocks
+ * @priv - value that will be passed as the first argument to each write
+ *
+ * Writes a sparse file by calling callback functions.  If sparse is true, the
+ * file will be written in the Android sparse file format, and fill and skip blocks
+ * along with metadata will be written with data_write. If sparse is false, the file
+ * will be expanded into normal format and fill and skip blocks will be written with
+ * the given callbacks.
+ * If a callback function is provided, the library will not unroll data into a buffer,
+ * and will instead pass it directly to the caller for custom implementation. If a
+ * callback is not provided, that type of block will be converted into a void* and
+ * written with data_write. If no callbacks other than data are provided, the behavior
+ * is the same as sparse_file_callback(). The callback should return negative on error,
+ * 0 on success.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_callback_typed(struct sparse_file* s, bool sparse,
+                               int (*data_write)(void* priv, const void* data, size_t len),
+                               int (*fd_write)(void* priv, int fd, size_t len),
+                               int (*fill_write)(void* priv, uint32_t fill_val, size_t len),
+                               int (*skip_write)(void* priv, int64_t len), void* priv);
+
+/**
  * sparse_file_foreach_chunk - call a callback for data blocks in sparse file
  *
  * @s - sparse file cookie
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index 5388e77..8a21dab 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -29,6 +29,8 @@
 #include <unistd.h>
 #include <zlib.h>
 
+#include <algorithm>
+
 #include "defs.h"
 #include "output_file.h"
 #include "sparse_crc32.h"
@@ -48,13 +50,6 @@
 #define off64_t off_t
 #endif
 
-#define min(a, b)        \
-  ({                     \
-    typeof(a) _a = (a);  \
-    typeof(b) _b = (b);  \
-    (_a < _b) ? _a : _b; \
-  })
-
 #define SPARSE_HEADER_MAJOR_VER 1
 #define SPARSE_HEADER_MINOR_VER 0
 #define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
@@ -67,11 +62,14 @@
   int (*skip)(struct output_file*, int64_t);
   int (*pad)(struct output_file*, int64_t);
   int (*write)(struct output_file*, void*, size_t);
+  int (*write_fd)(struct output_file*, int, size_t);
+  int (*write_fill)(struct output_file*, uint32_t, size_t);
   void (*close)(struct output_file*);
 };
 
 struct sparse_file_ops {
   int (*write_data_chunk)(struct output_file* out, unsigned int len, void* data);
+  int (*write_fd_chunk)(struct output_file* out, unsigned int len, int fd);
   int (*write_fill_chunk)(struct output_file* out, unsigned int len, uint32_t fill_val);
   int (*write_skip_chunk)(struct output_file* out, int64_t len);
   int (*write_end_chunk)(struct output_file* out);
@@ -108,11 +106,96 @@
 struct output_file_callback {
   struct output_file out;
   void* priv;
-  int (*write)(void* priv, const void* buf, size_t len);
+  int (*data_write)(void* priv, const void* data, size_t len);
+  int (*fd_write)(void* priv, int fd, size_t len);
+  int (*fill_write)(void* priv, uint32_t fill_val, size_t len);
+  int (*skip_write)(void* priv, off64_t len);
 };
 
 #define to_output_file_callback(_o) container_of((_o), struct output_file_callback, out)
 
+union handle_data {
+  void* data_ptr;
+  int fd;
+};
+
+static int default_file_write_fd(struct output_file* out, int fd, size_t len) {
+  int ret;
+  int64_t aligned_offset;
+  int aligned_diff;
+  uint64_t buffer_size;
+  char* ptr;
+  int64_t offset = lseek64(fd, 0, SEEK_CUR);
+
+  if (offset < 0) {
+    return -errno;
+  }
+
+  aligned_offset = offset & ~(4096 - 1);
+  aligned_diff = offset - aligned_offset;
+  buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
+
+#ifndef _WIN32
+  if (buffer_size > SIZE_MAX) {
+    return -E2BIG;
+  }
+  char* data =
+      reinterpret_cast<char*>(mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset));
+  if (data == MAP_FAILED) {
+    return -errno;
+  }
+  ptr = data + aligned_diff;
+#else
+  char* data = reinterpret_cast<char*>(malloc(len));
+  if (!data) {
+    return -errno;
+  }
+  ret = read_all(fd, data, len);
+  if (ret < 0) {
+    free(data);
+    return -errno;
+  }
+  ptr = data;
+#endif
+
+  ret = out->ops->write(out, ptr, len);
+
+  if (out->use_crc) {
+    out->crc32 = sparse_crc32(out->crc32, ptr, len);
+  }
+
+#ifndef _WIN32
+  munmap(data, buffer_size);
+#else
+  free(data);
+#endif
+
+  return ret;
+}
+
+static int default_file_write_fill(struct output_file* out, uint32_t fill_val, size_t len) {
+  int ret;
+  unsigned int i;
+  unsigned int write_len;
+
+  /* Initialize fill_buf with the fill_val */
+  for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
+    out->fill_buf[i] = fill_val;
+  }
+
+  while (len) {
+    write_len = std::min(len, static_cast<size_t>(out->block_size));
+    ret = out->ops->write(out, out->fill_buf, write_len);
+    if (ret < 0) {
+      return ret;
+    }
+
+    len -= write_len;
+  }
+
+  return 0;
+}
+
 static int file_open(struct output_file* out, int fd) {
   struct output_file_normal* outn = to_output_file_normal(out);
 
@@ -176,6 +259,8 @@
     .skip = file_skip,
     .pad = file_pad,
     .write = file_write,
+    .write_fd = default_file_write_fd,
+    .write_fill = default_file_write_fill,
     .close = file_close,
 };
 
@@ -231,9 +316,9 @@
   struct output_file_gz* outgz = to_output_file_gz(out);
 
   while (len > 0) {
-    ret = gzwrite(outgz->gz_fd, data, min(len, (unsigned int)INT_MAX));
+    ret = gzwrite(outgz->gz_fd, data, std::min(len, static_cast<size_t>(INT_MAX)));
     if (ret == 0) {
-      error("gzwrite %s", gzerror(outgz->gz_fd, NULL));
+      error("gzwrite %s", gzerror(outgz->gz_fd, nullptr));
       return -1;
     }
     len -= ret;
@@ -255,6 +340,8 @@
     .skip = gz_file_skip,
     .pad = gz_file_pad,
     .write = gz_file_write,
+    .write_fd = default_file_write_fd,
+    .write_fill = default_file_write_fill,
     .close = gz_file_close,
 };
 
@@ -267,9 +354,13 @@
   int to_write;
   int ret;
 
+  if (outc->skip_write) {
+    return outc->skip_write(outc->priv, off);
+  }
+
   while (off > 0) {
-    to_write = min(off, (int64_t)INT_MAX);
-    ret = outc->write(outc->priv, NULL, to_write);
+    to_write = std::min(off, static_cast<int64_t>(INT_MAX));
+    ret = outc->data_write(outc->priv, nullptr, to_write);
     if (ret < 0) {
       return ret;
     }
@@ -285,8 +376,23 @@
 
 static int callback_file_write(struct output_file* out, void* data, size_t len) {
   struct output_file_callback* outc = to_output_file_callback(out);
+  return outc->data_write(outc->priv, data, len);
+}
 
-  return outc->write(outc->priv, data, len);
+static int callback_file_write_fd(struct output_file* out, int fd, size_t len) {
+  struct output_file_callback* outc = to_output_file_callback(out);
+  if (outc->fd_write) {
+    return outc->fd_write(outc->priv, fd, len);
+  }
+  return default_file_write_fd(out, fd, len);
+}
+
+static int callback_file_write_fill(struct output_file* out, uint32_t fill_val, size_t len) {
+  struct output_file_callback* outc = to_output_file_callback(out);
+  if (outc->fill_write) {
+    return outc->fill_write(outc->priv, fill_val, len);
+  }
+  return default_file_write_fill(out, fill_val, len);
 }
 
 static void callback_file_close(struct output_file* out) {
@@ -300,6 +406,8 @@
     .skip = callback_file_skip,
     .pad = callback_file_pad,
     .write = callback_file_write,
+    .write_fd = callback_file_write_fd,
+    .write_fill = callback_file_write_fill,
     .close = callback_file_close,
 };
 
@@ -376,7 +484,8 @@
   return 0;
 }
 
-static int write_sparse_data_chunk(struct output_file* out, unsigned int len, void* data) {
+static int write_sparse_data_chunk_variant(struct output_file* out, unsigned int len,
+                                           handle_data data, bool is_fd) {
   chunk_header_t chunk_header;
   int rnd_up_len, zero_len;
   int ret;
@@ -393,7 +502,16 @@
   ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
 
   if (ret < 0) return -1;
-  ret = out->ops->write(out, data, len);
+
+  if (is_fd) {
+    // CRC is handled by write_fd
+    ret = out->ops->write_fd(out, data.fd, len);
+  } else {
+    ret = out->ops->write(out, data.data_ptr, len);
+    if (out->use_crc) {
+      out->crc32 = sparse_crc32(out->crc32, data.data_ptr, len);
+    }
+  }
   if (ret < 0) return -1;
   if (zero_len) {
     ret = out->ops->write(out, out->zero_buf, zero_len);
@@ -401,7 +519,6 @@
   }
 
   if (out->use_crc) {
-    out->crc32 = sparse_crc32(out->crc32, data, len);
     if (zero_len) out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
   }
 
@@ -411,6 +528,16 @@
   return 0;
 }
 
+static int write_sparse_data_chunk(struct output_file* out, unsigned int len, void* data_ptr) {
+  handle_data data = {data_ptr};
+  return write_sparse_data_chunk_variant(out, len, data, false /* isFd */);
+}
+
+static int write_sparse_fd_chunk(struct output_file* out, unsigned int len, int fd) {
+  handle_data data = {.fd = fd};
+  return write_sparse_data_chunk_variant(out, len, data, true /* isFd */);
+}
+
 int write_sparse_end_chunk(struct output_file* out) {
   chunk_header_t chunk_header;
   int ret;
@@ -438,16 +565,22 @@
 
 static struct sparse_file_ops sparse_file_ops = {
     .write_data_chunk = write_sparse_data_chunk,
+    .write_fd_chunk = write_sparse_fd_chunk,
     .write_fill_chunk = write_sparse_fill_chunk,
     .write_skip_chunk = write_sparse_skip_chunk,
     .write_end_chunk = write_sparse_end_chunk,
 };
 
-static int write_normal_data_chunk(struct output_file* out, unsigned int len, void* data) {
+static int write_normal_data_chunk_variant(struct output_file* out, unsigned int len,
+                                           handle_data data, bool isFd) {
   int ret;
   unsigned int rnd_up_len = ALIGN(len, out->block_size);
 
-  ret = out->ops->write(out, data, len);
+  if (isFd) {
+    ret = out->ops->write_fd(out, data.fd, len);
+  } else {
+    ret = out->ops->write(out, data.data_ptr, len);
+  }
   if (ret < 0) {
     return ret;
   }
@@ -459,27 +592,18 @@
   return ret;
 }
 
+static int write_normal_data_chunk(struct output_file* out, unsigned int len, void* data_ptr) {
+  handle_data data = {data_ptr};
+  return write_normal_data_chunk_variant(out, len, data, false /* isFd */);
+}
+
+static int write_normal_fd_chunk(struct output_file* out, unsigned int len, int fd) {
+  handle_data data = {.fd = fd};
+  return write_normal_data_chunk_variant(out, len, data, true /* isFd */);
+}
+
 static int write_normal_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
-  int ret;
-  unsigned int i;
-  unsigned int write_len;
-
-  /* Initialize fill_buf with the fill_val */
-  for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
-    out->fill_buf[i] = fill_val;
-  }
-
-  while (len) {
-    write_len = min(len, out->block_size);
-    ret = out->ops->write(out, out->fill_buf, write_len);
-    if (ret < 0) {
-      return ret;
-    }
-
-    len -= write_len;
-  }
-
-  return 0;
+  return out->ops->write_fill(out, fill_val, len);
 }
 
 static int write_normal_skip_chunk(struct output_file* out, int64_t len) {
@@ -492,6 +616,7 @@
 
 static struct sparse_file_ops normal_file_ops = {
     .write_data_chunk = write_normal_data_chunk,
+    .write_fd_chunk = write_normal_fd_chunk,
     .write_fill_chunk = write_normal_fill_chunk,
     .write_skip_chunk = write_normal_skip_chunk,
     .write_end_chunk = write_normal_end_chunk,
@@ -568,7 +693,7 @@
       reinterpret_cast<struct output_file_gz*>(calloc(1, sizeof(struct output_file_gz)));
   if (!outgz) {
     error_errno("malloc struct outgz");
-    return NULL;
+    return nullptr;
   }
 
   outgz->out.ops = &gz_file_ops;
@@ -581,7 +706,7 @@
       reinterpret_cast<struct output_file_normal*>(calloc(1, sizeof(struct output_file_normal)));
   if (!outn) {
     error_errno("malloc struct outn");
-    return NULL;
+    return nullptr;
   }
 
   outn->out.ops = &file_ops;
@@ -589,27 +714,38 @@
   return &outn->out;
 }
 
-struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
-                                              unsigned int block_size, int64_t len, int gz __unused,
-                                              int sparse, int chunks, int crc) {
+struct output_file* output_file_open_callback(int (*data_write)(void*, const void*, size_t),
+                                              int (*fd_write)(void*, int, size_t),
+                                              int (*fill_write)(void*, uint32_t, size_t),
+                                              int (*skip_write)(void*, off64_t), void* priv,
+                                              unsigned int block_size, int64_t len, int sparse,
+                                              int chunks, int crc) {
   int ret;
   struct output_file_callback* outc;
 
+  if (!data_write || (crc && (fd_write || fill_write))) {
+    errno = EINVAL;
+    return nullptr;
+  }
+
   outc =
       reinterpret_cast<struct output_file_callback*>(calloc(1, sizeof(struct output_file_callback)));
   if (!outc) {
     error_errno("malloc struct outc");
-    return NULL;
+    return nullptr;
   }
 
   outc->out.ops = &callback_file_ops;
   outc->priv = priv;
-  outc->write = write;
+  outc->data_write = data_write;
+  outc->fd_write = fd_write;
+  outc->fill_write = fill_write;
+  outc->skip_write = skip_write;
 
   ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
   if (ret < 0) {
     free(outc);
-    return NULL;
+    return nullptr;
   }
 
   return &outc->out;
@@ -626,7 +762,7 @@
     out = output_file_new_normal();
   }
   if (!out) {
-    return NULL;
+    return nullptr;
   }
 
   out->ops->open(out, fd);
@@ -634,7 +770,7 @@
   ret = output_file_init(out, block_size, len, sparse, chunks, crc);
   if (ret < 0) {
     free(out);
-    return NULL;
+    return nullptr;
   }
 
   return out;
@@ -651,52 +787,8 @@
 }
 
 int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) {
-  int ret;
-  int64_t aligned_offset;
-  int aligned_diff;
-  uint64_t buffer_size;
-  char* ptr;
-
-  aligned_offset = offset & ~(4096 - 1);
-  aligned_diff = offset - aligned_offset;
-  buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
-
-#ifndef _WIN32
-  if (buffer_size > SIZE_MAX) return -E2BIG;
-  char* data =
-      reinterpret_cast<char*>(mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset));
-  if (data == MAP_FAILED) {
-    return -errno;
-  }
-  ptr = data + aligned_diff;
-#else
-  off64_t pos;
-  char* data = reinterpret_cast<char*>(malloc(len));
-  if (!data) {
-    return -errno;
-  }
-  pos = lseek64(fd, offset, SEEK_SET);
-  if (pos < 0) {
-    free(data);
-    return -errno;
-  }
-  ret = read_all(fd, data, len);
-  if (ret < 0) {
-    free(data);
-    return ret;
-  }
-  ptr = data;
-#endif
-
-  ret = out->sparse_ops->write_data_chunk(out, len, ptr);
-
-#ifndef _WIN32
-  munmap(data, buffer_size);
-#else
-  free(data);
-#endif
-
-  return ret;
+  lseek64(fd, offset, SEEK_SET);
+  return out->sparse_ops->write_fd_chunk(out, len, fd);
 }
 
 /* Write a contiguous region of data blocks from a file */
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 278430b..114582e 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -22,14 +22,18 @@
 #endif
 
 #include <sparse/sparse.h>
+#include <sys/types.h>
 
 struct output_file;
 
 struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,
                                         int sparse, int chunks, int crc);
-struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
-                                              unsigned int block_size, int64_t len, int gz,
-                                              int sparse, int chunks, int crc);
+struct output_file* output_file_open_callback(int (*data_write)(void*, const void*, size_t),
+                                              int (*fd_write)(void*, int, size_t),
+                                              int (*fill_write)(void*, uint32_t, size_t),
+                                              int (*skip_write)(void*, off64_t), void* priv,
+                                              unsigned int block_size, int64_t len, int sparse,
+                                              int chunks, int crc);
 int write_data_chunk(struct output_file* out, unsigned int len, void* data);
 int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val);
 int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset);
diff --git a/libsparse/simg2simg.cpp b/libsparse/simg2simg.cpp
index 7e65701..a2c296e 100644
--- a/libsparse/simg2simg.cpp
+++ b/libsparse/simg2simg.cpp
@@ -66,7 +66,7 @@
     exit(-1);
   }
 
-  files = sparse_file_resparse(s, max_size, NULL, 0);
+  files = sparse_file_resparse(s, max_size, nullptr, 0);
   if (files < 0) {
     fprintf(stderr, "Failed to resparse\n");
     exit(-1);
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
index 6ff97b6..f5ca907 100644
--- a/libsparse/sparse.cpp
+++ b/libsparse/sparse.cpp
@@ -30,13 +30,13 @@
 struct sparse_file* sparse_file_new(unsigned int block_size, int64_t len) {
   struct sparse_file* s = reinterpret_cast<sparse_file*>(calloc(sizeof(struct sparse_file), 1));
   if (!s) {
-    return NULL;
+    return nullptr;
   }
 
   s->backed_block_list = backed_block_list_new(block_size);
   if (!s->backed_block_list) {
     free(s);
-    return NULL;
+    return nullptr;
   }
 
   s->block_size = block_size;
@@ -160,7 +160,30 @@
   struct output_file* out;
 
   chunks = sparse_count_chunks(s);
-  out = output_file_open_callback(write, priv, s->block_size, s->len, false, sparse, chunks, crc);
+  out = output_file_open_callback(write, nullptr, nullptr, nullptr, priv, s->block_size, s->len,
+                                  sparse, chunks, crc);
+
+  if (!out) return -ENOMEM;
+
+  ret = write_all_blocks(s, out);
+
+  output_file_close(out);
+
+  return ret;
+}
+
+int sparse_file_callback_typed(struct sparse_file* s, bool sparse,
+                               int (*data_write)(void* priv, const void* data, size_t len),
+                               int (*fd_write)(void* priv, int fd, size_t len),
+                               int (*fill_write)(void* priv, uint32_t fill_val, size_t len),
+                               int (*skip_write)(void* priv, int64_t len), void* priv) {
+  int ret;
+  int chunks;
+  struct output_file* out;
+
+  chunks = sparse_count_chunks(s);
+  out = output_file_open_callback(data_write, fd_write, fill_write, skip_write, priv, s->block_size,
+                                  s->len, sparse, chunks, false);
 
   if (!out) return -ENOMEM;
 
@@ -198,8 +221,8 @@
   chk.write = write;
   chk.block = chk.nr_blocks = 0;
   chunks = sparse_count_chunks(s);
-  out = output_file_open_callback(foreach_chunk_write, &chk, s->block_size, s->len, false, sparse,
-                                  chunks, crc);
+  out = output_file_open_callback(foreach_chunk_write, nullptr, nullptr, nullptr, &chk,
+                                  s->block_size, s->len, sparse, chunks, crc);
 
   if (!out) return -ENOMEM;
 
@@ -227,8 +250,8 @@
   int64_t count = 0;
   struct output_file* out;
 
-  out = output_file_open_callback(out_counter_write, &count, s->block_size, s->len, false, sparse,
-                                  chunks, crc);
+  out = output_file_open_callback(out_counter_write, nullptr, nullptr, nullptr, &count,
+                                  s->block_size, s->len, sparse, chunks, crc);
   if (!out) {
     return -1;
   }
@@ -252,7 +275,7 @@
                                                   unsigned int len) {
   int64_t count = 0;
   struct output_file* out_counter;
-  struct backed_block* last_bb = NULL;
+  struct backed_block* last_bb = nullptr;
   struct backed_block* bb;
   struct backed_block* start;
   unsigned int last_block = 0;
@@ -267,10 +290,10 @@
   len -= overhead;
 
   start = backed_block_iter_new(from->backed_block_list);
-  out_counter = output_file_open_callback(out_counter_write, &count, to->block_size, to->len, false,
-                                          true, 0, false);
+  out_counter = output_file_open_callback(out_counter_write, nullptr, nullptr, nullptr, &count,
+                                          to->block_size, to->len, true, 0, false);
   if (!out_counter) {
-    return NULL;
+    return nullptr;
   }
 
   for (bb = start; bb; bb = backed_block_iter_next(bb)) {
@@ -281,7 +304,7 @@
     /* will call out_counter_write to update count */
     ret = sparse_file_write_block(out_counter, bb);
     if (ret) {
-      bb = NULL;
+      bb = nullptr;
       goto out;
     }
     if (file_len + count > len) {
@@ -330,13 +353,13 @@
     if (c < out_s_count) {
       out_s[c] = s;
     } else {
-      backed_block_list_move(s->backed_block_list, tmp->backed_block_list, NULL, NULL);
+      backed_block_list_move(s->backed_block_list, tmp->backed_block_list, nullptr, nullptr);
       sparse_file_destroy(s);
     }
     c++;
   } while (bb);
 
-  backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list, NULL, NULL);
+  backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list, nullptr, nullptr);
 
   sparse_file_destroy(tmp);
 
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index 56e2c9a..c4c1823 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -276,7 +276,7 @@
     return ret;
   }
 
-  if (crc32 != NULL && file_crc32 != *crc32) {
+  if (crc32 != nullptr && file_crc32 != *crc32) {
     return -EINVAL;
   }
 
@@ -339,7 +339,7 @@
   sparse_header_t sparse_header;
   chunk_header_t chunk_header;
   uint32_t crc32 = 0;
-  uint32_t* crc_ptr = 0;
+  uint32_t* crc_ptr = nullptr;
   unsigned int cur_block = 0;
 
   if (!copybuf) {
@@ -489,39 +489,39 @@
   ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
   if (ret < 0) {
     verbose_error(verbose, ret, "header");
-    return NULL;
+    return nullptr;
   }
 
   if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
     verbose_error(verbose, -EINVAL, "header magic");
-    return NULL;
+    return nullptr;
   }
 
   if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
     verbose_error(verbose, -EINVAL, "header major version");
-    return NULL;
+    return nullptr;
   }
 
   if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
-    return NULL;
+    return nullptr;
   }
 
   if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
-    return NULL;
+    return nullptr;
   }
 
   len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
   s = sparse_file_new(sparse_header.blk_sz, len);
   if (!s) {
-    verbose_error(verbose, -EINVAL, NULL);
-    return NULL;
+    verbose_error(verbose, -EINVAL, nullptr);
+    return nullptr;
   }
 
   ret = source->SetOffset(0);
   if (ret < 0) {
     verbose_error(verbose, ret, "seeking");
     sparse_file_destroy(s);
-    return NULL;
+    return nullptr;
   }
 
   s->verbose = verbose;
@@ -529,7 +529,7 @@
   ret = sparse_file_read_sparse(s, source, crc);
   if (ret < 0) {
     sparse_file_destroy(s);
-    return NULL;
+    return nullptr;
   }
 
   return s;
@@ -557,20 +557,20 @@
 
   len = lseek64(fd, 0, SEEK_END);
   if (len < 0) {
-    return NULL;
+    return nullptr;
   }
 
   lseek64(fd, 0, SEEK_SET);
 
   s = sparse_file_new(4096, len);
   if (!s) {
-    return NULL;
+    return nullptr;
   }
 
   ret = sparse_file_read_normal(s, fd);
   if (ret < 0) {
     sparse_file_destroy(s);
-    return NULL;
+    return nullptr;
   }
 
   return s;
diff --git a/libsync/include/ndk/sync.h b/libsync/include/ndk/sync.h
index 49f01e1..ba7d8c4 100644
--- a/libsync/include/ndk/sync.h
+++ b/libsync/include/ndk/sync.h
@@ -27,11 +27,14 @@
 #define ANDROID_SYNC_H
 
 #include <stdint.h>
+#include <sys/cdefs.h>
 
 #include <linux/sync_file.h>
 
 __BEGIN_DECLS
 
+#if __ANDROID_API__ >= 26
+
 /* Fences indicate the status of an asynchronous task. They are initially
  * in unsignaled state (0), and make a one-time transition to either signaled
  * (1) or error (< 0) state. A sync file is a collection of one or more fences;
@@ -88,6 +91,8 @@
 /** Free a struct sync_file_info structure */
 void sync_file_info_free(struct sync_file_info* info) __INTRODUCED_IN(26);
 
+#endif /* __ANDROID_API__ >= 26 */
+
 __END_DECLS
 
 #endif /* ANDROID_SYNC_H */
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index 87e2684..835e226 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -42,7 +42,7 @@
 
 FrameworkListener::FrameworkListener(int sock) :
                             SocketListener(sock, true) {
-    init(NULL, false);
+    init(nullptr, false);
 }
 
 void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) {
@@ -154,7 +154,7 @@
             if (!haveCmdNum) {
                 char *endptr;
                 int cmdNum = (int)strtol(tmp, &endptr, 0);
-                if (endptr == NULL || *endptr != '\0') {
+                if (endptr == nullptr || *endptr != '\0') {
                     cli->sendMsg(500, "Invalid sequence number", false);
                     goto out;
                 }
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 35a3063..24ea7aa 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -45,8 +45,8 @@
 NetlinkEvent::NetlinkEvent() {
     mAction = Action::kUnknown;
     memset(mParams, 0, sizeof(mParams));
-    mPath = NULL;
-    mSubsystem = NULL;
+    mPath = nullptr;
+    mSubsystem = nullptr;
 }
 
 NetlinkEvent::~NetlinkEvent() {
@@ -89,7 +89,7 @@
         NL_EVENT_RTM_NAME(LOCAL_QLOG_NL_EVENT);
         NL_EVENT_RTM_NAME(LOCAL_NFLOG_PACKET);
         default:
-            return NULL;
+            return nullptr;
     }
 #undef NL_EVENT_RTM_NAME
 }
@@ -158,7 +158,7 @@
  */
 bool NetlinkEvent::parseIfAddrMessage(const struct nlmsghdr *nh) {
     struct ifaddrmsg *ifaddr = (struct ifaddrmsg *) NLMSG_DATA(nh);
-    struct ifa_cacheinfo *cacheinfo = NULL;
+    struct ifa_cacheinfo *cacheinfo = nullptr;
     char addrstr[INET6_ADDRSTRLEN] = "";
     char ifname[IFNAMSIZ] = "";
 
@@ -239,12 +239,13 @@
     asprintf(&mParams[1], "INTERFACE=%s", ifname);
     asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags);
     asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope);
+    asprintf(&mParams[4], "IFINDEX=%u", ifaddr->ifa_index);
 
     if (cacheinfo) {
-        asprintf(&mParams[4], "PREFERRED=%u", cacheinfo->ifa_prefered);
-        asprintf(&mParams[5], "VALID=%u", cacheinfo->ifa_valid);
-        asprintf(&mParams[6], "CSTAMP=%u", cacheinfo->cstamp);
-        asprintf(&mParams[7], "TSTAMP=%u", cacheinfo->tstamp);
+        asprintf(&mParams[5], "PREFERRED=%u", cacheinfo->ifa_prefered);
+        asprintf(&mParams[6], "VALID=%u", cacheinfo->ifa_valid);
+        asprintf(&mParams[7], "CSTAMP=%u", cacheinfo->cstamp);
+        asprintf(&mParams[8], "TSTAMP=%u", cacheinfo->tstamp);
     }
 
     return true;
@@ -285,7 +286,7 @@
 bool NetlinkEvent::parseNfPacketMessage(struct nlmsghdr *nh) {
     int uid = -1;
     int len = 0;
-    char* raw = NULL;
+    char* raw = nullptr;
 
     struct nlattr* uid_attr = findNlAttr(nh, sizeof(struct genlmsghdr), NFULA_UID);
     if (uid_attr) {
@@ -583,7 +584,7 @@
         (prefixlen == 0 || !memcmp(str, prefix, prefixlen))) {
         return str + prefixlen;
     } else {
-        return NULL;
+        return nullptr;
     }
 }
 
@@ -624,16 +625,16 @@
             first = 0;
         } else {
             const char* a;
-            if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) {
+            if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != nullptr) {
                 if (!strcmp(a, "add"))
                     mAction = Action::kAdd;
                 else if (!strcmp(a, "remove"))
                     mAction = Action::kRemove;
                 else if (!strcmp(a, "change"))
                     mAction = Action::kChange;
-            } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != NULL) {
+            } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != nullptr) {
                 mSeq = atoi(a);
-            } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != NULL) {
+            } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != nullptr) {
                 mSubsystem = strdup(a);
             } else if (param_idx < NL_PARAMS_MAX) {
                 mParams[param_idx++] = strdup(s);
@@ -655,14 +656,14 @@
 
 const char *NetlinkEvent::findParam(const char *paramName) {
     size_t len = strlen(paramName);
-    for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != NULL; ++i) {
+    for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != nullptr; ++i) {
         const char *ptr = mParams[i] + len;
         if (!strncmp(mParams[i], paramName, len) && *ptr == '=')
             return ++ptr;
     }
 
     SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
-    return NULL;
+    return nullptr;
 }
 
 nlattr* NetlinkEvent::findNlAttr(const nlmsghdr* nh, size_t hdrlen, uint16_t attr) {
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
index 971f908..0625db7 100644
--- a/libsysutils/src/SocketClient.cpp
+++ b/libsysutils/src/SocketClient.cpp
@@ -42,8 +42,8 @@
     mSocket = socket;
     mSocketOwned = owned;
     mUseCmdNum = useCmdNum;
-    pthread_mutex_init(&mWriteMutex, NULL);
-    pthread_mutex_init(&mRefCountMutex, NULL);
+    pthread_mutex_init(&mWriteMutex, nullptr);
+    pthread_mutex_init(&mRefCountMutex, nullptr);
     mPid = -1;
     mUid = -1;
     mGid = -1;
@@ -135,9 +135,9 @@
     const char *end = arg + len;
     char *oldresult;
 
-    if(result == NULL) {
+    if(result == nullptr) {
         SLOGW("malloc error (%s)", strerror(errno));
-        return NULL;
+        return nullptr;
     }
 
     *(current++) = '"';
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index 3f8f3db..0c8a688 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -39,7 +39,7 @@
 }
 
 SocketListener::SocketListener(int socketFd, bool listen) {
-    init(NULL, socketFd, listen, false);
+    init(nullptr, socketFd, listen, false);
 }
 
 SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) {
@@ -51,7 +51,7 @@
     mSocketName = socketName;
     mSock = socketFd;
     mUseCmdNum = useCmdNum;
-    pthread_mutex_init(&mClientsLock, NULL);
+    pthread_mutex_init(&mClientsLock, nullptr);
     mClients = new SocketClientCollection();
 }
 
@@ -102,7 +102,7 @@
         return -1;
     }
 
-    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
+    if (pthread_create(&mThread, nullptr, SocketListener::threadStart, this)) {
         SLOGE("pthread_create (%s)", strerror(errno));
         return -1;
     }
@@ -147,8 +147,8 @@
     SocketListener *me = reinterpret_cast<SocketListener *>(obj);
 
     me->runListener();
-    pthread_exit(NULL);
-    return NULL;
+    pthread_exit(nullptr);
+    return nullptr;
 }
 
 void SocketListener::runListener() {
@@ -183,7 +183,7 @@
         }
         pthread_mutex_unlock(&mClientsLock);
         SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);
-        if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
+        if ((rc = select(max + 1, &read_fds, nullptr, nullptr, nullptr)) < 0) {
             if (errno == EINTR)
                 continue;
             SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
index 635cefd..388ab0a 100644
--- a/libunwindstack/DwarfDebugFrame.h
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -26,9 +26,9 @@
 namespace unwindstack {
 
 template <typename AddressType>
-class DwarfDebugFrame : public DwarfSectionImpl<AddressType> {
+class DwarfDebugFrame : public DwarfSectionImplNoHdr<AddressType> {
  public:
-  DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {
+  DwarfDebugFrame(Memory* memory) : DwarfSectionImplNoHdr<AddressType>(memory) {
     this->cie32_value_ = static_cast<uint32_t>(-1);
     this->cie64_value_ = static_cast<uint64_t>(-1);
   }
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
index 7a41e45..df441fb 100644
--- a/libunwindstack/DwarfEhFrame.h
+++ b/libunwindstack/DwarfEhFrame.h
@@ -25,9 +25,9 @@
 namespace unwindstack {
 
 template <typename AddressType>
-class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
+class DwarfEhFrame : public DwarfSectionImplNoHdr<AddressType> {
  public:
-  DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  DwarfEhFrame(Memory* memory) : DwarfSectionImplNoHdr<AddressType>(memory) {}
   virtual ~DwarfEhFrame() = default;
 
   uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index fd6a457..668527a 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -39,6 +39,7 @@
   memory_.clear_text_offset();
   memory_.set_data_offset(offset);
   memory_.set_cur_offset(offset);
+  pc_offset_ = offset;
 
   // Read the first four bytes all at once.
   uint8_t data[4];
@@ -88,12 +89,22 @@
 }
 
 template <typename AddressType>
-const DwarfFde* DwarfEhFrameWithHdr<AddressType>::GetFdeFromIndex(size_t index) {
-  const FdeInfo* info = GetFdeInfoFromIndex(index);
-  if (info == nullptr) {
+const DwarfFde* DwarfEhFrameWithHdr<AddressType>::GetFdeFromPc(uint64_t pc) {
+  uint64_t fde_offset;
+  if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
     return nullptr;
   }
-  return this->GetFdeFromOffset(info->offset);
+  const DwarfFde* fde = this->GetFdeFromOffset(fde_offset);
+  if (fde == nullptr) {
+    return nullptr;
+  }
+
+  // Guaranteed pc >= pc_start, need to check pc in the fde range.
+  if (pc < fde->pc_end) {
+    return fde;
+  }
+  last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
+  return nullptr;
 }
 
 template <typename AddressType>
@@ -241,6 +252,21 @@
   }
 }
 
+template <typename AddressType>
+void DwarfEhFrameWithHdr<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
+  for (size_t i = 0; i < fde_count_; i++) {
+    const FdeInfo* info = GetFdeInfoFromIndex(i);
+    if (info == nullptr) {
+      break;
+    }
+    const DwarfFde* fde = this->GetFdeFromOffset(info->offset);
+    if (fde == nullptr) {
+      break;
+    }
+    fdes->push_back(fde);
+  }
+}
+
 // Explicitly instantiate DwarfEhFrameWithHdr
 template class DwarfEhFrameWithHdr<uint32_t>;
 template class DwarfEhFrameWithHdr<uint64_t>;
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
index d16dd10..e3e9ca8 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.h
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -21,7 +21,7 @@
 
 #include <unordered_map>
 
-#include "DwarfEhFrame.h"
+#include <unwindstack/DwarfSection.h>
 
 namespace unwindstack {
 
@@ -29,12 +29,12 @@
 class Memory;
 
 template <typename AddressType>
-class DwarfEhFrameWithHdr : public DwarfEhFrame<AddressType> {
+class DwarfEhFrameWithHdr : public DwarfSectionImpl<AddressType> {
  public:
   // Add these so that the protected members of DwarfSectionImpl
   // can be accessed without needing a this->.
   using DwarfSectionImpl<AddressType>::memory_;
-  using DwarfSectionImpl<AddressType>::fde_count_;
+  using DwarfSectionImpl<AddressType>::pc_offset_;
   using DwarfSectionImpl<AddressType>::entries_offset_;
   using DwarfSectionImpl<AddressType>::entries_end_;
   using DwarfSectionImpl<AddressType>::last_error_;
@@ -45,14 +45,27 @@
     uint64_t offset;
   };
 
-  DwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrame<AddressType>(memory) {}
+  DwarfEhFrameWithHdr(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
   virtual ~DwarfEhFrameWithHdr() = default;
 
+  uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
+    return this->memory_.cur_offset() - pointer - 4;
+  }
+
+  uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
+    return this->memory_.cur_offset() - pointer - 8;
+  }
+
+  uint64_t AdjustPcFromFde(uint64_t pc) override {
+    // The eh_frame uses relative pcs.
+    return pc + this->memory_.cur_offset() - 4;
+  }
+
   bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
 
-  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+  const DwarfFde* GetFdeFromPc(uint64_t pc) override;
 
-  const DwarfFde* GetFdeFromIndex(size_t index) override;
+  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset);
 
   const FdeInfo* GetFdeInfoFromIndex(size_t index);
 
@@ -60,6 +73,8 @@
 
   bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries);
 
+  void GetFdes(std::vector<const DwarfFde*>* fdes) override;
+
  protected:
   uint8_t version_;
   uint8_t ptr_encoding_;
@@ -71,6 +86,7 @@
   uint64_t entries_data_offset_;
   uint64_t cur_entries_offset_ = 0;
 
+  uint64_t fde_count_;
   std::unordered_map<uint64_t, FdeInfo> fde_info_;
 };
 
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index eb83949..6061f61 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -36,24 +36,6 @@
 
 DwarfSection::DwarfSection(Memory* memory) : memory_(memory) {}
 
-const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
-  uint64_t fde_offset;
-  if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
-    return nullptr;
-  }
-  const DwarfFde* fde = GetFdeFromOffset(fde_offset);
-  if (fde == nullptr) {
-    return nullptr;
-  }
-
-  // Guaranteed pc >= pc_start, need to check pc in the fde range.
-  if (pc < fde->pc_end) {
-    return fde;
-  }
-  last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
-  return nullptr;
-}
-
 bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
   // Lookup the pc in the cache.
   auto it = loc_regs_.upper_bound(pc);
@@ -81,6 +63,314 @@
 }
 
 template <typename AddressType>
+const DwarfCie* DwarfSectionImpl<AddressType>::GetCieFromOffset(uint64_t offset) {
+  auto cie_entry = cie_entries_.find(offset);
+  if (cie_entry != cie_entries_.end()) {
+    return &cie_entry->second;
+  }
+  DwarfCie* cie = &cie_entries_[offset];
+  memory_.set_cur_offset(offset);
+  if (!FillInCieHeader(cie) || !FillInCie(cie)) {
+    // Erase the cached entry.
+    cie_entries_.erase(offset);
+    return nullptr;
+  }
+  return cie;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInCieHeader(DwarfCie* cie) {
+  cie->lsda_encoding = DW_EH_PE_omit;
+  uint32_t length32;
+  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+  if (length32 == static_cast<uint32_t>(-1)) {
+    // 64 bit Cie
+    uint64_t length64;
+    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+
+    cie->cfa_instructions_end = memory_.cur_offset() + length64;
+    cie->fde_address_encoding = DW_EH_PE_sdata8;
+
+    uint64_t cie_id;
+    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    if (cie_id != cie64_value_) {
+      // This is not a Cie, something has gone horribly wrong.
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+  } else {
+    // 32 bit Cie
+    cie->cfa_instructions_end = memory_.cur_offset() + length32;
+    cie->fde_address_encoding = DW_EH_PE_sdata4;
+
+    uint32_t cie_id;
+    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    if (cie_id != cie32_value_) {
+      // This is not a Cie, something has gone horribly wrong.
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+  }
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
+  if (!memory_.ReadBytes(&cie->version, sizeof(cie->version))) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
+    // Unrecognized version.
+    last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
+    return false;
+  }
+
+  // Read the augmentation string.
+  char aug_value;
+  do {
+    if (!memory_.ReadBytes(&aug_value, 1)) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    cie->augmentation_string.push_back(aug_value);
+  } while (aug_value != '\0');
+
+  if (cie->version == 4) {
+    // Skip the Address Size field since we only use it for validation.
+    memory_.set_cur_offset(memory_.cur_offset() + 1);
+
+    // Segment Size
+    if (!memory_.ReadBytes(&cie->segment_size, 1)) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+  }
+
+  // Code Alignment Factor
+  if (!memory_.ReadULEB128(&cie->code_alignment_factor)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  // Data Alignment Factor
+  if (!memory_.ReadSLEB128(&cie->data_alignment_factor)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  if (cie->version == 1) {
+    // Return Address is a single byte.
+    uint8_t return_address_register;
+    if (!memory_.ReadBytes(&return_address_register, 1)) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    cie->return_address_register = return_address_register;
+  } else if (!memory_.ReadULEB128(&cie->return_address_register)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  if (cie->augmentation_string[0] != 'z') {
+    cie->cfa_instructions_offset = memory_.cur_offset();
+    return true;
+  }
+
+  uint64_t aug_length;
+  if (!memory_.ReadULEB128(&aug_length)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+  cie->cfa_instructions_offset = memory_.cur_offset() + aug_length;
+
+  for (size_t i = 1; i < cie->augmentation_string.size(); i++) {
+    switch (cie->augmentation_string[i]) {
+      case 'L':
+        if (!memory_.ReadBytes(&cie->lsda_encoding, 1)) {
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
+          return false;
+        }
+        break;
+      case 'P': {
+        uint8_t encoding;
+        if (!memory_.ReadBytes(&encoding, 1)) {
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
+          return false;
+        }
+        memory_.set_pc_offset(pc_offset_);
+        if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
+          return false;
+        }
+      } break;
+      case 'R':
+        if (!memory_.ReadBytes(&cie->fde_address_encoding, 1)) {
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
+          return false;
+        }
+        break;
+    }
+  }
+  return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromOffset(uint64_t offset) {
+  auto fde_entry = fde_entries_.find(offset);
+  if (fde_entry != fde_entries_.end()) {
+    return &fde_entry->second;
+  }
+  DwarfFde* fde = &fde_entries_[offset];
+  memory_.set_cur_offset(offset);
+  if (!FillInFdeHeader(fde) || !FillInFde(fde)) {
+    fde_entries_.erase(offset);
+    return nullptr;
+  }
+  return fde;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInFdeHeader(DwarfFde* fde) {
+  uint32_t length32;
+  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  if (length32 == static_cast<uint32_t>(-1)) {
+    // 64 bit Fde.
+    uint64_t length64;
+    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    fde->cfa_instructions_end = memory_.cur_offset() + length64;
+
+    uint64_t value64;
+    if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    if (value64 == cie64_value_) {
+      // This is a Cie, this means something has gone wrong.
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+
+    // Get the Cie pointer, which is necessary to properly read the rest of
+    // of the Fde information.
+    fde->cie_offset = GetCieOffsetFromFde64(value64);
+  } else {
+    // 32 bit Fde.
+    fde->cfa_instructions_end = memory_.cur_offset() + length32;
+
+    uint32_t value32;
+    if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    if (value32 == cie32_value_) {
+      // This is a Cie, this means something has gone wrong.
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+
+    // Get the Cie pointer, which is necessary to properly read the rest of
+    // of the Fde information.
+    fde->cie_offset = GetCieOffsetFromFde32(value32);
+  }
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInFde(DwarfFde* fde) {
+  uint64_t cur_offset = memory_.cur_offset();
+
+  const DwarfCie* cie = GetCieFromOffset(fde->cie_offset);
+  if (cie == nullptr) {
+    return false;
+  }
+  fde->cie = cie;
+
+  if (cie->segment_size != 0) {
+    // Skip over the segment selector for now.
+    cur_offset += cie->segment_size;
+  }
+  memory_.set_cur_offset(cur_offset);
+
+  // The load bias only applies to the start.
+  memory_.set_pc_offset(load_bias_);
+  bool valid = memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_start);
+  fde->pc_start = AdjustPcFromFde(fde->pc_start);
+
+  memory_.set_pc_offset(0);
+  if (!valid || !memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_end)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+  fde->pc_end += fde->pc_start;
+
+  if (cie->augmentation_string.size() > 0 && cie->augmentation_string[0] == 'z') {
+    // Augmentation Size
+    uint64_t aug_length;
+    if (!memory_.ReadULEB128(&aug_length)) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    uint64_t cur_offset = memory_.cur_offset();
+
+    memory_.set_pc_offset(pc_offset_);
+    if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+
+    // Set our position to after all of the augmentation data.
+    memory_.set_cur_offset(cur_offset + aug_length);
+  }
+  fde->cfa_instructions_offset = memory_.cur_offset();
+
+  return true;
+}
+
+template <typename AddressType>
 bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, Memory* regular_memory,
                                                    AddressType* value,
                                                    RegsInfo<AddressType>* regs_info,
@@ -260,307 +550,6 @@
 }
 
 template <typename AddressType>
-const DwarfCie* DwarfSectionImpl<AddressType>::GetCie(uint64_t offset) {
-  auto cie_entry = cie_entries_.find(offset);
-  if (cie_entry != cie_entries_.end()) {
-    return &cie_entry->second;
-  }
-  DwarfCie* cie = &cie_entries_[offset];
-  memory_.set_cur_offset(offset);
-  if (!FillInCie(cie)) {
-    // Erase the cached entry.
-    cie_entries_.erase(offset);
-    return nullptr;
-  }
-  return cie;
-}
-
-template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
-  uint32_t length32;
-  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  // Set the default for the lsda encoding.
-  cie->lsda_encoding = DW_EH_PE_omit;
-
-  if (length32 == static_cast<uint32_t>(-1)) {
-    // 64 bit Cie
-    uint64_t length64;
-    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-
-    cie->cfa_instructions_end = memory_.cur_offset() + length64;
-    cie->fde_address_encoding = DW_EH_PE_sdata8;
-
-    uint64_t cie_id;
-    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    if (cie_id != cie64_value_) {
-      // This is not a Cie, something has gone horribly wrong.
-      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
-    }
-  } else {
-    // 32 bit Cie
-    cie->cfa_instructions_end = memory_.cur_offset() + length32;
-    cie->fde_address_encoding = DW_EH_PE_sdata4;
-
-    uint32_t cie_id;
-    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    if (cie_id != cie32_value_) {
-      // This is not a Cie, something has gone horribly wrong.
-      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
-    }
-  }
-
-  if (!memory_.ReadBytes(&cie->version, sizeof(cie->version))) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-
-  if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
-    // Unrecognized version.
-    last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
-    return false;
-  }
-
-  // Read the augmentation string.
-  char aug_value;
-  do {
-    if (!memory_.ReadBytes(&aug_value, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    cie->augmentation_string.push_back(aug_value);
-  } while (aug_value != '\0');
-
-  if (cie->version == 4) {
-    // Skip the Address Size field since we only use it for validation.
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
-
-    // Segment Size
-    if (!memory_.ReadBytes(&cie->segment_size, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-  }
-
-  // Code Alignment Factor
-  if (!memory_.ReadULEB128(&cie->code_alignment_factor)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-
-  // Data Alignment Factor
-  if (!memory_.ReadSLEB128(&cie->data_alignment_factor)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-
-  if (cie->version == 1) {
-    // Return Address is a single byte.
-    uint8_t return_address_register;
-    if (!memory_.ReadBytes(&return_address_register, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    cie->return_address_register = return_address_register;
-  } else if (!memory_.ReadULEB128(&cie->return_address_register)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-
-  if (cie->augmentation_string[0] != 'z') {
-    cie->cfa_instructions_offset = memory_.cur_offset();
-    return true;
-  }
-
-  uint64_t aug_length;
-  if (!memory_.ReadULEB128(&aug_length)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  cie->cfa_instructions_offset = memory_.cur_offset() + aug_length;
-
-  for (size_t i = 1; i < cie->augmentation_string.size(); i++) {
-    switch (cie->augmentation_string[i]) {
-      case 'L':
-        if (!memory_.ReadBytes(&cie->lsda_encoding, 1)) {
-          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-          last_error_.address = memory_.cur_offset();
-          return false;
-        }
-        break;
-      case 'P': {
-        uint8_t encoding;
-        if (!memory_.ReadBytes(&encoding, 1)) {
-          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-          last_error_.address = memory_.cur_offset();
-          return false;
-        }
-        memory_.set_pc_offset(pc_offset_);
-        if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
-          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-          last_error_.address = memory_.cur_offset();
-          return false;
-        }
-      } break;
-      case 'R':
-        if (!memory_.ReadBytes(&cie->fde_address_encoding, 1)) {
-          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-          last_error_.address = memory_.cur_offset();
-          return false;
-        }
-        break;
-    }
-  }
-  return true;
-}
-
-template <typename AddressType>
-const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromOffset(uint64_t offset) {
-  auto fde_entry = fde_entries_.find(offset);
-  if (fde_entry != fde_entries_.end()) {
-    return &fde_entry->second;
-  }
-  DwarfFde* fde = &fde_entries_[offset];
-  memory_.set_cur_offset(offset);
-  if (!FillInFde(fde)) {
-    fde_entries_.erase(offset);
-    return nullptr;
-  }
-  return fde;
-}
-
-template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::FillInFde(DwarfFde* fde) {
-  uint32_t length32;
-  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-
-  if (length32 == static_cast<uint32_t>(-1)) {
-    // 64 bit Fde.
-    uint64_t length64;
-    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    fde->cfa_instructions_end = memory_.cur_offset() + length64;
-
-    uint64_t value64;
-    if (!memory_.ReadBytes(&value64, sizeof(value64))) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    if (value64 == cie64_value_) {
-      // This is a Cie, this means something has gone wrong.
-      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
-    }
-
-    // Get the Cie pointer, which is necessary to properly read the rest of
-    // of the Fde information.
-    fde->cie_offset = GetCieOffsetFromFde64(value64);
-  } else {
-    // 32 bit Fde.
-    fde->cfa_instructions_end = memory_.cur_offset() + length32;
-
-    uint32_t value32;
-    if (!memory_.ReadBytes(&value32, sizeof(value32))) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    if (value32 == cie32_value_) {
-      // This is a Cie, this means something has gone wrong.
-      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
-    }
-
-    // Get the Cie pointer, which is necessary to properly read the rest of
-    // of the Fde information.
-    fde->cie_offset = GetCieOffsetFromFde32(value32);
-  }
-  uint64_t cur_offset = memory_.cur_offset();
-
-  const DwarfCie* cie = GetCie(fde->cie_offset);
-  if (cie == nullptr) {
-    return false;
-  }
-  fde->cie = cie;
-
-  if (cie->segment_size != 0) {
-    // Skip over the segment selector for now.
-    cur_offset += cie->segment_size;
-  }
-  memory_.set_cur_offset(cur_offset);
-
-  // The load bias only applies to the start.
-  memory_.set_pc_offset(load_bias_);
-  bool valid = memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_start);
-  fde->pc_start = AdjustPcFromFde(fde->pc_start);
-
-  memory_.set_pc_offset(0);
-  if (!valid || !memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_end)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  fde->pc_end += fde->pc_start;
-
-  if (cie->augmentation_string.size() > 0 && cie->augmentation_string[0] == 'z') {
-    // Augmentation Size
-    uint64_t aug_length;
-    if (!memory_.ReadULEB128(&aug_length)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    uint64_t cur_offset = memory_.cur_offset();
-
-    memory_.set_pc_offset(pc_offset_);
-    if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-
-    // Set our position to after all of the augmentation data.
-    memory_.set_cur_offset(cur_offset + aug_length);
-  }
-  fde->cfa_instructions_offset = memory_.cur_offset();
-
-  return true;
-}
-
-template <typename AddressType>
 bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
                                                        dwarf_loc_regs_t* loc_regs) {
   DwarfCfa<AddressType> cfa(&memory_, fde);
@@ -601,9 +590,10 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
+bool DwarfSectionImplNoHdr<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
   load_bias_ = load_bias;
   entries_offset_ = offset;
+  next_entries_offset_ = offset;
   entries_end_ = offset + size;
 
   memory_.clear_func_offset();
@@ -612,298 +602,213 @@
   memory_.set_data_offset(offset);
   pc_offset_ = offset;
 
-  return CreateSortedFdeList();
-}
-
-template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) {
-  uint8_t version;
-  if (!memory_.ReadBytes(&version, 1)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  // Read the augmentation string.
-  std::vector<char> aug_string;
-  char aug_value;
-  bool get_encoding = false;
-  do {
-    if (!memory_.ReadBytes(&aug_value, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    if (aug_value == 'R') {
-      get_encoding = true;
-    }
-    aug_string.push_back(aug_value);
-  } while (aug_value != '\0');
-
-  if (version == 4) {
-    // Skip the Address Size field.
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
-
-    // Read the segment size.
-    if (!memory_.ReadBytes(segment_size, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-  } else {
-    *segment_size = 0;
-  }
-
-  if (aug_string[0] != 'z' || !get_encoding) {
-    // No encoding
-    return true;
-  }
-
-  // Skip code alignment factor
-  uint8_t value;
-  do {
-    if (!memory_.ReadBytes(&value, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-  } while (value & 0x80);
-
-  // Skip data alignment factor
-  do {
-    if (!memory_.ReadBytes(&value, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-  } while (value & 0x80);
-
-  if (version == 1) {
-    // Skip return address register.
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
-  } else {
-    // Skip return address register.
-    do {
-      if (!memory_.ReadBytes(&value, 1)) {
-        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-    } while (value & 0x80);
-  }
-
-  // Skip the augmentation length.
-  do {
-    if (!memory_.ReadBytes(&value, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-  } while (value & 0x80);
-
-  for (size_t i = 1; i < aug_string.size(); i++) {
-    if (aug_string[i] == 'R') {
-      if (!memory_.ReadBytes(encoding, 1)) {
-        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-      // Got the encoding, that's all we are looking for.
-      return true;
-    } else if (aug_string[i] == 'L') {
-      memory_.set_cur_offset(memory_.cur_offset() + 1);
-    } else if (aug_string[i] == 'P') {
-      uint8_t encoding;
-      if (!memory_.ReadBytes(&encoding, 1)) {
-        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-      uint64_t value;
-      memory_.set_pc_offset(pc_offset_);
-      if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
-        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-    }
-  }
-
-  // It should be impossible to get here.
-  abort();
-}
-
-template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::AddFdeInfo(uint64_t entry_offset, uint8_t segment_size,
-                                               uint8_t encoding) {
-  if (segment_size != 0) {
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
-  }
-
-  uint64_t start;
-  memory_.set_pc_offset(load_bias_);
-  bool valid = memory_.template ReadEncodedValue<AddressType>(encoding, &start);
-  start = AdjustPcFromFde(start);
-
-  uint64_t length;
-  memory_.set_pc_offset(0);
-  if (!valid || !memory_.template ReadEncodedValue<AddressType>(encoding, &length)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  if (length != 0) {
-    fdes_.emplace_back(entry_offset, start, length);
-  }
-
   return true;
 }
 
+// Create a cached version of the fde information such that it is a std::map
+// that is indexed by end pc and contains a pair that represents the start pc
+// followed by the fde object. The fde pointers are owned by fde_entries_
+// and not by the map object.
+// It is possible for an fde to be represented by multiple entries in
+// the map. This can happen if the the start pc and end pc overlap already
+// existing entries. For example, if there is already an entry of 0x400, 0x200,
+// and an fde has a start pc of 0x100 and end pc of 0x500, two new entries
+// will be added: 0x200, 0x100 and 0x500, 0x400.
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::CreateSortedFdeList() {
-  memory_.set_cur_offset(entries_offset_);
+void DwarfSectionImplNoHdr<AddressType>::InsertFde(const DwarfFde* fde) {
+  uint64_t start = fde->pc_start;
+  uint64_t end = fde->pc_end;
+  auto it = fdes_.upper_bound(start);
+  bool add_element = false;
+  while (it != fdes_.end() && start < end) {
+    if (add_element) {
+      add_element = false;
+      if (end < it->second.first) {
+        if (it->first == end) {
+          return;
+        }
+        fdes_[end] = std::make_pair(start, fde);
+        return;
+      }
+      if (start != it->second.first) {
+        fdes_[it->second.first] = std::make_pair(start, fde);
+      }
+    }
+    if (start < it->first) {
+      if (end < it->second.first) {
+        if (it->first != end) {
+          fdes_[end] = std::make_pair(start, fde);
+        }
+        return;
+      }
+      add_element = true;
+    }
+    start = it->first;
+    ++it;
+  }
+  if (start < end) {
+    fdes_[end] = std::make_pair(start, fde);
+  }
+}
 
-  // Loop through all of the entries and read just enough to create
-  // a sorted list of pcs.
-  // This code assumes that first comes the cie, then the fdes that
-  // it applies to.
-  uint64_t cie_offset = 0;
-  uint8_t address_encoding;
-  uint8_t segment_size;
-  while (memory_.cur_offset() < entries_end_) {
-    uint64_t cur_entry_offset = memory_.cur_offset();
+template <typename AddressType>
+bool DwarfSectionImplNoHdr<AddressType>::GetNextCieOrFde(DwarfFde** fde_entry) {
+  uint64_t start_offset = next_entries_offset_;
 
-    // Figure out the entry length and type.
-    uint32_t value32;
+  memory_.set_cur_offset(next_entries_offset_);
+  uint32_t value32;
+  if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  uint64_t cie_offset;
+  uint8_t cie_fde_encoding;
+  bool entry_is_cie = false;
+  if (value32 == static_cast<uint32_t>(-1)) {
+    // 64 bit entry.
+    uint64_t value64;
+    if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+
+    next_entries_offset_ = memory_.cur_offset() + value64;
+    // Read the Cie Id of a Cie or the pointer of the Fde.
+    if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+
+    if (value64 == cie64_value_) {
+      entry_is_cie = true;
+      cie_fde_encoding = DW_EH_PE_sdata8;
+    } else {
+      cie_offset = this->GetCieOffsetFromFde64(value64);
+    }
+  } else {
+    next_entries_offset_ = memory_.cur_offset() + value32;
+
+    // 32 bit Cie
     if (!memory_.ReadBytes(&value32, sizeof(value32))) {
       last_error_.code = DWARF_ERROR_MEMORY_INVALID;
       last_error_.address = memory_.cur_offset();
       return false;
     }
 
-    uint64_t next_entry_offset;
-    if (value32 == static_cast<uint32_t>(-1)) {
-      uint64_t value64;
-      if (!memory_.ReadBytes(&value64, sizeof(value64))) {
-        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-      next_entry_offset = memory_.cur_offset() + value64;
-
-      // Read the Cie Id of a Cie or the pointer of the Fde.
-      if (!memory_.ReadBytes(&value64, sizeof(value64))) {
-        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-
-      if (value64 == cie64_value_) {
-        // Cie 64 bit
-        address_encoding = DW_EH_PE_sdata8;
-        if (!GetCieInfo(&segment_size, &address_encoding)) {
-          return false;
-        }
-        cie_offset = cur_entry_offset;
-      } else {
-        uint64_t last_cie_offset = GetCieOffsetFromFde64(value64);
-        if (last_cie_offset != cie_offset) {
-          // This means that this Fde is not following the Cie.
-          last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-          return false;
-        }
-
-        // Fde 64 bit
-        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
-          return false;
-        }
-      }
+    if (value32 == cie32_value_) {
+      entry_is_cie = true;
+      cie_fde_encoding = DW_EH_PE_sdata4;
     } else {
-      next_entry_offset = memory_.cur_offset() + value32;
-
-      // Read the Cie Id of a Cie or the pointer of the Fde.
-      if (!memory_.ReadBytes(&value32, sizeof(value32))) {
-        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-
-      if (value32 == cie32_value_) {
-        // Cie 32 bit
-        address_encoding = DW_EH_PE_sdata4;
-        if (!GetCieInfo(&segment_size, &address_encoding)) {
-          return false;
-        }
-        cie_offset = cur_entry_offset;
-      } else {
-        uint64_t last_cie_offset = GetCieOffsetFromFde32(value32);
-        if (last_cie_offset != cie_offset) {
-          // This means that this Fde is not following the Cie.
-          last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-          return false;
-        }
-
-        // Fde 32 bit
-        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
-          return false;
-        }
-      }
+      cie_offset = this->GetCieOffsetFromFde32(value32);
     }
-
-    if (next_entry_offset < memory_.cur_offset()) {
-      // Simply consider the processing done in this case.
-      break;
-    }
-    memory_.set_cur_offset(next_entry_offset);
   }
 
-  // Sort the entries.
-  std::sort(fdes_.begin(), fdes_.end(), [](const FdeInfo& a, const FdeInfo& b) {
-    if (a.start == b.start) return a.end < b.end;
-    return a.start < b.start;
-  });
+  if (entry_is_cie) {
+    DwarfCie* cie = &cie_entries_[start_offset];
+    cie->lsda_encoding = DW_EH_PE_omit;
+    cie->cfa_instructions_end = next_entries_offset_;
+    cie->fde_address_encoding = cie_fde_encoding;
 
-  fde_count_ = fdes_.size();
+    if (!this->FillInCie(cie)) {
+      cie_entries_.erase(start_offset);
+      return false;
+    }
+    *fde_entry = nullptr;
+  } else {
+    DwarfFde* fde = &fde_entries_[start_offset];
+    fde->cfa_instructions_end = next_entries_offset_;
+    fde->cie_offset = cie_offset;
 
+    if (!this->FillInFde(fde)) {
+      fde_entries_.erase(start_offset);
+      return false;
+    }
+    *fde_entry = fde;
+  }
   return true;
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
-  if (fde_count_ == 0) {
-    return false;
-  }
-
-  size_t first = 0;
-  size_t last = fde_count_;
-  while (first < last) {
-    size_t current = (first + last) / 2;
-    const FdeInfo* info = &fdes_[current];
-    if (pc >= info->start && pc < info->end) {
-      *fde_offset = info->offset;
-      return true;
-    }
-
-    if (pc < info->start) {
-      last = current;
+void DwarfSectionImplNoHdr<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
+  // Loop through the already cached entries.
+  uint64_t entry_offset = entries_offset_;
+  while (entry_offset < next_entries_offset_) {
+    auto cie_it = cie_entries_.find(entry_offset);
+    if (cie_it != cie_entries_.end()) {
+      entry_offset = cie_it->second.cfa_instructions_end;
     } else {
-      first = current + 1;
+      auto fde_it = fde_entries_.find(entry_offset);
+      if (fde_it == fde_entries_.end()) {
+        // No fde or cie at this entry, should not be possible.
+        return;
+      }
+      entry_offset = fde_it->second.cfa_instructions_end;
+      fdes->push_back(&fde_it->second);
     }
   }
-  return false;
+
+  while (next_entries_offset_ < entries_end_) {
+    DwarfFde* fde;
+    if (!GetNextCieOrFde(&fde)) {
+      break;
+    }
+    if (fde != nullptr) {
+      InsertFde(fde);
+      fdes->push_back(fde);
+    }
+
+    if (next_entries_offset_ < memory_.cur_offset()) {
+      // Simply consider the processing done in this case.
+      break;
+    }
+  }
 }
 
 template <typename AddressType>
-const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromIndex(size_t index) {
-  if (index >= fdes_.size()) {
-    return nullptr;
+const DwarfFde* DwarfSectionImplNoHdr<AddressType>::GetFdeFromPc(uint64_t pc) {
+  // Search in the list of fdes we already have.
+  auto it = fdes_.upper_bound(pc);
+  if (it != fdes_.end()) {
+    if (pc >= it->second.first) {
+      return it->second.second;
+    }
   }
-  return this->GetFdeFromOffset(fdes_[index].offset);
+
+  // The section might have overlapping pcs in fdes, so it is necessary
+  // to do a linear search of the fdes by pc. As fdes are read, a cached
+  // search map is created.
+  while (next_entries_offset_ < entries_end_) {
+    DwarfFde* fde;
+    if (!GetNextCieOrFde(&fde)) {
+      return nullptr;
+    }
+    if (fde != nullptr) {
+      InsertFde(fde);
+      if (pc >= fde->pc_start && pc < fde->pc_end) {
+        return fde;
+      }
+    }
+
+    if (next_entries_offset_ < memory_.cur_offset()) {
+      // Simply consider the processing done in this case.
+      break;
+    }
+  }
+  return nullptr;
 }
 
 // Explicitly instantiate DwarfSectionImpl
 template class DwarfSectionImpl<uint32_t>;
 template class DwarfSectionImpl<uint64_t>;
 
+// Explicitly instantiate DwarfSectionImplNoHdr
+template class DwarfSectionImplNoHdr<uint32_t>;
+template class DwarfSectionImplNoHdr<uint64_t>;
+
 // Explicitly instantiate DwarfDebugFrame
 template class DwarfDebugFrame<uint32_t>;
 template class DwarfDebugFrame<uint64_t>;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 915cddb..2c00456 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -204,49 +204,19 @@
   uint64_t offset = ehdr.e_phoff;
   for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
     PhdrType phdr;
-    if (!memory_->ReadField(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
+    if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
       last_error_.code = ERROR_MEMORY_INVALID;
-      last_error_.address =
-          offset + reinterpret_cast<uintptr_t>(&phdr.p_type) - reinterpret_cast<uintptr_t>(&phdr);
+      last_error_.address = offset;
       return false;
     }
 
-    if (HandleType(offset, phdr.p_type)) {
-      continue;
-    }
-
     switch (phdr.p_type) {
     case PT_LOAD:
     {
-      // Get the flags first, if this isn't an executable header, ignore it.
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_flags) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       if ((phdr.p_flags & PF_X) == 0) {
         continue;
       }
 
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_vaddr) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_offset) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_memsz) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
                                           static_cast<size_t>(phdr.p_memsz)};
       if (phdr.p_offset == 0) {
@@ -256,46 +226,20 @@
     }
 
     case PT_GNU_EH_FRAME:
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_offset) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       // This is really the pointer to the .eh_frame_hdr section.
       eh_frame_hdr_offset_ = phdr.p_offset;
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_memsz) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       eh_frame_hdr_size_ = phdr.p_memsz;
       break;
 
     case PT_DYNAMIC:
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_offset) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       dynamic_offset_ = phdr.p_offset;
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_vaddr) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       dynamic_vaddr_ = phdr.p_vaddr;
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_memsz) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       dynamic_size_ = phdr.p_memsz;
       break;
+
+    default:
+      HandleUnknownType(phdr.p_type, phdr.p_offset, phdr.p_filesz);
+      break;
     }
   }
   return true;
@@ -313,8 +257,7 @@
   ShdrType shdr;
   if (ehdr.e_shstrndx < ehdr.e_shnum) {
     uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
-    if (memory_->ReadField(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
-        memory_->ReadField(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+    if (memory_->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
       sec_offset = shdr.sh_offset;
       sec_size = shdr.sh_size;
     }
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index a3244e8..3dd5d54 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -87,23 +87,17 @@
 #define PT_ARM_EXIDX 0x70000001
 #endif
 
-bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) {
+void ElfInterfaceArm::HandleUnknownType(uint32_t type, uint64_t ph_offset, uint64_t ph_filesz) {
   if (type != PT_ARM_EXIDX) {
-    return false;
-  }
-
-  Elf32_Phdr phdr;
-  if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
-    return true;
+    return;
   }
 
   // The offset already takes into account the load bias.
-  start_offset_ = phdr.p_offset;
+  start_offset_ = ph_offset;
 
   // Always use filesz instead of memsz. In most cases they are the same,
   // but some shared libraries wind up setting one correctly and not the other.
-  total_entries_ = phdr.p_filesz / 8;
-  return true;
+  total_entries_ = ph_filesz / 8;
 }
 
 bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index 3bee9cf..4c3a0c3 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -70,7 +70,7 @@
 
   bool FindEntry(uint32_t pc, uint64_t* entry_offset);
 
-  bool HandleType(uint64_t offset, uint32_t type) override;
+  void HandleUnknownType(uint32_t type, uint64_t ph_offset, uint64_t ph_filesz) override;
 
   bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
 
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index 847f382..e9942de 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -43,7 +43,12 @@
 
   class iterator : public std::iterator<std::bidirectional_iterator_tag, DwarfFde*> {
    public:
-    iterator(DwarfSection* section, size_t index) : section_(section), index_(index) {}
+    iterator(DwarfSection* section, size_t index) : index_(index) {
+      section->GetFdes(&fdes_);
+      if (index_ == static_cast<size_t>(-1)) {
+        index_ = fdes_.size();
+      }
+    }
 
     iterator& operator++() {
       index_++;
@@ -65,15 +70,18 @@
     bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
     bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
 
-    const DwarfFde* operator*() { return section_->GetFdeFromIndex(index_); }
+    const DwarfFde* operator*() {
+      if (index_ > fdes_.size()) return nullptr;
+      return fdes_[index_];
+    }
 
    private:
-    DwarfSection* section_ = nullptr;
+    std::vector<const DwarfFde*> fdes_;
     size_t index_ = 0;
   };
 
   iterator begin() { return iterator(this, 0); }
-  iterator end() { return iterator(this, fde_count_); }
+  iterator end() { return iterator(this, static_cast<size_t>(-1)); }
 
   DwarfErrorCode LastErrorCode() { return last_error_.code; }
   uint64_t LastErrorAddress() { return last_error_.address; }
@@ -82,15 +90,11 @@
 
   virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
 
-  virtual bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
-
   virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) = 0;
 
-  virtual const DwarfFde* GetFdeFromIndex(size_t index) = 0;
+  virtual void GetFdes(std::vector<const DwarfFde*>* fdes) = 0;
 
-  const DwarfFde* GetFdeFromPc(uint64_t pc);
-
-  virtual const DwarfFde* GetFdeFromOffset(uint64_t fde_offset) = 0;
+  virtual const DwarfFde* GetFdeFromPc(uint64_t pc) = 0;
 
   virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
 
@@ -109,7 +113,6 @@
   uint32_t cie32_value_ = 0;
   uint64_t cie64_value_ = 0;
 
-  uint64_t fde_count_ = 0;
   std::unordered_map<uint64_t, DwarfFde> fde_entries_;
   std::unordered_map<uint64_t, DwarfCie> cie_entries_;
   std::unordered_map<uint64_t, dwarf_loc_regs_t> cie_loc_regs_;
@@ -119,55 +122,73 @@
 template <typename AddressType>
 class DwarfSectionImpl : public DwarfSection {
  public:
-  struct FdeInfo {
-    FdeInfo(uint64_t offset, uint64_t start, uint64_t length)
-        : offset(offset), start(start), end(start + length) {}
-
-    uint64_t offset;
-    AddressType start;
-    AddressType end;
-  };
-
   DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
   virtual ~DwarfSectionImpl() = default;
 
-  bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
+  const DwarfCie* GetCieFromOffset(uint64_t offset);
 
-  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
-
-  const DwarfFde* GetFdeFromIndex(size_t index) override;
+  const DwarfFde* GetFdeFromOffset(uint64_t offset);
 
   bool EvalRegister(const DwarfLocation* loc, uint32_t reg, AddressType* reg_ptr, void* info);
 
   bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
             Regs* regs, bool* finished) override;
 
-  const DwarfCie* GetCie(uint64_t offset);
-  bool FillInCie(DwarfCie* cie);
-
-  const DwarfFde* GetFdeFromOffset(uint64_t offset) override;
-  bool FillInFde(DwarfFde* fde);
-
   bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
 
   bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) override;
 
  protected:
+  bool FillInCieHeader(DwarfCie* cie);
+
+  bool FillInCie(DwarfCie* cie);
+
+  bool FillInFdeHeader(DwarfFde* fde);
+
+  bool FillInFde(DwarfFde* fde);
+
   bool EvalExpression(const DwarfLocation& loc, Memory* regular_memory, AddressType* value,
                       RegsInfo<AddressType>* regs_info, bool* is_dex_pc);
 
-  bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding);
-
-  bool AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, uint8_t encoding);
-
-  bool CreateSortedFdeList();
-
   uint64_t load_bias_ = 0;
+  uint64_t entries_offset_ = 0;
+  uint64_t entries_end_ = 0;
   uint64_t pc_offset_ = 0;
+};
 
-  std::vector<FdeInfo> fdes_;
-  uint64_t entries_offset_;
-  uint64_t entries_end_;
+template <typename AddressType>
+class DwarfSectionImplNoHdr : public DwarfSectionImpl<AddressType> {
+ public:
+  // Add these so that the protected members of DwarfSectionImpl
+  // can be accessed without needing a this->.
+  using DwarfSectionImpl<AddressType>::memory_;
+  using DwarfSectionImpl<AddressType>::pc_offset_;
+  using DwarfSectionImpl<AddressType>::entries_offset_;
+  using DwarfSectionImpl<AddressType>::entries_end_;
+  using DwarfSectionImpl<AddressType>::last_error_;
+  using DwarfSectionImpl<AddressType>::load_bias_;
+  using DwarfSectionImpl<AddressType>::cie_entries_;
+  using DwarfSectionImpl<AddressType>::fde_entries_;
+  using DwarfSectionImpl<AddressType>::cie32_value_;
+  using DwarfSectionImpl<AddressType>::cie64_value_;
+
+  DwarfSectionImplNoHdr(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  virtual ~DwarfSectionImplNoHdr() = default;
+
+  bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
+
+  const DwarfFde* GetFdeFromPc(uint64_t pc) override;
+
+  void GetFdes(std::vector<const DwarfFde*>* fdes) override;
+
+ protected:
+  bool GetNextCieOrFde(DwarfFde** fde_entry);
+
+  void InsertFde(const DwarfFde* fde);
+
+  uint64_t next_entries_offset_ = 0;
+
+  std::map<uint64_t, std::pair<uint64_t, const DwarfFde*>> fdes_;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 0c588da..5c1210d 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -118,7 +118,7 @@
   template <typename SymType>
   bool GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address);
 
-  virtual bool HandleType(uint64_t, uint32_t) { return false; }
+  virtual void HandleUnknownType(uint32_t, uint64_t, uint64_t) {}
 
   template <typename EhdrType>
   static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index c0c07f4..dee5e98 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -41,18 +41,6 @@
 
   bool ReadFully(uint64_t addr, void* dst, size_t size);
 
-  inline bool ReadField(uint64_t addr, void* start, void* field, size_t size) {
-    if (reinterpret_cast<uintptr_t>(field) < reinterpret_cast<uintptr_t>(start)) {
-      return false;
-    }
-    uint64_t offset = reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start);
-    if (__builtin_add_overflow(addr, offset, &offset)) {
-      return false;
-    }
-    // The read will check if offset + size overflows.
-    return ReadFully(offset, field, size);
-  }
-
   inline bool Read32(uint64_t addr, uint32_t* dst) {
     return ReadFully(addr, dst, sizeof(uint32_t));
   }
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index 3a52044..d620934 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -16,7 +16,8 @@
 
 #include <stdint.h>
 
-#include <gmock/gmock.h>
+#include <vector>
+
 #include <gtest/gtest.h>
 
 #include <unwindstack/DwarfError.h>
@@ -30,480 +31,377 @@
 namespace unwindstack {
 
 template <typename TypeParam>
-class MockDwarfDebugFrame : public DwarfDebugFrame<TypeParam> {
- public:
-  MockDwarfDebugFrame(Memory* memory) : DwarfDebugFrame<TypeParam>(memory) {}
-  ~MockDwarfDebugFrame() = default;
-
-  void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
-  void TestSetOffset(uint64_t offset) { this->entries_offset_ = offset; }
-  void TestSetEndOffset(uint64_t offset) { this->entries_end_ = offset; }
-  void TestPushFdeInfo(const typename DwarfDebugFrame<TypeParam>::FdeInfo& info) {
-    this->fdes_.push_back(info);
-  }
-
-  uint64_t TestGetFdeCount() { return this->fde_count_; }
-  uint8_t TestGetOffset() { return this->offset_; }
-  uint8_t TestGetEndOffset() { return this->end_offset_; }
-  void TestGetFdeInfo(size_t index, typename DwarfDebugFrame<TypeParam>::FdeInfo* info) {
-    *info = this->fdes_[index];
-  }
-};
-
-template <typename TypeParam>
 class DwarfDebugFrameTest : public ::testing::Test {
  protected:
   void SetUp() override {
     memory_.Clear();
-    debug_frame_ = new MockDwarfDebugFrame<TypeParam>(&memory_);
+    debug_frame_ = new DwarfDebugFrame<TypeParam>(&memory_);
     ResetLogs();
   }
 
   void TearDown() override { delete debug_frame_; }
 
   MemoryFake memory_;
-  MockDwarfDebugFrame<TypeParam>* debug_frame_ = nullptr;
+  DwarfDebugFrame<TypeParam>* debug_frame_ = nullptr;
 };
 TYPED_TEST_CASE_P(DwarfDebugFrameTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
-TYPED_TEST_P(DwarfDebugFrameTest, Init32) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 1);
-  this->memory_.SetData8(0x5009, '\0');
+static void SetCie32(MemoryFake* memory, uint64_t offset, uint32_t length,
+                     std::vector<uint8_t> data) {
+  memory->SetData32(offset, length);
+  offset += 4;
+  // Indicates this is a cie.
+  memory->SetData32(offset, 0xffffffff);
+  offset += 4;
+  memory->SetMemory(offset, data);
+}
+
+static void SetCie64(MemoryFake* memory, uint64_t offset, uint64_t length,
+                     std::vector<uint8_t> data) {
+  memory->SetData32(offset, 0xffffffff);
+  offset += 4;
+  memory->SetData64(offset, length);
+  offset += 8;
+  // Indicates this is a cie.
+  memory->SetData64(offset, 0xffffffffffffffffUL);
+  offset += 8;
+  memory->SetMemory(offset, data);
+}
+
+static void SetFde32(MemoryFake* memory, uint64_t offset, uint32_t length, uint64_t cie_offset,
+                     uint32_t pc_start, uint32_t pc_length, uint64_t segment_length = 0,
+                     std::vector<uint8_t>* data = nullptr) {
+  memory->SetData32(offset, length);
+  offset += 4;
+  memory->SetData32(offset, cie_offset);
+  offset += 4 + segment_length;
+  memory->SetData32(offset, pc_start);
+  offset += 4;
+  memory->SetData32(offset, pc_length);
+  if (data != nullptr) {
+    offset += 4;
+    memory->SetMemory(offset, *data);
+  }
+}
+
+static void SetFde64(MemoryFake* memory, uint64_t offset, uint64_t length, uint64_t cie_offset,
+                     uint64_t pc_start, uint64_t pc_length, uint64_t segment_length = 0,
+                     std::vector<uint8_t>* data = nullptr) {
+  memory->SetData32(offset, 0xffffffff);
+  offset += 4;
+  memory->SetData64(offset, length);
+  offset += 8;
+  memory->SetData64(offset, cie_offset);
+  offset += 8 + segment_length;
+  memory->SetData64(offset, pc_start);
+  offset += 8;
+  memory->SetData64(offset, pc_length);
+  if (data != nullptr) {
+    offset += 8;
+    memory->SetMemory(offset, *data);
+  }
+}
+
+static void SetFourFdes32(MemoryFake* memory) {
+  SetCie32(memory, 0x5000, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
 
   // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0);
-  this->memory_.SetData32(0x5108, 0x1500);
-  this->memory_.SetData32(0x510c, 0x200);
-
-  this->memory_.SetData32(0x5200, 0xfc);
-  this->memory_.SetData32(0x5204, 0);
-  this->memory_.SetData32(0x5208, 0x2500);
-  this->memory_.SetData32(0x520c, 0x300);
+  SetFde32(memory, 0x5100, 0xfc, 0, 0x1500, 0x200);
+  SetFde32(memory, 0x5200, 0xfc, 0, 0x2500, 0x300);
 
   // CIE 32 information.
-  this->memory_.SetData32(0x5300, 0xfc);
-  this->memory_.SetData32(0x5304, 0xffffffff);
-  this->memory_.SetData8(0x5308, 1);
-  this->memory_.SetData8(0x5309, '\0');
+  SetCie32(memory, 0x5300, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
 
   // FDE 32 information.
-  this->memory_.SetData32(0x5400, 0xfc);
-  this->memory_.SetData32(0x5404, 0x300);
-  this->memory_.SetData32(0x5408, 0x3500);
-  this->memory_.SetData32(0x540c, 0x400);
+  SetFde32(memory, 0x5400, 0xfc, 0x300, 0x3500, 0x400);
+  SetFde32(memory, 0x5500, 0xfc, 0x300, 0x4500, 0x500);
+}
 
-  this->memory_.SetData32(0x5500, 0xfc);
-  this->memory_.SetData32(0x5504, 0x300);
-  this->memory_.SetData32(0x5508, 0x4500);
-  this->memory_.SetData32(0x550c, 0x500);
-
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes32) {
+  SetFourFdes32(&this->memory_);
   ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
-  ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
 
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
 
-  this->debug_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x1500U, info.start);
-  EXPECT_EQ(0x1700U, info.end);
+  ASSERT_EQ(4U, fdes.size());
 
-  this->debug_frame_->TestGetFdeInfo(1, &info);
-  EXPECT_EQ(0x5200U, info.offset);
-  EXPECT_EQ(0x2500U, info.start);
-  EXPECT_EQ(0x2800U, info.end);
+  EXPECT_EQ(0x5000U, fdes[0]->cie_offset);
+  EXPECT_EQ(0x5110U, fdes[0]->cfa_instructions_offset);
+  EXPECT_EQ(0x5200U, fdes[0]->cfa_instructions_end);
+  EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+  EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+  EXPECT_EQ(0U, fdes[0]->lsda_address);
+  EXPECT_TRUE(fdes[0]->cie != nullptr);
 
-  this->debug_frame_->TestGetFdeInfo(2, &info);
-  EXPECT_EQ(0x5400U, info.offset);
-  EXPECT_EQ(0x3500U, info.start);
-  EXPECT_EQ(0x3900U, info.end);
+  EXPECT_EQ(0x5000U, fdes[1]->cie_offset);
+  EXPECT_EQ(0x5210U, fdes[1]->cfa_instructions_offset);
+  EXPECT_EQ(0x5300U, fdes[1]->cfa_instructions_end);
+  EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+  EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+  EXPECT_EQ(0U, fdes[1]->lsda_address);
+  EXPECT_TRUE(fdes[1]->cie != nullptr);
 
-  this->debug_frame_->TestGetFdeInfo(3, &info);
-  EXPECT_EQ(0x5500U, info.offset);
-  EXPECT_EQ(0x4500U, info.start);
-  EXPECT_EQ(0x4a00U, info.end);
+  EXPECT_EQ(0x5300U, fdes[2]->cie_offset);
+  EXPECT_EQ(0x5410U, fdes[2]->cfa_instructions_offset);
+  EXPECT_EQ(0x5500U, fdes[2]->cfa_instructions_end);
+  EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+  EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+  EXPECT_EQ(0U, fdes[2]->lsda_address);
+  EXPECT_TRUE(fdes[2]->cie != nullptr);
+
+  EXPECT_EQ(0x5300U, fdes[3]->cie_offset);
+  EXPECT_EQ(0x5510U, fdes[3]->cfa_instructions_offset);
+  EXPECT_EQ(0x5600U, fdes[3]->cfa_instructions_end);
+  EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+  EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
+  EXPECT_EQ(0U, fdes[3]->lsda_address);
+  EXPECT_TRUE(fdes[3]->cie != nullptr);
 }
 
-TYPED_TEST_P(DwarfDebugFrameTest, Init32_fde_not_following_cie) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 1);
-  this->memory_.SetData8(0x5009, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0x1000);
-  this->memory_.SetData32(0x5108, 0x1500);
-  this->memory_.SetData32(0x510c, 0x200);
-
-  ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600, 0));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->LastErrorCode());
-}
-
-TYPED_TEST_P(DwarfDebugFrameTest, Init32_do_not_fail_on_bad_next_entry) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 1);
-  this->memory_.SetData8(0x5009, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0);
-  this->memory_.SetData32(0x5108, 0x1500);
-  this->memory_.SetData32(0x510c, 0x200);
-
-  this->memory_.SetData32(0x5200, 0xfc);
-  this->memory_.SetData32(0x5204, 0);
-  this->memory_.SetData32(0x5208, 0x2500);
-  this->memory_.SetData32(0x520c, 0x300);
-
-  // CIE 32 information.
-  this->memory_.SetData32(0x5300, 0);
-  this->memory_.SetData32(0x5304, 0xffffffff);
-  this->memory_.SetData8(0x5308, 1);
-  this->memory_.SetData8(0x5309, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5400, 0xfc);
-  this->memory_.SetData32(0x5404, 0x300);
-  this->memory_.SetData32(0x5408, 0x3500);
-  this->memory_.SetData32(0x540c, 0x400);
-
-  this->memory_.SetData32(0x5500, 0xfc);
-  this->memory_.SetData32(0x5504, 0x300);
-  this->memory_.SetData32(0x5508, 0x4500);
-  this->memory_.SetData32(0x550c, 0x500);
-
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes32_after_GetFdeFromPc) {
+  SetFourFdes32(&this->memory_);
   ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
-  ASSERT_EQ(2U, this->debug_frame_->TestGetFdeCount());
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x3600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x3500U, fde->pc_start);
+  EXPECT_EQ(0x3900U, fde->pc_end);
+
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
+  ASSERT_EQ(4U, fdes.size());
+
+  // Verify that they got added in the correct order.
+  EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+  EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+  EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+  EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+  EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+  EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+  EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+  EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
 }
 
-TYPED_TEST_P(DwarfDebugFrameTest, Init64) {
-  // CIE 64 information.
-  this->memory_.SetData32(0x5000, 0xffffffff);
-  this->memory_.SetData64(0x5004, 0xf4);
-  this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x5014, 1);
-  this->memory_.SetData8(0x5015, '\0');
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes32_not_in_section) {
+  SetFourFdes32(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
 
-  // FDE 64 information.
-  this->memory_.SetData32(0x5100, 0xffffffff);
-  this->memory_.SetData64(0x5104, 0xf4);
-  this->memory_.SetData64(0x510c, 0);
-  this->memory_.SetData64(0x5114, 0x1500);
-  this->memory_.SetData64(0x511c, 0x200);
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
 
-  this->memory_.SetData32(0x5200, 0xffffffff);
-  this->memory_.SetData64(0x5204, 0xf4);
-  this->memory_.SetData64(0x520c, 0);
-  this->memory_.SetData64(0x5214, 0x2500);
-  this->memory_.SetData64(0x521c, 0x300);
+  ASSERT_EQ(3U, fdes.size());
+}
 
-  // CIE 64 information.
-  this->memory_.SetData32(0x5300, 0xffffffff);
-  this->memory_.SetData64(0x5304, 0xf4);
-  this->memory_.SetData64(0x530c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x5314, 1);
-  this->memory_.SetData8(0x5315, '\0');
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x5400, 0xffffffff);
-  this->memory_.SetData64(0x5404, 0xf4);
-  this->memory_.SetData64(0x540c, 0x300);
-  this->memory_.SetData64(0x5414, 0x3500);
-  this->memory_.SetData64(0x541c, 0x400);
-
-  this->memory_.SetData32(0x5500, 0xffffffff);
-  this->memory_.SetData64(0x5504, 0xf4);
-  this->memory_.SetData64(0x550c, 0x300);
-  this->memory_.SetData64(0x5514, 0x4500);
-  this->memory_.SetData64(0x551c, 0x500);
-
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc32) {
+  SetFourFdes32(&this->memory_);
   ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
-  ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
 
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x1600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x1500U, fde->pc_start);
 
-  this->debug_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x1500U, info.start);
-  EXPECT_EQ(0x1700U, info.end);
-
-  this->debug_frame_->TestGetFdeInfo(1, &info);
-  EXPECT_EQ(0x5200U, info.offset);
-  EXPECT_EQ(0x2500U, info.start);
-  EXPECT_EQ(0x2800U, info.end);
-
-  this->debug_frame_->TestGetFdeInfo(2, &info);
-  EXPECT_EQ(0x5400U, info.offset);
-  EXPECT_EQ(0x3500U, info.start);
-  EXPECT_EQ(0x3900U, info.end);
-
-  this->debug_frame_->TestGetFdeInfo(3, &info);
-  EXPECT_EQ(0x5500U, info.offset);
-  EXPECT_EQ(0x4500U, info.start);
-  EXPECT_EQ(0x4a00U, info.end);
-}
-
-TYPED_TEST_P(DwarfDebugFrameTest, Init64_fde_not_following_cie) {
-  // CIE 64 information.
-  this->memory_.SetData32(0x5000, 0xffffffff);
-  this->memory_.SetData64(0x5004, 0xf4);
-  this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x5014, 1);
-  this->memory_.SetData8(0x5015, '\0');
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x5100, 0xffffffff);
-  this->memory_.SetData64(0x5104, 0xf4);
-  this->memory_.SetData64(0x510c, 0x1000);
-  this->memory_.SetData64(0x5114, 0x1500);
-  this->memory_.SetData64(0x511c, 0x200);
-
-  ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600, 0));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->LastErrorCode());
-}
-
-TYPED_TEST_P(DwarfDebugFrameTest, Init64_do_not_fail_on_bad_next_entry) {
-  // CIE 64 information.
-  this->memory_.SetData32(0x5000, 0xffffffff);
-  this->memory_.SetData64(0x5004, 0xf4);
-  this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x5014, 1);
-  this->memory_.SetData8(0x5015, '\0');
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x5100, 0xffffffff);
-  this->memory_.SetData64(0x5104, 0xf4);
-  this->memory_.SetData64(0x510c, 0);
-  this->memory_.SetData64(0x5114, 0x1500);
-  this->memory_.SetData64(0x511c, 0x200);
-
-  this->memory_.SetData32(0x5200, 0xffffffff);
-  this->memory_.SetData64(0x5204, 0xf4);
-  this->memory_.SetData64(0x520c, 0);
-  this->memory_.SetData64(0x5214, 0x2500);
-  this->memory_.SetData64(0x521c, 0x300);
-
-  // CIE 64 information.
-  this->memory_.SetData32(0x5300, 0xffffffff);
-  this->memory_.SetData64(0x5304, 0);
-  this->memory_.SetData64(0x530c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x5314, 1);
-  this->memory_.SetData8(0x5315, '\0');
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x5400, 0xffffffff);
-  this->memory_.SetData64(0x5404, 0xf4);
-  this->memory_.SetData64(0x540c, 0x300);
-  this->memory_.SetData64(0x5414, 0x3500);
-  this->memory_.SetData64(0x541c, 0x400);
-
-  this->memory_.SetData32(0x5500, 0xffffffff);
-  this->memory_.SetData64(0x5504, 0xf4);
-  this->memory_.SetData64(0x550c, 0x300);
-  this->memory_.SetData64(0x5514, 0x4500);
-  this->memory_.SetData64(0x551c, 0x500);
-
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
-  ASSERT_EQ(2U, this->debug_frame_->TestGetFdeCount());
-}
-
-TYPED_TEST_P(DwarfDebugFrameTest, Init_non_zero_load_bias) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 1);
-  this->memory_.SetData8(0x5009, 'z');
-  this->memory_.SetData8(0x500a, 'R');
-  this->memory_.SetData8(0x500b, '\0');
-  this->memory_.SetData8(0x500c, 0);
-  this->memory_.SetData8(0x500d, 0);
-  this->memory_.SetData8(0x500e, 0);
-  this->memory_.SetData8(0x500f, 0);
-  this->memory_.SetData8(0x5010, 0x1b);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0);
-  this->memory_.SetData32(0x5108, 0x1500);
-  this->memory_.SetData32(0x510c, 0x200);
-  this->memory_.SetData8(0x5110, 0);
-  this->memory_.SetData8(0x5111, 0);
-
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200, 0x1000));
-  ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
-
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
-
-  this->debug_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x2500U, info.start);
-  EXPECT_EQ(0x2700U, info.end);
-
-  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x2504);
+  fde = this->debug_frame_->GetFdeFromPc(0x2600);
   ASSERT_TRUE(fde != nullptr);
   EXPECT_EQ(0x2500U, fde->pc_start);
-  EXPECT_EQ(0x2700U, fde->pc_end);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x3600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x3500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0);
+  ASSERT_TRUE(fde == nullptr);
 }
 
-TYPED_TEST_P(DwarfDebugFrameTest, Init_version1) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 1);
-  // Augment string.
-  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'R', 'P', 'L', '\0'});
-  // Code alignment factor.
-  this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x80, 0x00});
-  // Data alignment factor.
-  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
-  // Return address register
-  this->memory_.SetData8(0x5014, 0x84);
-  // Augmentation length
-  this->memory_.SetMemory(0x5015, std::vector<uint8_t>{0x84, 0x00});
-  // R data.
-  this->memory_.SetData8(0x5017, DW_EH_PE_pcrel | DW_EH_PE_udata2);
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc32_reverse) {
+  SetFourFdes32(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
 
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0);
-  this->memory_.SetData16(0x5108, 0x1500);
-  this->memory_.SetData16(0x510a, 0x200);
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
 
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200, 0));
-  ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
+  fde = this->debug_frame_->GetFdeFromPc(0x3600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x3500U, fde->pc_start);
 
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  this->debug_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x1500U, info.start);
-  EXPECT_EQ(0x1700U, info.end);
+  fde = this->debug_frame_->GetFdeFromPc(0x2600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x1600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x1500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0);
+  ASSERT_TRUE(fde == nullptr);
 }
 
-TYPED_TEST_P(DwarfDebugFrameTest, Init_version4) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 4);
-  // Augment string.
-  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
-  // Address size.
-  this->memory_.SetData8(0x500e, 4);
-  // Segment size.
-  this->memory_.SetData8(0x500f, 0);
-  // Code alignment factor.
-  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x80, 0x00});
-  // Data alignment factor.
-  this->memory_.SetMemory(0x5012, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
-  // Return address register
-  this->memory_.SetMemory(0x5016, std::vector<uint8_t>{0x85, 0x10});
-  // Augmentation length
-  this->memory_.SetMemory(0x5018, std::vector<uint8_t>{0x84, 0x00});
-  // L data.
-  this->memory_.SetData8(0x501a, 0x10);
-  // P data.
-  this->memory_.SetData8(0x501b, DW_EH_PE_udata4);
-  this->memory_.SetData32(0x501c, 0x100);
-  // R data.
-  this->memory_.SetData8(0x5020, DW_EH_PE_pcrel | DW_EH_PE_udata2);
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc32_not_in_section) {
+  SetFourFdes32(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
 
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0);
-  this->memory_.SetData16(0x5108, 0x1500);
-  this->memory_.SetData16(0x510a, 0x200);
-
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200, 0));
-  ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
-
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  this->debug_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x1500U, info.start);
-  EXPECT_EQ(0x1700U, info.end);
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde == nullptr);
 }
 
-TYPED_TEST_P(DwarfDebugFrameTest, GetFdeOffsetFromPc) {
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  for (size_t i = 0; i < 9; i++) {
-    info.start = 0x1000 * (i + 1);
-    info.end = 0x1000 * (i + 2) - 0x10;
-    info.offset = 0x5000 + i * 0x20;
-    this->debug_frame_->TestPushFdeInfo(info);
-  }
+static void SetFourFdes64(MemoryFake* memory) {
+  // CIE 64 information.
+  SetCie64(memory, 0x5000, 0xf4, std::vector<uint8_t>{1, '\0', 0, 0, 1});
 
-  this->debug_frame_->TestSetFdeCount(0);
-  uint64_t fde_offset;
-  ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  // FDE 64 information.
+  SetFde64(memory, 0x5100, 0xf4, 0, 0x1500, 0x200);
+  SetFde64(memory, 0x5200, 0xf4, 0, 0x2500, 0x300);
 
-  this->debug_frame_->TestSetFdeCount(9);
-  ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
-  // Odd number of elements.
-  for (size_t i = 0; i < 9; i++) {
-    TypeParam pc = 0x1000 * (i + 1);
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
-                                                                             << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
-        << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
-  }
+  // CIE 64 information.
+  SetCie64(memory, 0x5300, 0xf4, std::vector<uint8_t>{1, '\0', 0, 0, 1});
 
-  // Even number of elements.
-  this->debug_frame_->TestSetFdeCount(10);
-  info.start = 0xa000;
-  info.end = 0xaff0;
-  info.offset = 0x5120;
-  this->debug_frame_->TestPushFdeInfo(info);
+  // FDE 64 information.
+  SetFde64(memory, 0x5400, 0xf4, 0x300, 0x3500, 0x400);
+  SetFde64(memory, 0x5500, 0xf4, 0x300, 0x4500, 0x500);
+}
 
-  for (size_t i = 0; i < 10; i++) {
-    TypeParam pc = 0x1000 * (i + 1);
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
-                                                                             << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
-        << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
-  }
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes64) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
+
+  ASSERT_EQ(4U, fdes.size());
+
+  EXPECT_EQ(0x5000U, fdes[0]->cie_offset);
+  EXPECT_EQ(0x5124U, fdes[0]->cfa_instructions_offset);
+  EXPECT_EQ(0x5200U, fdes[0]->cfa_instructions_end);
+  EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+  EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+  EXPECT_EQ(0U, fdes[0]->lsda_address);
+  EXPECT_TRUE(fdes[0]->cie != nullptr);
+
+  EXPECT_EQ(0x5000U, fdes[1]->cie_offset);
+  EXPECT_EQ(0x5224U, fdes[1]->cfa_instructions_offset);
+  EXPECT_EQ(0x5300U, fdes[1]->cfa_instructions_end);
+  EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+  EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+  EXPECT_EQ(0U, fdes[1]->lsda_address);
+  EXPECT_TRUE(fdes[1]->cie != nullptr);
+
+  EXPECT_EQ(0x5300U, fdes[2]->cie_offset);
+  EXPECT_EQ(0x5424U, fdes[2]->cfa_instructions_offset);
+  EXPECT_EQ(0x5500U, fdes[2]->cfa_instructions_end);
+  EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+  EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+  EXPECT_EQ(0U, fdes[2]->lsda_address);
+  EXPECT_TRUE(fdes[2]->cie != nullptr);
+
+  EXPECT_EQ(0x5300U, fdes[3]->cie_offset);
+  EXPECT_EQ(0x5524U, fdes[3]->cfa_instructions_offset);
+  EXPECT_EQ(0x5600U, fdes[3]->cfa_instructions_end);
+  EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+  EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
+  EXPECT_EQ(0U, fdes[3]->lsda_address);
+  EXPECT_TRUE(fdes[3]->cie != nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes64_after_GetFdeFromPc) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x2600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
+  EXPECT_EQ(0x2800U, fde->pc_end);
+
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
+  ASSERT_EQ(4U, fdes.size());
+
+  // Verify that they got added in the correct order.
+  EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+  EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+  EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+  EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+  EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+  EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+  EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+  EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes64_not_in_section) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
+
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
+
+  ASSERT_EQ(3U, fdes.size());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc64) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x1600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x1500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x2600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x3600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x3500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0);
+  ASSERT_TRUE(fde == nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc64_reverse) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x3600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x3500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x2600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x1600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x1500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0);
+  ASSERT_TRUE(fde == nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc64_not_in_section) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde == nullptr);
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde32) {
-  this->debug_frame_->TestSetOffset(0x4000);
-
-  // CIE 32 information.
-  this->memory_.SetData32(0xf000, 0x100);
-  this->memory_.SetData32(0xf004, 0xffffffff);
-  this->memory_.SetData8(0xf008, 0x1);
-  this->memory_.SetData8(0xf009, '\0');
-  this->memory_.SetData8(0xf00a, 4);
-  this->memory_.SetData8(0xf00b, 8);
-  this->memory_.SetData8(0xf00c, 0x20);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x14000, 0x20);
-  this->memory_.SetData32(0x14004, 0xb000);
-  this->memory_.SetData32(0x14008, 0x9000);
-  this->memory_.SetData32(0x1400c, 0x100);
+  SetCie32(&this->memory_, 0xf000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  SetFde32(&this->memory_, 0x14000, 0x20, 0xf000, 0x9000, 0x100);
 
   const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x14000);
   ASSERT_TRUE(fde != nullptr);
@@ -530,24 +428,8 @@
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde64) {
-  this->debug_frame_->TestSetOffset(0x2000);
-
-  // CIE 64 information.
-  this->memory_.SetData32(0x6000, 0xffffffff);
-  this->memory_.SetData64(0x6004, 0x100);
-  this->memory_.SetData64(0x600c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x6014, 0x1);
-  this->memory_.SetData8(0x6015, '\0');
-  this->memory_.SetData8(0x6016, 4);
-  this->memory_.SetData8(0x6017, 8);
-  this->memory_.SetData8(0x6018, 0x20);
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x8000, 0xffffffff);
-  this->memory_.SetData64(0x8004, 0x200);
-  this->memory_.SetData64(0x800c, 0x4000);
-  this->memory_.SetData64(0x8014, 0x5000);
-  this->memory_.SetData64(0x801c, 0x300);
+  SetCie64(&this->memory_, 0x6000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  SetFde64(&this->memory_, 0x8000, 0x200, 0x6000, 0x5000, 0x300);
 
   const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x8000);
   ASSERT_TRUE(fde != nullptr);
@@ -573,11 +455,357 @@
   EXPECT_EQ(0x20U, fde->cie->return_address_register);
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie,
-                           Init32_do_not_fail_on_bad_next_entry, Init64,
-                           Init64_do_not_fail_on_bad_next_entry, Init64_fde_not_following_cie,
-                           Init_non_zero_load_bias, Init_version1, Init_version4,
-                           GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
+static void VerifyCieVersion(const DwarfCie* cie, uint8_t version, uint8_t segment_size,
+                             uint8_t fde_encoding, uint64_t return_address, uint64_t start_offset,
+                             uint64_t end_offset) {
+  EXPECT_EQ(version, cie->version);
+  EXPECT_EQ(fde_encoding, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(segment_size, cie->segment_size);
+  EXPECT_EQ(1U, cie->augmentation_string.size());
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(return_address, cie->return_address_register);
+  EXPECT_EQ(0x5000U + start_offset, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5000U + end_offset, cie->cfa_instructions_end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_cie_cached) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+
+  std::vector<uint8_t> zero(0x100, 0);
+  this->memory_.SetMemory(0x5000, zero);
+  cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_cie_cached) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+
+  std::vector<uint8_t> zero(0x100, 0);
+  this->memory_.SetMemory(0x5000, zero);
+  cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version1) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version1) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version3) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{3, '\0', 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 3, 0, DW_EH_PE_sdata4, 0x181, 0xe, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version3) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{3, '\0', 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 3, 0, DW_EH_PE_sdata8, 0x181, 0x1a, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version4) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 0, 10, 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata4, 0x181, 0x10, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version4) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 0, 10, 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset_version_invalid) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{0, '\0', 1, 2, 3, 4, 5, 6, 7});
+  ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x5000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+  SetCie64(&this->memory_, 0x6000, 0x100, std::vector<uint8_t>{0, '\0', 1, 2, 3, 4, 5, 6, 7});
+  ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x6000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+
+  SetCie32(&this->memory_, 0x7000, 0x100, std::vector<uint8_t>{5, '\0', 1, 2, 3, 4, 5, 6, 7});
+  ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x7000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+  SetCie64(&this->memory_, 0x8000, 0x100, std::vector<uint8_t>{5, '\0', 1, 2, 3, 4, 5, 6, 7});
+  ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x8000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+}
+
+static void VerifyCieAugment(const DwarfCie* cie, uint64_t inst_offset, uint64_t inst_end) {
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_udata2, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_textrel | DW_EH_PE_udata2, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ(5U, cie->augmentation_string.size());
+  EXPECT_EQ('z', cie->augmentation_string[0]);
+  EXPECT_EQ('L', cie->augmentation_string[1]);
+  EXPECT_EQ('P', cie->augmentation_string[2]);
+  EXPECT_EQ('R', cie->augmentation_string[3]);
+  EXPECT_EQ('\0', cie->augmentation_string[4]);
+  EXPECT_EQ(0x12345678U, cie->personality_handler);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(0x10U, cie->return_address_register);
+  EXPECT_EQ(inst_offset, cie->cfa_instructions_offset);
+  EXPECT_EQ(inst_end, cie->cfa_instructions_end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_augment) {
+  SetCie32(&this->memory_, 0x5000, 0x100,
+           std::vector<uint8_t>{/* version */ 1,
+                                /* augment string */ 'z', 'L', 'P', 'R', '\0',
+                                /* code alignment factor */ 4,
+                                /* data alignment factor */ 8,
+                                /* return address register */ 0x10,
+                                /* augment length */ 0xf,
+                                /* L data */ DW_EH_PE_textrel | DW_EH_PE_udata2,
+                                /* P data */ DW_EH_PE_udata4, 0x78, 0x56, 0x34, 0x12,
+                                /* R data */ DW_EH_PE_udata2});
+
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieAugment(cie, 0x5021, 0x5104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_augment) {
+  SetCie64(&this->memory_, 0x5000, 0x100,
+           std::vector<uint8_t>{/* version */ 1,
+                                /* augment string */ 'z', 'L', 'P', 'R', '\0',
+                                /* code alignment factor */ 4,
+                                /* data alignment factor */ 8,
+                                /* return address register */ 0x10,
+                                /* augment length */ 0xf,
+                                /* L data */ DW_EH_PE_textrel | DW_EH_PE_udata2,
+                                /* P data */ DW_EH_PE_udata4, 0x78, 0x56, 0x34, 0x12,
+                                /* R data */ DW_EH_PE_udata2});
+
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieAugment(cie, 0x502d, 0x510c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset32_augment) {
+  SetCie32(&this->memory_, 0x5000, 0xfc,
+           std::vector<uint8_t>{/* version */ 4,
+                                /* augment string */ 'z', '\0',
+                                /* address size */ 8,
+                                /* segment size */ 0x10,
+                                /* code alignment factor */ 16,
+                                /* data alignment factor */ 32,
+                                /* return address register */ 10,
+                                /* augment length */ 0x0});
+
+  std::vector<uint8_t> data{/* augment length */ 0x80, 0x3};
+  SetFde32(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0x10, &data);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(4U, fde->cie->version);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x53a2U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x5504U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x4300U, fde->pc_start);
+  EXPECT_EQ(0x4600U, fde->pc_end);
+  EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset64_augment) {
+  SetCie64(&this->memory_, 0x5000, 0xfc,
+           std::vector<uint8_t>{/* version */ 4,
+                                /* augment string */ 'z', '\0',
+                                /* address size */ 8,
+                                /* segment size */ 0x10,
+                                /* code alignment factor */ 16,
+                                /* data alignment factor */ 32,
+                                /* return address register */ 10,
+                                /* augment length */ 0x0});
+
+  std::vector<uint8_t> data{/* augment length */ 0x80, 0x3};
+  SetFde64(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0x10, &data);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(4U, fde->cie->version);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x53b6U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x550cU, fde->cfa_instructions_end);
+  EXPECT_EQ(0x4300U, fde->pc_start);
+  EXPECT_EQ(0x4600U, fde->pc_end);
+  EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset32_lsda_address) {
+  SetCie32(&this->memory_, 0x5000, 0xfc,
+           std::vector<uint8_t>{/* version */ 1,
+                                /* augment string */ 'z', 'L', '\0',
+                                /* address size */ 8,
+                                /* code alignment factor */ 16,
+                                /* data alignment factor */ 32,
+                                /* return address register */ 10,
+                                /* augment length */ 0x2,
+                                /* L data */ DW_EH_PE_udata2});
+
+  std::vector<uint8_t> data{/* augment length */ 0x80, 0x3,
+                            /* lsda address */ 0x20, 0x45};
+  SetFde32(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0, &data);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x5392U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x5504U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x4300U, fde->pc_start);
+  EXPECT_EQ(0x4600U, fde->pc_end);
+  EXPECT_EQ(0x4520U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset64_lsda_address) {
+  SetCie64(&this->memory_, 0x5000, 0xfc,
+           std::vector<uint8_t>{/* version */ 1,
+                                /* augment string */ 'z', 'L', '\0',
+                                /* address size */ 8,
+                                /* code alignment factor */ 16,
+                                /* data alignment factor */ 32,
+                                /* return address register */ 10,
+                                /* augment length */ 0x2,
+                                /* L data */ DW_EH_PE_udata2});
+
+  std::vector<uint8_t> data{/* augment length */ 0x80, 0x3,
+                            /* lsda address */ 0x20, 0x45};
+  SetFde64(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0, &data);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x53a6U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x550cU, fde->cfa_instructions_end);
+  EXPECT_EQ(0x4300U, fde->pc_start);
+  EXPECT_EQ(0x4600U, fde->pc_end);
+  EXPECT_EQ(0x4520U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc_interleaved) {
+  SetCie32(&this->memory_, 0x5000, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+  // FDE 0 (0x100 - 0x200)
+  SetFde32(&this->memory_, 0x5100, 0xfc, 0, 0x100, 0x100);
+  // FDE 1 (0x300 - 0x500)
+  SetFde32(&this->memory_, 0x5200, 0xfc, 0, 0x300, 0x200);
+  // FDE 2 (0x700 - 0x800)
+  SetFde32(&this->memory_, 0x5300, 0xfc, 0, 0x700, 0x100);
+  // FDE 3 (0xa00 - 0xb00)
+  SetFde32(&this->memory_, 0x5400, 0xfc, 0, 0xa00, 0x100);
+  // FDE 4 (0x100 - 0xb00)
+  SetFde32(&this->memory_, 0x5500, 0xfc, 0, 0x150, 0xa00);
+  // FDE 5 (0x0 - 0x50)
+  SetFde32(&this->memory_, 0x5600, 0xfc, 0, 0, 0x50);
+
+  this->debug_frame_->Init(0x5000, 0x700, 0);
+
+  // Force reading all entries so no entries are found.
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0xfffff);
+  ASSERT_TRUE(fde == nullptr);
+
+  //   0x0   - 0x50   FDE 5
+  fde = this->debug_frame_->GetFdeFromPc(0x10);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0U, fde->pc_start);
+  EXPECT_EQ(0x50U, fde->pc_end);
+
+  //   0x100 - 0x200  FDE 0
+  fde = this->debug_frame_->GetFdeFromPc(0x170);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x100U, fde->pc_start);
+  EXPECT_EQ(0x200U, fde->pc_end);
+
+  //   0x200 - 0x300  FDE 4
+  fde = this->debug_frame_->GetFdeFromPc(0x210);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x150U, fde->pc_start);
+  EXPECT_EQ(0xb50U, fde->pc_end);
+
+  //   0x300 - 0x500  FDE 1
+  fde = this->debug_frame_->GetFdeFromPc(0x310);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x300U, fde->pc_start);
+  EXPECT_EQ(0x500U, fde->pc_end);
+
+  //   0x700 - 0x800  FDE 2
+  fde = this->debug_frame_->GetFdeFromPc(0x790);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x700U, fde->pc_start);
+  EXPECT_EQ(0x800U, fde->pc_end);
+
+  //   0x800 - 0x900  FDE 4
+  fde = this->debug_frame_->GetFdeFromPc(0x850);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x150U, fde->pc_start);
+  EXPECT_EQ(0xb50U, fde->pc_end);
+
+  //   0xa00 - 0xb00  FDE 3
+  fde = this->debug_frame_->GetFdeFromPc(0xa35);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0xa00U, fde->pc_start);
+  EXPECT_EQ(0xb00U, fde->pc_end);
+
+  //   0xb00 - 0xb50  FDE 4
+  fde = this->debug_frame_->GetFdeFromPc(0xb20);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x150U, fde->pc_start);
+  EXPECT_EQ(0xb50U, fde->pc_end);
+}
+
+REGISTER_TYPED_TEST_CASE_P(
+    DwarfDebugFrameTest, GetFdes32, GetFdes32_after_GetFdeFromPc, GetFdes32_not_in_section,
+    GetFdeFromPc32, GetFdeFromPc32_reverse, GetFdeFromPc32_not_in_section, GetFdes64,
+    GetFdes64_after_GetFdeFromPc, GetFdes64_not_in_section, GetFdeFromPc64, GetFdeFromPc64_reverse,
+    GetFdeFromPc64_not_in_section, GetCieFde32, GetCieFde64, GetCieFromOffset32_cie_cached,
+    GetCieFromOffset64_cie_cached, GetCieFromOffset32_version1, GetCieFromOffset64_version1,
+    GetCieFromOffset32_version3, GetCieFromOffset64_version3, GetCieFromOffset32_version4,
+    GetCieFromOffset64_version4, GetCieFromOffset_version_invalid, GetCieFromOffset32_augment,
+    GetCieFromOffset64_augment, GetFdeFromOffset32_augment, GetFdeFromOffset64_augment,
+    GetFdeFromOffset32_lsda_address, GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index e8d53e6..9cac6e8 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -16,7 +16,6 @@
 
 #include <stdint.h>
 
-#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include <unwindstack/DwarfError.h>
@@ -30,50 +29,32 @@
 namespace unwindstack {
 
 template <typename TypeParam>
-class MockDwarfEhFrame : public DwarfEhFrame<TypeParam> {
- public:
-  MockDwarfEhFrame(Memory* memory) : DwarfEhFrame<TypeParam>(memory) {}
-  ~MockDwarfEhFrame() = default;
-
-  void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
-  void TestSetOffset(uint64_t offset) { this->entries_offset_ = offset; }
-  void TestSetEndOffset(uint64_t offset) { this->entries_end_ = offset; }
-  void TestPushFdeInfo(const typename DwarfEhFrame<TypeParam>::FdeInfo& info) {
-    this->fdes_.push_back(info);
-  }
-
-  uint64_t TestGetFdeCount() { return this->fde_count_; }
-  uint8_t TestGetOffset() { return this->offset_; }
-  uint8_t TestGetEndOffset() { return this->end_offset_; }
-  void TestGetFdeInfo(size_t index, typename DwarfEhFrame<TypeParam>::FdeInfo* info) {
-    *info = this->fdes_[index];
-  }
-};
-
-template <typename TypeParam>
 class DwarfEhFrameTest : public ::testing::Test {
  protected:
   void SetUp() override {
     memory_.Clear();
-    eh_frame_ = new MockDwarfEhFrame<TypeParam>(&memory_);
+    eh_frame_ = new DwarfEhFrame<TypeParam>(&memory_);
     ResetLogs();
   }
 
   void TearDown() override { delete eh_frame_; }
 
   MemoryFake memory_;
-  MockDwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
+  DwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
 };
 TYPED_TEST_CASE_P(DwarfEhFrameTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
-TYPED_TEST_P(DwarfEhFrameTest, Init32) {
+// Only verify different cie/fde format. All other DwarfSection corner
+// cases are tested in DwarfDebugFrameTest.cpp.
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeCieFromOffset32) {
   // CIE 32 information.
   this->memory_.SetData32(0x5000, 0xfc);
+  // Indicates this is a cie for eh_frame.
   this->memory_.SetData32(0x5004, 0);
-  this->memory_.SetData8(0x5008, 1);
-  this->memory_.SetData8(0x5009, '\0');
+  this->memory_.SetMemory(0x5008, std::vector<uint8_t>{1, '\0', 16, 32, 1});
 
   // FDE 32 information.
   this->memory_.SetData32(0x5100, 0xfc);
@@ -81,415 +62,70 @@
   this->memory_.SetData32(0x5108, 0x1500);
   this->memory_.SetData32(0x510c, 0x200);
 
-  this->memory_.SetData32(0x5200, 0xfc);
-  this->memory_.SetData32(0x5204, 0x204);
-  this->memory_.SetData32(0x5208, 0x2500);
-  this->memory_.SetData32(0x520c, 0x300);
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x5100);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x5110U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x5200U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x6608U, fde->pc_start);
+  EXPECT_EQ(0x6808U, fde->pc_end);
+  EXPECT_EQ(0U, fde->lsda_address);
 
-  // CIE 32 information.
-  this->memory_.SetData32(0x5300, 0xfc);
-  this->memory_.SetData32(0x5304, 0);
-  this->memory_.SetData8(0x5308, 1);
-  this->memory_.SetData8(0x5309, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5400, 0xfc);
-  this->memory_.SetData32(0x5404, 0x104);
-  this->memory_.SetData32(0x5408, 0x3500);
-  this->memory_.SetData32(0x540c, 0x400);
-
-  this->memory_.SetData32(0x5500, 0xfc);
-  this->memory_.SetData32(0x5504, 0x204);
-  this->memory_.SetData32(0x5508, 0x4500);
-  this->memory_.SetData32(0x550c, 0x500);
-
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600, 0));
-  ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
-
-  this->eh_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x6608U, info.start);
-  EXPECT_EQ(0x6808U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(1, &info);
-  EXPECT_EQ(0x5200U, info.offset);
-  EXPECT_EQ(0x7708U, info.start);
-  EXPECT_EQ(0x7a08U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(2, &info);
-  EXPECT_EQ(0x5400U, info.offset);
-  EXPECT_EQ(0x8908U, info.start);
-  EXPECT_EQ(0x8d08U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(3, &info);
-  EXPECT_EQ(0x5500U, info.offset);
-  EXPECT_EQ(0x9a08U, info.start);
-  EXPECT_EQ(0x9f08U, info.end);
+  const DwarfCie* cie = fde->cie;
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x500dU, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5100U, cie->cfa_instructions_end);
+  EXPECT_EQ(16U, cie->code_alignment_factor);
+  EXPECT_EQ(32U, cie->data_alignment_factor);
+  EXPECT_EQ(1U, cie->return_address_register);
 }
 
-TYPED_TEST_P(DwarfEhFrameTest, Init32_fde_not_following_cie) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0);
-  this->memory_.SetData8(0x5008, 1);
-  this->memory_.SetData8(0x5009, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0x1000);
-  this->memory_.SetData32(0x5108, 0x1500);
-  this->memory_.SetData32(0x510c, 0x200);
-
-  ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600, 0));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, Init64) {
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeCieFromOffset64) {
   // CIE 64 information.
   this->memory_.SetData32(0x5000, 0xffffffff);
-  this->memory_.SetData64(0x5004, 0xf4);
+  this->memory_.SetData64(0x5004, 0xfc);
+  // Indicates this is a cie for eh_frame.
   this->memory_.SetData64(0x500c, 0);
-  this->memory_.SetData8(0x5014, 1);
-  this->memory_.SetData8(0x5015, '\0');
+  this->memory_.SetMemory(0x5014, std::vector<uint8_t>{1, '\0', 16, 32, 1});
 
   // FDE 64 information.
   this->memory_.SetData32(0x5100, 0xffffffff);
-  this->memory_.SetData64(0x5104, 0xf4);
+  this->memory_.SetData64(0x5104, 0xfc);
   this->memory_.SetData64(0x510c, 0x10c);
   this->memory_.SetData64(0x5114, 0x1500);
   this->memory_.SetData64(0x511c, 0x200);
 
-  this->memory_.SetData32(0x5200, 0xffffffff);
-  this->memory_.SetData64(0x5204, 0xf4);
-  this->memory_.SetData64(0x520c, 0x20c);
-  this->memory_.SetData64(0x5214, 0x2500);
-  this->memory_.SetData64(0x521c, 0x300);
-
-  // CIE 64 information.
-  this->memory_.SetData32(0x5300, 0xffffffff);
-  this->memory_.SetData64(0x5304, 0xf4);
-  this->memory_.SetData64(0x530c, 0);
-  this->memory_.SetData8(0x5314, 1);
-  this->memory_.SetData8(0x5315, '\0');
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x5400, 0xffffffff);
-  this->memory_.SetData64(0x5404, 0xf4);
-  this->memory_.SetData64(0x540c, 0x10c);
-  this->memory_.SetData64(0x5414, 0x3500);
-  this->memory_.SetData64(0x541c, 0x400);
-
-  this->memory_.SetData32(0x5500, 0xffffffff);
-  this->memory_.SetData64(0x5504, 0xf4);
-  this->memory_.SetData64(0x550c, 0x20c);
-  this->memory_.SetData64(0x5514, 0x4500);
-  this->memory_.SetData64(0x551c, 0x500);
-
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600, 0));
-  ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
-
-  this->eh_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x6618U, info.start);
-  EXPECT_EQ(0x6818U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(1, &info);
-  EXPECT_EQ(0x5200U, info.offset);
-  EXPECT_EQ(0x7718U, info.start);
-  EXPECT_EQ(0x7a18U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(2, &info);
-  EXPECT_EQ(0x5400U, info.offset);
-  EXPECT_EQ(0x8918U, info.start);
-  EXPECT_EQ(0x8d18U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(3, &info);
-  EXPECT_EQ(0x5500U, info.offset);
-  EXPECT_EQ(0x9a18U, info.start);
-  EXPECT_EQ(0x9f18U, info.end);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, Init64_fde_not_following_cie) {
-  // CIE 64 information.
-  this->memory_.SetData32(0x5000, 0xffffffff);
-  this->memory_.SetData64(0x5004, 0xf4);
-  this->memory_.SetData64(0x500c, 0);
-  this->memory_.SetData8(0x5014, 1);
-  this->memory_.SetData8(0x5015, '\0');
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x5100, 0xffffffff);
-  this->memory_.SetData64(0x5104, 0xf4);
-  this->memory_.SetData64(0x510c, 0x1000);
-  this->memory_.SetData64(0x5114, 0x1500);
-  this->memory_.SetData64(0x511c, 0x200);
-
-  ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600, 0));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, Init_non_zero_load_bias) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0);
-  this->memory_.SetData8(0x5008, 1);
-  this->memory_.SetData8(0x5009, 'z');
-  this->memory_.SetData8(0x500a, 'R');
-  this->memory_.SetData8(0x500b, '\0');
-  this->memory_.SetData8(0x500c, 0);
-  this->memory_.SetData8(0x500d, 0);
-  this->memory_.SetData8(0x500e, 0);
-  this->memory_.SetData8(0x500f, 0);
-  this->memory_.SetData8(0x5010, 0x1b);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0x104);
-  this->memory_.SetData32(0x5108, 0x1500);
-  this->memory_.SetData32(0x510c, 0x200);
-  this->memory_.SetData8(0x5110, 0);
-  this->memory_.SetData8(0x5111, 0);
-
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200, 0x2000));
-  ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
-
-  this->eh_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x8608U, info.start);
-  EXPECT_EQ(0x8808U, info.end);
-
-  const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x8700);
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x5100);
   ASSERT_TRUE(fde != nullptr);
-  EXPECT_EQ(0x8608U, fde->pc_start);
-  EXPECT_EQ(0x8808U, fde->pc_end);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, Init_version1) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0);
-  this->memory_.SetData8(0x5008, 1);
-  // Augment string.
-  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'R', 'P', 'L', '\0'});
-  // Code alignment factor.
-  this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x80, 0x00});
-  // Data alignment factor.
-  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
-  // Return address register
-  this->memory_.SetData8(0x5014, 0x84);
-  // Augmentation length
-  this->memory_.SetMemory(0x5015, std::vector<uint8_t>{0x84, 0x00});
-  // R data.
-  this->memory_.SetData8(0x5017, DW_EH_PE_pcrel | DW_EH_PE_udata2);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0x104);
-  this->memory_.SetData16(0x5108, 0x1500);
-  this->memory_.SetData16(0x510a, 0x200);
-
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200, 0));
-  ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  this->eh_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x6606U, info.start);
-  EXPECT_EQ(0x6806U, info.end);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, Init_version4) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0);
-  this->memory_.SetData8(0x5008, 4);
-  // Augment string.
-  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
-  // Address size.
-  this->memory_.SetData8(0x500e, 4);
-  // Segment size.
-  this->memory_.SetData8(0x500f, 0);
-  // Code alignment factor.
-  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x80, 0x00});
-  // Data alignment factor.
-  this->memory_.SetMemory(0x5012, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
-  // Return address register
-  this->memory_.SetMemory(0x5016, std::vector<uint8_t>{0x85, 0x10});
-  // Augmentation length
-  this->memory_.SetMemory(0x5018, std::vector<uint8_t>{0x84, 0x00});
-  // L data.
-  this->memory_.SetData8(0x501a, 0x10);
-  // P data.
-  this->memory_.SetData8(0x501b, DW_EH_PE_udata4);
-  this->memory_.SetData32(0x501c, 0x100);
-  // R data.
-  this->memory_.SetData8(0x5020, DW_EH_PE_pcrel | DW_EH_PE_udata2);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0x104);
-  this->memory_.SetData16(0x5108, 0x1500);
-  this->memory_.SetData16(0x510a, 0x200);
-
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200, 0));
-  ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  this->eh_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x6606U, info.start);
-  EXPECT_EQ(0x6806U, info.end);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc) {
-  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  for (size_t i = 0; i < 9; i++) {
-    info.start = 0x1000 * (i + 1);
-    info.end = 0x1000 * (i + 2) - 0x10;
-    info.offset = 0x5000 + i * 0x20;
-    this->eh_frame_->TestPushFdeInfo(info);
-  }
-
-  this->eh_frame_->TestSetFdeCount(0);
-  uint64_t fde_offset;
-  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
-
-  this->eh_frame_->TestSetFdeCount(9);
-  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
-  // Odd number of elements.
-  for (size_t i = 0; i < 9; i++) {
-    TypeParam pc = 0x1000 * (i + 1);
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
-        << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
-  }
-
-  // Even number of elements.
-  this->eh_frame_->TestSetFdeCount(10);
-  info.start = 0xa000;
-  info.end = 0xaff0;
-  info.offset = 0x5120;
-  this->eh_frame_->TestPushFdeInfo(info);
-
-  for (size_t i = 0; i < 10; i++) {
-    TypeParam pc = 0x1000 * (i + 1);
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
-        << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
-  }
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetCieFde32) {
-  this->eh_frame_->TestSetOffset(0x4000);
-
-  // CIE 32 information.
-  this->memory_.SetData32(0xf000, 0x100);
-  this->memory_.SetData32(0xf004, 0);
-  this->memory_.SetData8(0xf008, 0x1);
-  this->memory_.SetData8(0xf009, '\0');
-  this->memory_.SetData8(0xf00a, 4);
-  this->memory_.SetData8(0xf00b, 8);
-  this->memory_.SetData8(0xf00c, 0x20);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x14000, 0x20);
-  this->memory_.SetData32(0x14004, 0x5004);
-  this->memory_.SetData32(0x14008, 0x9000);
-  this->memory_.SetData32(0x1400c, 0x100);
-
-  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x14000);
-  ASSERT_TRUE(fde != nullptr);
-  EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x1d008U, fde->pc_start);
-  EXPECT_EQ(0x1d108U, fde->pc_end);
-  EXPECT_EQ(0xf000U, fde->cie_offset);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x5124U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x5208U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x6618U, fde->pc_start);
+  EXPECT_EQ(0x6818U, fde->pc_end);
   EXPECT_EQ(0U, fde->lsda_address);
 
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(1U, fde->cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
-  EXPECT_EQ(0U, fde->cie->segment_size);
-  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
-  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
-  EXPECT_EQ(0U, fde->cie->personality_handler);
-  EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset);
-  EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end);
-  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
-  EXPECT_EQ(8, fde->cie->data_alignment_factor);
-  EXPECT_EQ(0x20U, fde->cie->return_address_register);
+  const DwarfCie* cie = fde->cie;
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata8, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x5019U, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5108U, cie->cfa_instructions_end);
+  EXPECT_EQ(16U, cie->code_alignment_factor);
+  EXPECT_EQ(32U, cie->data_alignment_factor);
+  EXPECT_EQ(1U, cie->return_address_register);
 }
 
-TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) {
-  this->eh_frame_->TestSetOffset(0x2000);
-
-  // CIE 64 information.
-  this->memory_.SetData32(0x6000, 0xffffffff);
-  this->memory_.SetData64(0x6004, 0x100);
-  this->memory_.SetData64(0x600c, 0);
-  this->memory_.SetData8(0x6014, 0x1);
-  this->memory_.SetData8(0x6015, '\0');
-  this->memory_.SetData8(0x6016, 4);
-  this->memory_.SetData8(0x6017, 8);
-  this->memory_.SetData8(0x6018, 0x20);
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x8000, 0xffffffff);
-  this->memory_.SetData64(0x8004, 0x200);
-  this->memory_.SetData64(0x800c, 0x200c);
-  this->memory_.SetData64(0x8014, 0x5000);
-  this->memory_.SetData64(0x801c, 0x300);
-
-  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x8000);
-  ASSERT_TRUE(fde != nullptr);
-  EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
-  EXPECT_EQ(0xd018U, fde->pc_start);
-  EXPECT_EQ(0xd318U, fde->pc_end);
-  EXPECT_EQ(0x6000U, fde->cie_offset);
-  EXPECT_EQ(0U, fde->lsda_address);
-
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(1U, fde->cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
-  EXPECT_EQ(0U, fde->cie->segment_size);
-  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
-  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
-  EXPECT_EQ(0U, fde->cie->personality_handler);
-  EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
-  EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
-  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
-  EXPECT_EQ(8, fde->cie->data_alignment_factor);
-  EXPECT_EQ(0x20U, fde->cie->return_address_register);
-}
-
-REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, Init32, Init32_fde_not_following_cie, Init64,
-                           Init64_fde_not_following_cie, Init_non_zero_load_bias, Init_version1,
-                           Init_version4, GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, GetFdeCieFromOffset32, GetFdeCieFromOffset64);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 19c7b98..910ae36 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -30,10 +30,10 @@
 namespace unwindstack {
 
 template <typename TypeParam>
-class MockDwarfEhFrameWithHdr : public DwarfEhFrameWithHdr<TypeParam> {
+class TestDwarfEhFrameWithHdr : public DwarfEhFrameWithHdr<TypeParam> {
  public:
-  MockDwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrameWithHdr<TypeParam>(memory) {}
-  ~MockDwarfEhFrameWithHdr() = default;
+  TestDwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrameWithHdr<TypeParam>(memory) {}
+  ~TestDwarfEhFrameWithHdr() = default;
 
   void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; }
   void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; }
@@ -64,14 +64,14 @@
  protected:
   void SetUp() override {
     memory_.Clear();
-    eh_frame_ = new MockDwarfEhFrameWithHdr<TypeParam>(&memory_);
+    eh_frame_ = new TestDwarfEhFrameWithHdr<TypeParam>(&memory_);
     ResetLogs();
   }
 
   void TearDown() override { delete eh_frame_; }
 
   MemoryFake memory_;
-  MockDwarfEhFrameWithHdr<TypeParam>* eh_frame_ = nullptr;
+  TestDwarfEhFrameWithHdr<TypeParam>* eh_frame_ = nullptr;
 };
 TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest);
 
@@ -121,23 +121,14 @@
   // CIE 32 information.
   this->memory_.SetData32(0x1300, 0xfc);
   this->memory_.SetData32(0x1304, 0);
-  this->memory_.SetData8(0x1308, 1);
-  this->memory_.SetData8(0x1309, 'z');
-  this->memory_.SetData8(0x130a, 'R');
-  this->memory_.SetData8(0x130b, '\0');
-  this->memory_.SetData8(0x130c, 0);
-  this->memory_.SetData8(0x130d, 0);
-  this->memory_.SetData8(0x130e, 0);
-  this->memory_.SetData8(0x130f, 0);
-  this->memory_.SetData8(0x1310, 0x1b);
+  this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, 'z', 'R', '\0', 0, 0, 0, 0, 0x1b});
 
   // FDE 32 information.
   this->memory_.SetData32(0x1400, 0xfc);
   this->memory_.SetData32(0x1404, 0x104);
   this->memory_.SetData32(0x1408, 0x10f8);
   this->memory_.SetData32(0x140c, 0x200);
-  this->memory_.SetData8(0x1410, 0);
-  this->memory_.SetData8(0x1411, 0);
+  this->memory_.SetData16(0x1410, 0);
 
   ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0x2000));
   EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
@@ -157,6 +148,68 @@
   EXPECT_EQ(0x4700U, fde->pc_end);
 }
 
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdes) {
+  this->memory_.SetMemory(
+      0x1000, std::vector<uint8_t>{1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
+  this->memory_.SetData16(0x1004, 0x500);
+  this->memory_.SetData32(0x1006, 4);
+
+  // Header information.
+  this->memory_.SetData32(0x100a, 0x4600);
+  this->memory_.SetData32(0x100e, 0x1500);
+  this->memory_.SetData32(0x1012, 0x5500);
+  this->memory_.SetData32(0x1016, 0x1400);
+  this->memory_.SetData32(0x101a, 0x6800);
+  this->memory_.SetData32(0x101e, 0x1700);
+  this->memory_.SetData32(0x1022, 0x7700);
+  this->memory_.SetData32(0x1026, 0x1600);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0x1300, 0xfc);
+  this->memory_.SetData32(0x1304, 0);
+  this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, '\0', 0, 0, 0});
+
+  // FDE 32 information.
+  // pc 0x5500 - 0x5700
+  this->memory_.SetData32(0x1400, 0xfc);
+  this->memory_.SetData32(0x1404, 0x104);
+  this->memory_.SetData32(0x1408, 0x40f8);
+  this->memory_.SetData32(0x140c, 0x200);
+
+  // pc 0x4600 - 0x4800
+  this->memory_.SetData32(0x1500, 0xfc);
+  this->memory_.SetData32(0x1504, 0x204);
+  this->memory_.SetData32(0x1508, 0x30f8);
+  this->memory_.SetData32(0x150c, 0x200);
+
+  // pc 0x7700 - 0x7900
+  this->memory_.SetData32(0x1600, 0xfc);
+  this->memory_.SetData32(0x1604, 0x304);
+  this->memory_.SetData32(0x1608, 0x60f8);
+  this->memory_.SetData32(0x160c, 0x200);
+
+  // pc 0x6800 - 0x6a00
+  this->memory_.SetData32(0x1700, 0xfc);
+  this->memory_.SetData32(0x1704, 0x404);
+  this->memory_.SetData32(0x1708, 0x50f8);
+  this->memory_.SetData32(0x170c, 0x200);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
+
+  std::vector<const DwarfFde*> fdes;
+  this->eh_frame_->GetFdes(&fdes);
+  ASSERT_EQ(4U, fdes.size());
+
+  EXPECT_EQ(0x4600U, fdes[0]->pc_start);
+  EXPECT_EQ(0x4800U, fdes[0]->pc_end);
+  EXPECT_EQ(0x5500U, fdes[1]->pc_start);
+  EXPECT_EQ(0x5700U, fdes[1]->pc_end);
+  EXPECT_EQ(0x6800U, fdes[2]->pc_start);
+  EXPECT_EQ(0x6a00U, fdes[2]->pc_end);
+  EXPECT_EQ(0x7700U, fdes[3]->pc_start);
+  EXPECT_EQ(0x7900U, fdes[3]->pc_end);
+}
+
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_expect_cache_fail) {
   this->eh_frame_->TestSetTableEntrySize(0x10);
   this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
@@ -388,11 +441,7 @@
   // CIE 32 information.
   this->memory_.SetData32(0xf000, 0x100);
   this->memory_.SetData32(0xf004, 0);
-  this->memory_.SetData8(0xf008, 0x1);
-  this->memory_.SetData8(0xf009, '\0');
-  this->memory_.SetData8(0xf00a, 4);
-  this->memory_.SetData8(0xf00b, 8);
-  this->memory_.SetData8(0xf00c, 0x20);
+  this->memory_.SetMemory(0xf008, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
 
   // FDE 32 information.
   this->memory_.SetData32(0x14000, 0x20);
@@ -429,11 +478,7 @@
   this->memory_.SetData32(0x6000, 0xffffffff);
   this->memory_.SetData64(0x6004, 0x100);
   this->memory_.SetData64(0x600c, 0);
-  this->memory_.SetData8(0x6014, 0x1);
-  this->memory_.SetData8(0x6015, '\0');
-  this->memory_.SetData8(0x6016, 4);
-  this->memory_.SetData8(0x6017, 8);
-  this->memory_.SetData8(0x6018, 0x20);
+  this->memory_.SetMemory(0x6014, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
 
   // FDE 64 information.
   this->memory_.SetData32(0x8000, 0xffffffff);
@@ -478,7 +523,7 @@
   ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias,
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias, GetFdes,
                            GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
                            GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
                            GetFdeOffsetBinary_verify, GetFdeOffsetBinary_index_fail,
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 414f2f2..46f555a 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -16,7 +16,6 @@
 
 #include <stdint.h>
 
-#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include <unwindstack/DwarfError.h>
@@ -31,42 +30,27 @@
 namespace unwindstack {
 
 template <typename TypeParam>
-class MockDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
+class TestDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
  public:
-  MockDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
-  virtual ~MockDwarfSectionImpl() = default;
+  TestDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
+  virtual ~TestDwarfSectionImpl() = default;
 
-  MOCK_METHOD3(Init, bool(uint64_t, uint64_t, uint64_t));
+  bool Init(uint64_t, uint64_t, uint64_t) override { return false; }
 
-  MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
+  void GetFdes(std::vector<const DwarfFde*>*) override {}
 
-  MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
+  const DwarfFde* GetFdeFromPc(uint64_t) override { return nullptr; }
 
-  MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
+  uint64_t GetCieOffsetFromFde32(uint32_t) { return 0; }
 
-  MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
+  uint64_t GetCieOffsetFromFde64(uint64_t) { return 0; }
 
-  MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
-
-  void TestSetCie32Value(uint32_t value32) { this->cie32_value_ = value32; }
-
-  void TestSetCie64Value(uint64_t value64) { this->cie64_value_ = value64; }
-
-  void TestSetCachedCieEntry(uint64_t offset, const DwarfCie& cie) {
-    this->cie_entries_[offset] = cie;
-  }
-  void TestClearCachedCieEntry() { this->cie_entries_.clear(); }
-
-  void TestSetCachedFdeEntry(uint64_t offset, const DwarfFde& fde) {
-    this->fde_entries_[offset] = fde;
-  }
-  void TestClearCachedFdeEntry() { this->fde_entries_.clear(); }
+  uint64_t AdjustPcFromFde(uint64_t) override { return 0; }
 
   void TestSetCachedCieLocRegs(uint64_t offset, const dwarf_loc_regs_t& loc_regs) {
     this->cie_loc_regs_[offset] = loc_regs;
   }
   void TestClearCachedCieLocRegs() { this->cie_loc_regs_.clear(); }
-
   void TestClearError() { this->last_error_.code = DWARF_ERROR_NONE; }
 };
 
@@ -75,21 +59,41 @@
  protected:
   void SetUp() override {
     memory_.Clear();
-    section_ = new MockDwarfSectionImpl<TypeParam>(&memory_);
+    section_ = new TestDwarfSectionImpl<TypeParam>(&memory_);
     ResetLogs();
-    section_->TestSetCie32Value(static_cast<uint32_t>(-1));
-    section_->TestSetCie64Value(static_cast<uint64_t>(-1));
   }
 
   void TearDown() override { delete section_; }
 
   MemoryFake memory_;
-  MockDwarfSectionImpl<TypeParam>* section_ = nullptr;
+  TestDwarfSectionImpl<TypeParam>* section_ = nullptr;
 };
 TYPED_TEST_CASE_P(DwarfSectionImplTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
+TYPED_TEST_P(DwarfSectionImplTest, GetCieFromOffset_fail_should_not_cache) {
+  ASSERT_TRUE(this->section_->GetCieFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetCieFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_fail_should_not_cache) {
+  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+}
+
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
   RegsImplFake<TypeParam> regs(10);
@@ -487,334 +491,6 @@
   EXPECT_EQ(0x80000000U, regs.pc());
 }
 
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_fail_should_not_cache) {
-  ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
-  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
-  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_32_version_check) {
-  this->memory_.SetData32(0x5000, 0x100);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 0x1);
-  this->memory_.SetData8(0x5009, '\0');
-  this->memory_.SetData8(0x500a, 4);
-  this->memory_.SetData8(0x500b, 8);
-  this->memory_.SetData8(0x500c, 0x20);
-
-  const DwarfCie* cie = this->section_->GetCie(0x5000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(1U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
-  EXPECT_EQ(0U, cie->segment_size);
-  EXPECT_EQ(1U, cie->augmentation_string.size());
-  EXPECT_EQ('\0', cie->augmentation_string[0]);
-  EXPECT_EQ(0U, cie->personality_handler);
-  EXPECT_EQ(0x500dU, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(8, cie->data_alignment_factor);
-  EXPECT_EQ(0x20U, cie->return_address_register);
-  EXPECT_EQ(DWARF_ERROR_NONE, this->section_->LastErrorCode());
-
-  this->section_->TestClearCachedCieEntry();
-  // Set version to 0, 2, 5 and verify we fail.
-  this->memory_.SetData8(0x5008, 0x0);
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->LastErrorCode());
-
-  this->memory_.SetData8(0x5008, 0x2);
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->LastErrorCode());
-
-  this->memory_.SetData8(0x5008, 0x5);
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->LastErrorCode());
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_negative_data_alignment_factor) {
-  this->memory_.SetData32(0x5000, 0x100);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 0x1);
-  this->memory_.SetData8(0x5009, '\0');
-  this->memory_.SetData8(0x500a, 4);
-  this->memory_.SetMemory(0x500b, std::vector<uint8_t>{0xfc, 0xff, 0xff, 0xff, 0x7f});
-  this->memory_.SetData8(0x5010, 0x20);
-
-  const DwarfCie* cie = this->section_->GetCie(0x5000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(1U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
-  EXPECT_EQ(0U, cie->segment_size);
-  EXPECT_EQ(1U, cie->augmentation_string.size());
-  EXPECT_EQ('\0', cie->augmentation_string[0]);
-  EXPECT_EQ(0U, cie->personality_handler);
-  EXPECT_EQ(0x5011U, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(-4, cie->data_alignment_factor);
-  EXPECT_EQ(0x20U, cie->return_address_register);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_64_no_augment) {
-  this->memory_.SetData32(0x8000, 0xffffffff);
-  this->memory_.SetData64(0x8004, 0x200);
-  this->memory_.SetData64(0x800c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x8014, 0x1);
-  this->memory_.SetData8(0x8015, '\0');
-  this->memory_.SetData8(0x8016, 4);
-  this->memory_.SetData8(0x8017, 8);
-  this->memory_.SetData8(0x8018, 0x20);
-
-  const DwarfCie* cie = this->section_->GetCie(0x8000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(1U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata8, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
-  EXPECT_EQ(0U, cie->segment_size);
-  EXPECT_EQ(1U, cie->augmentation_string.size());
-  EXPECT_EQ('\0', cie->augmentation_string[0]);
-  EXPECT_EQ(0U, cie->personality_handler);
-  EXPECT_EQ(0x8019U, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x820cU, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(8, cie->data_alignment_factor);
-  EXPECT_EQ(0x20U, cie->return_address_register);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_augment) {
-  this->memory_.SetData32(0x5000, 0x100);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 0x1);
-  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
-  this->memory_.SetData8(0x500e, 4);
-  this->memory_.SetData8(0x500f, 8);
-  this->memory_.SetData8(0x5010, 0x10);
-  // Augment length.
-  this->memory_.SetData8(0x5011, 0xf);
-  // L data.
-  this->memory_.SetData8(0x5012, DW_EH_PE_textrel | DW_EH_PE_udata2);
-  // P data.
-  this->memory_.SetData8(0x5013, DW_EH_PE_udata4);
-  this->memory_.SetData32(0x5014, 0x12345678);
-  // R data.
-  this->memory_.SetData8(0x5018, DW_EH_PE_udata2);
-
-  const DwarfCie* cie = this->section_->GetCie(0x5000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(1U, cie->version);
-  EXPECT_EQ(DW_EH_PE_udata2, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_textrel | DW_EH_PE_udata2, cie->lsda_encoding);
-  EXPECT_EQ(0U, cie->segment_size);
-  EXPECT_EQ(5U, cie->augmentation_string.size());
-  EXPECT_EQ('z', cie->augmentation_string[0]);
-  EXPECT_EQ('L', cie->augmentation_string[1]);
-  EXPECT_EQ('P', cie->augmentation_string[2]);
-  EXPECT_EQ('R', cie->augmentation_string[3]);
-  EXPECT_EQ('\0', cie->augmentation_string[4]);
-  EXPECT_EQ(0x12345678U, cie->personality_handler);
-  EXPECT_EQ(0x5021U, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(8, cie->data_alignment_factor);
-  EXPECT_EQ(0x10U, cie->return_address_register);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_3) {
-  this->memory_.SetData32(0x5000, 0x100);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 0x3);
-  this->memory_.SetData8(0x5009, '\0');
-  this->memory_.SetData8(0x500a, 4);
-  this->memory_.SetData8(0x500b, 8);
-  this->memory_.SetMemory(0x500c, std::vector<uint8_t>{0x81, 0x03});
-
-  const DwarfCie* cie = this->section_->GetCie(0x5000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(3U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
-  EXPECT_EQ(0U, cie->segment_size);
-  EXPECT_EQ(1U, cie->augmentation_string.size());
-  EXPECT_EQ('\0', cie->augmentation_string[0]);
-  EXPECT_EQ(0U, cie->personality_handler);
-  EXPECT_EQ(0x500eU, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(8, cie->data_alignment_factor);
-  EXPECT_EQ(0x181U, cie->return_address_register);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_4) {
-  this->memory_.SetData32(0x5000, 0x100);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 0x4);
-  this->memory_.SetData8(0x5009, '\0');
-  this->memory_.SetData8(0x500a, 4);
-  this->memory_.SetData8(0x500b, 0x13);
-  this->memory_.SetData8(0x500c, 4);
-  this->memory_.SetData8(0x500d, 8);
-  this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x81, 0x03});
-
-  const DwarfCie* cie = this->section_->GetCie(0x5000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(4U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
-  EXPECT_EQ(0x13U, cie->segment_size);
-  EXPECT_EQ(1U, cie->augmentation_string.size());
-  EXPECT_EQ('\0', cie->augmentation_string[0]);
-  EXPECT_EQ(0U, cie->personality_handler);
-  EXPECT_EQ(0x5010U, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(8, cie->data_alignment_factor);
-  EXPECT_EQ(0x181U, cie->return_address_register);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_fail_should_not_cache) {
-  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
-  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
-  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment) {
-  this->memory_.SetData32(0x4000, 0x20);
-  this->memory_.SetData32(0x4004, 0x8000);
-  this->memory_.SetData32(0x4008, 0x5000);
-  this->memory_.SetData32(0x400c, 0x100);
-
-  EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
-  DwarfCie cie{};
-  cie.fde_address_encoding = DW_EH_PE_udata4;
-  this->section_->TestSetCachedCieEntry(0x8000, cie);
-  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
-
-  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
-  ASSERT_TRUE(fde != nullptr);
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(0x4010U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x4024U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x5000U, fde->pc_start);
-  EXPECT_EQ(0x5100U, fde->pc_end);
-  EXPECT_EQ(0x8000U, fde->cie_offset);
-  EXPECT_EQ(0U, fde->lsda_address);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment_non_zero_segment_size) {
-  this->memory_.SetData32(0x4000, 0x30);
-  this->memory_.SetData32(0x4004, 0x8000);
-  this->memory_.SetData32(0x4018, 0x5000);
-  this->memory_.SetData32(0x401c, 0x100);
-
-  EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
-  DwarfCie cie{};
-  cie.fde_address_encoding = DW_EH_PE_udata4;
-  cie.segment_size = 0x10;
-  this->section_->TestSetCachedCieEntry(0x8000, cie);
-  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
-
-  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
-  ASSERT_TRUE(fde != nullptr);
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(0x4020U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x4034U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x5000U, fde->pc_start);
-  EXPECT_EQ(0x5100U, fde->pc_end);
-  EXPECT_EQ(0x8000U, fde->cie_offset);
-  EXPECT_EQ(0U, fde->lsda_address);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_augment) {
-  this->memory_.SetData32(0x4000, 0x100);
-  this->memory_.SetData32(0x4004, 0x8000);
-  this->memory_.SetData32(0x4008, 0x5000);
-  this->memory_.SetData32(0x400c, 0x100);
-  this->memory_.SetMemory(0x4010, std::vector<uint8_t>{0x82, 0x01});
-  this->memory_.SetData16(0x4012, 0x1234);
-
-  EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
-  DwarfCie cie{};
-  cie.fde_address_encoding = DW_EH_PE_udata4;
-  cie.augmentation_string.push_back('z');
-  cie.lsda_encoding = DW_EH_PE_udata2;
-  this->section_->TestSetCachedCieEntry(0x8000, cie);
-  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
-
-  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
-  ASSERT_TRUE(fde != nullptr);
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(0x4094U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x4104U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x5000U, fde->pc_start);
-  EXPECT_EQ(0x5100U, fde->pc_end);
-  EXPECT_EQ(0x8000U, fde->cie_offset);
-  EXPECT_EQ(0x1234U, fde->lsda_address);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_64_no_augment) {
-  this->memory_.SetData32(0x4000, 0xffffffff);
-  this->memory_.SetData64(0x4004, 0x100);
-  this->memory_.SetData64(0x400c, 0x12345678);
-  this->memory_.SetData32(0x4014, 0x5000);
-  this->memory_.SetData32(0x4018, 0x100);
-
-  EXPECT_CALL(*this->section_, GetCieOffsetFromFde64(0x12345678))
-      .WillOnce(::testing::Return(0x12345678));
-  DwarfCie cie{};
-  cie.fde_address_encoding = DW_EH_PE_udata4;
-  this->section_->TestSetCachedCieEntry(0x12345678, cie);
-  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
-
-  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
-  ASSERT_TRUE(fde != nullptr);
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(0x401cU, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x410cU, fde->cfa_instructions_end);
-  EXPECT_EQ(0x5000U, fde->pc_start);
-  EXPECT_EQ(0x5100U, fde->pc_end);
-  EXPECT_EQ(0x12345678U, fde->cie_offset);
-  EXPECT_EQ(0U, fde->lsda_address);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_cached) {
-  DwarfCie cie{};
-  cie.fde_address_encoding = DW_EH_PE_udata4;
-  cie.augmentation_string.push_back('z');
-  cie.lsda_encoding = DW_EH_PE_udata2;
-
-  DwarfFde fde_cached{};
-  fde_cached.cfa_instructions_offset = 0x1000;
-  fde_cached.cfa_instructions_end = 0x1100;
-  fde_cached.pc_start = 0x9000;
-  fde_cached.pc_end = 0x9400;
-  fde_cached.cie_offset = 0x30000;
-  fde_cached.cie = &cie;
-  this->section_->TestSetCachedFdeEntry(0x6000, fde_cached);
-
-  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x6000);
-  ASSERT_TRUE(fde != nullptr);
-  ASSERT_EQ(&cie, fde->cie);
-  EXPECT_EQ(0x1000U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x1100U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x9000U, fde->pc_start);
-  EXPECT_EQ(0x9400U, fde->pc_end);
-  EXPECT_EQ(0x30000U, fde->cie_offset);
-}
-
 TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_not_cached) {
   DwarfCie cie{};
   cie.cfa_instructions_offset = 0x3000;
@@ -895,18 +571,16 @@
   ASSERT_EQ("", GetFakeLogBuf());
 }
 
-REGISTER_TYPED_TEST_CASE_P(
-    DwarfSectionImplTest, Eval_cfa_expr_eval_fail, Eval_cfa_expr_no_stack,
-    Eval_cfa_expr_is_register, Eval_cfa_expr, Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa,
-    Eval_cfa_bad, Eval_cfa_register_prev, Eval_cfa_register_from_value, Eval_double_indirection,
-    Eval_register_reference_chain, Eval_dex_pc, Eval_invalid_register, Eval_different_reg_locations,
-    Eval_return_address_undefined, Eval_pc_zero, Eval_return_address, Eval_ignore_large_reg_loc,
-    Eval_reg_expr, Eval_reg_val_expr, GetCie_fail_should_not_cache, GetCie_32_version_check,
-    GetCie_negative_data_alignment_factor, GetCie_64_no_augment, GetCie_augment, GetCie_version_3,
-    GetCie_version_4, GetFdeFromOffset_fail_should_not_cache, GetFdeFromOffset_32_no_augment,
-    GetFdeFromOffset_32_no_augment_non_zero_segment_size, GetFdeFromOffset_32_augment,
-    GetFdeFromOffset_64_no_augment, GetFdeFromOffset_cached, GetCfaLocationInfo_cie_not_cached,
-    GetCfaLocationInfo_cie_cached, Log);
+REGISTER_TYPED_TEST_CASE_P(DwarfSectionImplTest, GetCieFromOffset_fail_should_not_cache,
+                           GetFdeFromOffset_fail_should_not_cache, Eval_cfa_expr_eval_fail,
+                           Eval_cfa_expr_no_stack, Eval_cfa_expr_is_register, Eval_cfa_expr,
+                           Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa, Eval_cfa_bad,
+                           Eval_cfa_register_prev, Eval_cfa_register_from_value,
+                           Eval_double_indirection, Eval_register_reference_chain, Eval_dex_pc,
+                           Eval_invalid_register, Eval_different_reg_locations,
+                           Eval_return_address_undefined, Eval_pc_zero, Eval_return_address,
+                           Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+                           GetCfaLocationInfo_cie_not_cached, GetCfaLocationInfo_cie_cached, Log);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index 2c6c879..d754fcc 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -30,24 +30,18 @@
   MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
   virtual ~MockDwarfSection() = default;
 
-  MOCK_METHOD3(Log, bool(uint8_t, uint64_t, const DwarfFde*));
+  MOCK_METHOD3(Init, bool(uint64_t, uint64_t, uint64_t));
 
   MOCK_METHOD5(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*));
 
+  MOCK_METHOD3(Log, bool(uint8_t, uint64_t, const DwarfFde*));
+
+  MOCK_METHOD1(GetFdes, void(std::vector<const DwarfFde*>*));
+
+  MOCK_METHOD1(GetFdeFromPc, const DwarfFde*(uint64_t));
+
   MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
 
-  MOCK_METHOD3(Init, bool(uint64_t, uint64_t, uint64_t));
-
-  MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
-
-  MOCK_METHOD1(GetFdeFromOffset, const DwarfFde*(uint64_t));
-
-  MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
-
-  MOCK_METHOD1(IsCie32, bool(uint32_t));
-
-  MOCK_METHOD1(IsCie64, bool(uint64_t));
-
   MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
 
   MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
@@ -57,112 +51,60 @@
 
 class DwarfSectionTest : public ::testing::Test {
  protected:
+  void SetUp() override { section_.reset(new MockDwarfSection(&memory_)); }
+
   MemoryFake memory_;
+  std::unique_ptr<MockDwarfSection> section_;
 };
 
-TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_from_pc) {
-  MockDwarfSection mock_section(&memory_);
-
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(false));
-
-  // Verify nullptr when GetFdeOffsetFromPc fails.
-  ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
-}
-
-TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_fde_pc_end) {
-  MockDwarfSection mock_section(&memory_);
-
-  DwarfFde fde{};
-  fde.pc_end = 0x500;
-
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
-
-  // Verify nullptr when GetFdeOffsetFromPc fails.
-  ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
-}
-
-TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_pass) {
-  MockDwarfSection mock_section(&memory_);
-
-  DwarfFde fde{};
-  fde.pc_end = 0x2000;
-
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
-
-  // Verify nullptr when GetFdeOffsetFromPc fails.
-  ASSERT_EQ(&fde, mock_section.GetFdeFromPc(0x1000));
-}
-
 TEST_F(DwarfSectionTest, Step_fail_fde) {
-  MockDwarfSection mock_section(&memory_);
-
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(false));
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(nullptr));
 
   bool finished;
-  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &finished));
+  ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
 }
 
 TEST_F(DwarfSectionTest, Step_fail_cie_null) {
-  MockDwarfSection mock_section(&memory_);
-
   DwarfFde fde{};
   fde.pc_end = 0x2000;
   fde.cie = nullptr;
 
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
 
   bool finished;
-  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &finished));
+  ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
 }
 
 TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
-  MockDwarfSection mock_section(&memory_);
-
   DwarfCie cie{};
   DwarfFde fde{};
   fde.pc_end = 0x2000;
   fde.cie = &cie;
 
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
-
-  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
       .WillOnce(::testing::Return(false));
 
   bool finished;
-  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &finished));
+  ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
 }
 
 TEST_F(DwarfSectionTest, Step_pass) {
-  MockDwarfSection mock_section(&memory_);
-
   DwarfCie cie{};
   DwarfFde fde{};
   fde.pc_end = 0x2000;
   fde.cie = &cie;
 
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
-
-  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
       .WillOnce(::testing::Return(true));
 
   MemoryFake process;
-  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+  EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
       .WillOnce(::testing::Return(true));
 
   bool finished;
-  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
 }
 
 static bool MockGetCfaLocationInfo(::testing::Unused, const DwarfFde* fde,
@@ -173,64 +115,53 @@
 }
 
 TEST_F(DwarfSectionTest, Step_cache) {
-  MockDwarfSection mock_section(&memory_);
-
   DwarfCie cie{};
   DwarfFde fde{};
   fde.pc_start = 0x500;
   fde.pc_end = 0x2000;
   fde.cie = &cie;
 
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
-
-  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
       .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
 
   MemoryFake process;
-  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+  EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
       .WillRepeatedly(::testing::Return(true));
 
   bool finished;
-  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
-  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
-  ASSERT_TRUE(mock_section.Step(0x1500, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x1500, nullptr, &process, &finished));
 }
 
 TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
-  MockDwarfSection mock_section(&memory_);
-
   DwarfCie cie{};
   DwarfFde fde0{};
   fde0.pc_start = 0x1000;
   fde0.pc_end = 0x2000;
   fde0.cie = &cie;
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde0));
-  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde0, ::testing::_))
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde0));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde0, ::testing::_))
       .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
 
   MemoryFake process;
-  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+  EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
       .WillRepeatedly(::testing::Return(true));
 
   bool finished;
-  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
 
   DwarfFde fde1{};
   fde1.pc_start = 0x500;
   fde1.pc_end = 0x800;
   fde1.cie = &cie;
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x600, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde1));
-  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x600, &fde1, ::testing::_))
+  EXPECT_CALL(*section_, GetFdeFromPc(0x600)).WillOnce(::testing::Return(&fde1));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x600, &fde1, ::testing::_))
       .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
 
-  ASSERT_TRUE(mock_section.Step(0x600, nullptr, &process, &finished));
-  ASSERT_TRUE(mock_section.Step(0x700, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x600, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x700, nullptr, &process, &finished));
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index a8bb4aa..43c6a97 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -242,44 +242,21 @@
   ASSERT_EQ(0xa020U, entries[4]);
 }
 
-TEST_F(ElfInterfaceArmTest, HandleType_not_arm_exidx) {
+TEST_F(ElfInterfaceArmTest, HandleUnknownType_arm_exidx) {
   ElfInterfaceArmFake interface(&memory_);
 
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_NULL));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOAD));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_DYNAMIC));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_INTERP));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_NOTE));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_SHLIB));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_PHDR));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_TLS));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOOS));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_HIOS));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOPROC));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_HIPROC));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_EH_FRAME));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_STACK));
-}
-
-TEST_F(ElfInterfaceArmTest, HandleType_arm_exidx) {
-  ElfInterfaceArmFake interface(&memory_);
-
-  Elf32_Phdr phdr = {};
   interface.FakeSetStartOffset(0x1000);
   interface.FakeSetTotalEntries(100);
-  phdr.p_offset = 0x2000;
-  phdr.p_filesz = 0xa00;
 
-  // Verify that if reads fail, we don't set the values but still get true.
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
+  // Verify that if the type is not the one we want, we don't set the values.
+  interface.HandleUnknownType(0x70000000, 0x2000, 320);
   ASSERT_EQ(0x1000U, interface.start_offset());
   ASSERT_EQ(100U, interface.total_entries());
 
   // Everything is correct and present.
-  memory_.SetMemory(0x1000, &phdr, sizeof(phdr));
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
+  interface.HandleUnknownType(0x70000001, 0x2000, 320);
   ASSERT_EQ(0x2000U, interface.start_offset());
-  ASSERT_EQ(320U, interface.total_entries());
+  ASSERT_EQ(40U, interface.total_entries());
 }
 
 TEST_F(ElfInterfaceArmTest, StepExidx) {
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index 487d39c..aa6df84 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -656,8 +656,7 @@
 
   memory_.SetData32(0x5000, 0xfc);
   memory_.SetData32(0x5004, 0xffffffff);
-  memory_.SetData8(0x5008, 1);
-  memory_.SetData8(0x5009, '\0');
+  memory_.SetMemory(0x5008, std::vector<uint8_t>{1, '\0', 4, 8, 2});
 
   memory_.SetData32(0x5100, 0xfc);
   memory_.SetData32(0x5104, 0);
@@ -678,56 +677,6 @@
   InitHeadersDebugFrame<ElfInterface64Fake>();
 }
 
-template <typename ElfType>
-void ElfInterfaceTest::InitHeadersEhFrameFail() {
-  ElfType elf(&memory_);
-
-  elf.FakeSetEhFrameOffset(0x1000);
-  elf.FakeSetEhFrameSize(0x100);
-  elf.FakeSetDebugFrameOffset(0);
-  elf.FakeSetDebugFrameSize(0);
-
-  elf.InitHeaders(0);
-
-  EXPECT_TRUE(elf.eh_frame() == nullptr);
-  EXPECT_EQ(0U, elf.eh_frame_offset());
-  EXPECT_EQ(static_cast<uint64_t>(-1), elf.eh_frame_size());
-  EXPECT_TRUE(elf.debug_frame() == nullptr);
-}
-
-TEST_F(ElfInterfaceTest, init_headers_eh_frame32_fail) {
-  InitHeadersEhFrameFail<ElfInterface32Fake>();
-}
-
-TEST_F(ElfInterfaceTest, init_headers_eh_frame64_fail) {
-  InitHeadersEhFrameFail<ElfInterface64Fake>();
-}
-
-template <typename ElfType>
-void ElfInterfaceTest::InitHeadersDebugFrameFail() {
-  ElfType elf(&memory_);
-
-  elf.FakeSetEhFrameOffset(0);
-  elf.FakeSetEhFrameSize(0);
-  elf.FakeSetDebugFrameOffset(0x1000);
-  elf.FakeSetDebugFrameSize(0x100);
-
-  elf.InitHeaders(0);
-
-  EXPECT_TRUE(elf.eh_frame() == nullptr);
-  EXPECT_TRUE(elf.debug_frame() == nullptr);
-  EXPECT_EQ(0U, elf.debug_frame_offset());
-  EXPECT_EQ(static_cast<uint64_t>(-1), elf.debug_frame_size());
-}
-
-TEST_F(ElfInterfaceTest, init_headers_debug_frame32_fail) {
-  InitHeadersDebugFrameFail<ElfInterface32Fake>();
-}
-
-TEST_F(ElfInterfaceTest, init_headers_debug_frame64_fail) {
-  InitHeadersDebugFrameFail<ElfInterface64Fake>();
-}
-
 template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
 void ElfInterfaceTest::InitSectionHeadersMalformed() {
   std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
@@ -1024,11 +973,7 @@
   // CIE 32.
   memory_.SetData32(0x600, 0xfc);
   memory_.SetData32(0x604, 0xffffffff);
-  memory_.SetData8(0x608, 1);
-  memory_.SetData8(0x609, '\0');
-  memory_.SetData8(0x60a, 0x4);
-  memory_.SetData8(0x60b, 0x4);
-  memory_.SetData8(0x60c, 0x1);
+  memory_.SetMemory(0x608, std::vector<uint8_t>{1, '\0', 4, 4, 1});
 
   // FDE 32.
   memory_.SetData32(0x700, 0xfc);
@@ -1085,11 +1030,7 @@
   // CIE 32.
   memory_.SetData32(0x600, 0xfc);
   memory_.SetData32(0x604, 0);
-  memory_.SetData8(0x608, 1);
-  memory_.SetData8(0x609, '\0');
-  memory_.SetData8(0x60a, 0x4);
-  memory_.SetData8(0x60b, 0x4);
-  memory_.SetData8(0x60c, 0x1);
+  memory_.SetMemory(0x608, std::vector<uint8_t>{1, '\0', 4, 4, 1});
 
   // FDE 32.
   memory_.SetData32(0x700, 0xfc);
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
index 4a9ed9f..3655984 100644
--- a/libunwindstack/tests/MemoryTest.cpp
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -51,40 +51,6 @@
   uint64_t four;
 };
 
-TEST(MemoryTest, read_field) {
-  MemoryFakeAlwaysReadZero memory;
-
-  FakeStruct data;
-  memset(&data, 0xff, sizeof(data));
-  ASSERT_TRUE(memory.ReadField(0, &data, &data.one, sizeof(data.one)));
-  ASSERT_EQ(0, data.one);
-
-  memset(&data, 0xff, sizeof(data));
-  ASSERT_TRUE(memory.ReadField(0, &data, &data.two, sizeof(data.two)));
-  ASSERT_FALSE(data.two);
-
-  memset(&data, 0xff, sizeof(data));
-  ASSERT_TRUE(memory.ReadField(0, &data, &data.three, sizeof(data.three)));
-  ASSERT_EQ(0U, data.three);
-
-  memset(&data, 0xff, sizeof(data));
-  ASSERT_TRUE(memory.ReadField(0, &data, &data.four, sizeof(data.four)));
-  ASSERT_EQ(0U, data.four);
-}
-
-TEST(MemoryTest, read_field_fails) {
-  MemoryFakeAlwaysReadZero memory;
-
-  FakeStruct data;
-  memset(&data, 0xff, sizeof(data));
-
-  ASSERT_FALSE(memory.ReadField(UINT64_MAX, &data, &data.three, sizeof(data.three)));
-
-  // Field and start reversed, should fail.
-  ASSERT_FALSE(memory.ReadField(100, &data.two, &data, sizeof(data.two)));
-  ASSERT_FALSE(memory.ReadField(0, &data.two, &data, sizeof(data.two)));
-}
-
 TEST(MemoryTest, read_string) {
   std::string name("string_in_memory");
 
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 83695bb..ea992c7 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -32,6 +32,7 @@
 #include <vector>
 
 #include <android-base/stringprintf.h>
+#include <android-base/threads.h>
 
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
@@ -231,8 +232,7 @@
     usleep(1000);
   }
   ASSERT_NE(0, tid.load());
-  // Portable tgkill method.
-  ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
+  ASSERT_EQ(0, tgkill(getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
 
   // Wait for context data.
   void* ucontext;
diff --git a/libutils/Android.bp b/libutils/Android.bp
index bbfa9d8..d635e65 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -155,6 +155,7 @@
             ],
         },
         linux: {
+            shared_libs: ["libbase"],
             srcs: [
                 "Looper.cpp",
             ],
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 3c4d81c..583c6b9 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -48,10 +48,10 @@
 
 // Constructor.  Create an empty object.
 FileMap::FileMap(void)
-    : mFileName(NULL),
-      mBasePtr(NULL),
+    : mFileName(nullptr),
+      mBasePtr(nullptr),
       mBaseLength(0),
-      mDataPtr(NULL),
+      mDataPtr(nullptr),
       mDataLength(0)
 #if defined(__MINGW32__)
       ,
@@ -69,9 +69,9 @@
       , mFileHandle(other.mFileHandle), mFileMapping(other.mFileMapping)
 #endif
 {
-    other.mFileName = NULL;
-    other.mBasePtr = NULL;
-    other.mDataPtr = NULL;
+    other.mFileName = nullptr;
+    other.mBasePtr = nullptr;
+    other.mDataPtr = nullptr;
 #if defined(__MINGW32__)
     other.mFileHandle = INVALID_HANDLE_VALUE;
     other.mFileMapping = NULL;
@@ -86,9 +86,9 @@
     mDataOffset = other.mDataOffset;
     mDataPtr = other.mDataPtr;
     mDataLength = other.mDataLength;
-    other.mFileName = NULL;
-    other.mBasePtr = NULL;
-    other.mDataPtr = NULL;
+    other.mFileName = nullptr;
+    other.mBasePtr = nullptr;
+    other.mDataPtr = nullptr;
 #if defined(__MINGW32__)
     mFileHandle = other.mFileHandle;
     mFileMapping = other.mFileMapping;
@@ -101,7 +101,7 @@
 // Destructor.
 FileMap::~FileMap(void)
 {
-    if (mFileName != NULL) {
+    if (mFileName != nullptr) {
         free(mFileName);
     }
 #if defined(__MINGW32__)
@@ -196,7 +196,7 @@
     if (!readOnly)
         prot |= PROT_WRITE;
 
-    ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset);
+    ptr = mmap(nullptr, adjLength, prot, flags, fd, adjOffset);
     if (ptr == MAP_FAILED) {
         ALOGE("mmap(%lld,%zu) failed: %s\n",
             (long long)adjOffset, adjLength, strerror(errno));
@@ -205,7 +205,7 @@
     mBasePtr = ptr;
 #endif // !defined(__MINGW32__)
 
-    mFileName = origFileName != NULL ? strdup(origFileName) : NULL;
+    mFileName = origFileName != nullptr ? strdup(origFileName) : nullptr;
     mBaseLength = adjLength;
     mDataOffset = offset;
     mDataPtr = (char*) mBasePtr + adjust;
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 6c57b2e..102fdf0 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -29,7 +29,7 @@
 
 void WeakMessageHandler::handleMessage(const Message& message) {
     sp<MessageHandler> handler = mHandler.promote();
-    if (handler != NULL) {
+    if (handler != nullptr) {
         handler->handleMessage(message);
     }
 }
@@ -60,24 +60,22 @@
 static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT;
 static pthread_key_t gTLSKey = 0;
 
-Looper::Looper(bool allowNonCallbacks) :
-        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
-        mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
-        mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
-    mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
-    LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
-                        strerror(errno));
+Looper::Looper(bool allowNonCallbacks)
+    : mAllowNonCallbacks(allowNonCallbacks),
+      mSendingMessage(false),
+      mPolling(false),
+      mEpollRebuildRequired(false),
+      mNextRequestSeq(0),
+      mResponseIndex(0),
+      mNextMessageUptime(LLONG_MAX) {
+    mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
+    LOG_ALWAYS_FATAL_IF(mWakeEventFd.get() < 0, "Could not make wake event fd: %s", strerror(errno));
 
     AutoMutex _l(mLock);
     rebuildEpollLocked();
 }
 
 Looper::~Looper() {
-    close(mWakeEventFd);
-    mWakeEventFd = -1;
-    if (mEpollFd >= 0) {
-        close(mEpollFd);
-    }
 }
 
 void Looper::initTLSKey() {
@@ -87,7 +85,7 @@
 
 void Looper::threadDestructor(void *st) {
     Looper* const self = static_cast<Looper*>(st);
-    if (self != NULL) {
+    if (self != nullptr) {
         self->decStrong((void*)threadDestructor);
     }
 }
@@ -95,13 +93,13 @@
 void Looper::setForThread(const sp<Looper>& looper) {
     sp<Looper> old = getForThread(); // also has side-effect of initializing TLS
 
-    if (looper != NULL) {
+    if (looper != nullptr) {
         looper->incStrong((void*)threadDestructor);
     }
 
     pthread_setspecific(gTLSKey, looper.get());
 
-    if (old != NULL) {
+    if (old != nullptr) {
         old->decStrong((void*)threadDestructor);
     }
 }
@@ -116,7 +114,7 @@
 sp<Looper> Looper::prepare(int opts) {
     bool allowNonCallbacks = opts & PREPARE_ALLOW_NON_CALLBACKS;
     sp<Looper> looper = Looper::getForThread();
-    if (looper == NULL) {
+    if (looper == nullptr) {
         looper = new Looper(allowNonCallbacks);
         Looper::setForThread(looper);
     }
@@ -137,18 +135,18 @@
 #if DEBUG_CALLBACKS
         ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
 #endif
-        close(mEpollFd);
+        mEpollFd.reset();
     }
 
     // Allocate the new epoll instance and register the wake pipe.
-    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
+    mEpollFd.reset(epoll_create(EPOLL_SIZE_HINT));
     LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
 
     struct epoll_event eventItem;
     memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
     eventItem.events = EPOLLIN;
-    eventItem.data.fd = mWakeEventFd;
-    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
+    eventItem.data.fd = mWakeEventFd.get();
+    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
                         strerror(errno));
 
@@ -157,7 +155,7 @@
         struct epoll_event eventItem;
         request.initEventItem(&eventItem);
 
-        int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
+        int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
         if (epollResult < 0) {
             ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
                   request.fd, strerror(errno));
@@ -190,9 +188,9 @@
                         "fd=%d, events=0x%x, data=%p",
                         this, ident, fd, events, data);
 #endif
-                if (outFd != NULL) *outFd = fd;
-                if (outEvents != NULL) *outEvents = events;
-                if (outData != NULL) *outData = data;
+                if (outFd != nullptr) *outFd = fd;
+                if (outEvents != nullptr) *outEvents = events;
+                if (outData != nullptr) *outData = data;
                 return ident;
             }
         }
@@ -201,9 +199,9 @@
 #if DEBUG_POLL_AND_WAKE
             ALOGD("%p ~ pollOnce - returning result %d", this, result);
 #endif
-            if (outFd != NULL) *outFd = 0;
-            if (outEvents != NULL) *outEvents = 0;
-            if (outData != NULL) *outData = NULL;
+            if (outFd != nullptr) *outFd = 0;
+            if (outEvents != nullptr) *outEvents = 0;
+            if (outData != nullptr) *outData = nullptr;
             return result;
         }
 
@@ -239,7 +237,7 @@
     mPolling = true;
 
     struct epoll_event eventItems[EPOLL_MAX_EVENTS];
-    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
+    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
 
     // No longer idling.
     mPolling = false;
@@ -281,7 +279,7 @@
     for (int i = 0; i < eventCount; i++) {
         int fd = eventItems[i].data.fd;
         uint32_t epollEvents = eventItems[i].events;
-        if (fd == mWakeEventFd) {
+        if (fd == mWakeEventFd.get()) {
             if (epollEvents & EPOLLIN) {
                 awoken();
             } else {
@@ -401,11 +399,11 @@
 #endif
 
     uint64_t inc = 1;
-    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
+    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
     if (nWrite != sizeof(uint64_t)) {
         if (errno != EAGAIN) {
-            LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",
-                    mWakeEventFd, strerror(errno));
+            LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s", mWakeEventFd.get(),
+                             strerror(errno));
         }
     }
 }
@@ -416,7 +414,7 @@
 #endif
 
     uint64_t counter;
-    TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
+    TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
 }
 
 void Looper::pushResponse(int events, const Request& request) {
@@ -427,7 +425,7 @@
 }
 
 int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
-    return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data);
+    return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : nullptr, data);
 }
 
 int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
@@ -467,14 +465,14 @@
 
         ssize_t requestIndex = mRequests.indexOfKey(fd);
         if (requestIndex < 0) {
-            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
+            int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);
             if (epollResult < 0) {
                 ALOGE("Error adding epoll events for fd %d: %s", fd, strerror(errno));
                 return -1;
             }
             mRequests.add(fd, request);
         } else {
-            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
+            int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_MOD, fd, &eventItem);
             if (epollResult < 0) {
                 if (errno == ENOENT) {
                     // Tolerate ENOENT because it means that an older file descriptor was
@@ -495,7 +493,7 @@
                             "being recycled, falling back on EPOLL_CTL_ADD: %s",
                             this, strerror(errno));
 #endif
-                    epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
+                    epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);
                     if (epollResult < 0) {
                         ALOGE("Error modifying or adding epoll events for fd %d: %s",
                                 fd, strerror(errno));
@@ -542,7 +540,7 @@
         // updating the epoll set so that we avoid accidentally leaking callbacks.
         mRequests.removeItemsAt(requestIndex);
 
-        int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL);
+        int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_DEL, fd, nullptr);
         if (epollResult < 0) {
             if (seq != -1 && (errno == EBADF || errno == ENOENT)) {
                 // Tolerate EBADF or ENOENT when the sequence number is known because it
diff --git a/libutils/NativeHandle.cpp b/libutils/NativeHandle.cpp
index 97d06b8..d437a9f 100644
--- a/libutils/NativeHandle.cpp
+++ b/libutils/NativeHandle.cpp
@@ -20,7 +20,7 @@
 namespace android {
 
 sp<NativeHandle> NativeHandle::create(native_handle_t* handle, bool ownsHandle) {
-    return handle ? new NativeHandle(handle, ownsHandle) : NULL;
+    return handle ? new NativeHandle(handle, ownsHandle) : nullptr;
 }
 
 NativeHandle::NativeHandle(native_handle_t* handle, bool ownsHandle)
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index cbf042e..c9ae210 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -73,7 +73,7 @@
 }
 
 void LogPrinter::printLine(const char* string) {
-    if (string == NULL) {
+    if (string == nullptr) {
         ALOGW("%s: NULL string passed in", __FUNCTION__);
         return;
     }
@@ -107,7 +107,7 @@
 }
 
 void FdPrinter::printLine(const char* string) {
-    if (string == NULL) {
+    if (string == nullptr) {
         ALOGW("%s: NULL string passed in", __FUNCTION__);
         return;
     } else if (mFd < 0) {
@@ -127,16 +127,16 @@
         mTarget(target),
         mPrefix(prefix ?: "") {
 
-    if (target == NULL) {
+    if (target == nullptr) {
         ALOGW("%s: Target string was NULL", __FUNCTION__);
     }
 }
 
 void String8Printer::printLine(const char* string) {
-    if (string == NULL) {
+    if (string == nullptr) {
         ALOGW("%s: NULL string passed in", __FUNCTION__);
         return;
-    } else if (mTarget == NULL) {
+    } else if (mTarget == nullptr) {
         ALOGW("%s: Target string was NULL", __FUNCTION__);
         return;
     }
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index b8fb6dc..f054de9 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -42,14 +42,14 @@
 static const char* PATH_SELF_TASK = "/proc/self/task";
 
 static void dumpProcessHeader(Printer& printer, pid_t pid, const char* timeStr) {
-    if (timeStr == NULL) {
+    if (timeStr == nullptr) {
         ALOGW("%s: timeStr was NULL", __FUNCTION__);
         return;
     }
 
     char path[PATH_MAX];
     char procNameBuf[MAX_PROC_PATH];
-    char* procName = NULL;
+    char* procName = nullptr;
     FILE* fp;
 
     snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
@@ -76,7 +76,7 @@
 
 static String8 getThreadName(pid_t tid) {
     char path[PATH_MAX];
-    char* procName = NULL;
+    char* procName = nullptr;
     char procNameBuf[MAX_PROC_PATH];
     FILE* fp;
 
@@ -88,7 +88,7 @@
         ALOGE("%s: Failed to open %s", __FUNCTION__, path);
     }
 
-    if (procName == NULL) {
+    if (procName == nullptr) {
         // Reading /proc/self/task/%d/comm failed due to a race
         return String8::format("[err-unknown-tid-%d]", tid);
     }
@@ -128,7 +128,7 @@
 
 void ProcessCallStack::update() {
     std::unique_ptr<DIR, decltype(&closedir)> dp(opendir(PATH_SELF_TASK), closedir);
-    if (dp == NULL) {
+    if (dp == nullptr) {
         ALOGE("%s: Failed to update the process's call stacks: %s",
               __FUNCTION__, strerror(errno));
         return;
@@ -140,7 +140,7 @@
 
     // Get current time.
     {
-        time_t t = time(NULL);
+        time_t t = time(nullptr);
         struct tm tm;
         localtime_r(&t, &tm);
 
@@ -152,7 +152,7 @@
      * - Read every file in directory => get every tid
      */
     dirent* ep;
-    while ((ep = readdir(dp.get())) != NULL) {
+    while ((ep = readdir(dp.get())) != nullptr) {
         pid_t tid = -1;
         sscanf(ep->d_name, "%d", &tid);
 
diff --git a/libutils/PropertyMap.cpp b/libutils/PropertyMap.cpp
index 4bcdd0f..b8c065d 100644
--- a/libutils/PropertyMap.cpp
+++ b/libutils/PropertyMap.cpp
@@ -112,7 +112,7 @@
 }
 
 status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) {
-    *outMap = NULL;
+    *outMap = nullptr;
 
     Tokenizer* tokenizer;
     status_t status = Tokenizer::open(filename, &tokenizer);
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 8bccb0f..9074850 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -712,7 +712,7 @@
         delete mRefs;
     }
     // For debugging purposes, clear mRefs.  Ineffective against outstanding wp's.
-    const_cast<weakref_impl*&>(mRefs) = NULL;
+    const_cast<weakref_impl*&>(mRefs) = nullptr;
 }
 
 void RefBase::extendObjectLifetime(int32_t mode)
diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp
index bad98b2..7910c6e 100644
--- a/libutils/SharedBuffer.cpp
+++ b/libutils/SharedBuffer.cpp
@@ -75,7 +75,7 @@
                             "Invalid buffer size %zu", newSize);
 
         buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);
-        if (buf != NULL) {
+        if (buf != nullptr) {
             buf->mSize = newSize;
             return buf;
         }
@@ -94,7 +94,7 @@
     if (onlyOwner()) {
         return const_cast<SharedBuffer*>(this);
     }
-    return 0;
+    return nullptr;
 }
 
 SharedBuffer* SharedBuffer::reset(size_t new_size) const
diff --git a/libutils/SharedBuffer.h b/libutils/SharedBuffer.h
index 81cadff..fdf13a9 100644
--- a/libutils/SharedBuffer.h
+++ b/libutils/SharedBuffer.h
@@ -124,11 +124,11 @@
 }
 
 SharedBuffer* SharedBuffer::bufferFromData(void* data) {
-    return data ? static_cast<SharedBuffer *>(data)-1 : 0;
+    return data ? static_cast<SharedBuffer *>(data)-1 : nullptr;
 }
     
 const SharedBuffer* SharedBuffer::bufferFromData(const void* data) {
-    return data ? static_cast<const SharedBuffer *>(data)-1 : 0;
+    return data ? static_cast<const SharedBuffer *>(data)-1 : nullptr;
 }
 
 size_t SharedBuffer::sizeFromData(const void* data) {
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 84d53dd..f820b8b 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -74,7 +74,7 @@
 }
 
 String16::String16(StaticLinkage)
-    : mString(0)
+    : mString(nullptr)
 {
     // this constructor is used when we can't rely on the static-initializers
     // having run. In this case we always allocate an empty string. It's less
@@ -336,7 +336,7 @@
 {
     const size_t N = size();
     const char16_t* str = string();
-    char16_t* edit = NULL;
+    char16_t* edit = nullptr;
     for (size_t i=0; i<N; i++) {
         const char16_t v = str[i];
         if (v >= 'A' && v <= 'Z') {
@@ -358,7 +358,7 @@
 {
     const size_t N = size();
     const char16_t* str = string();
-    char16_t* edit = NULL;
+    char16_t* edit = nullptr;
     for (size_t i=0; i<N; i++) {
         if (str[i] == replaceThis) {
             if (!edit) {
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 580e870..8d318f7 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -58,7 +58,7 @@
 {
     if (len > 0) {
         if (len == SIZE_MAX) {
-            return NULL;
+            return nullptr;
         }
         SharedBuffer* buf = SharedBuffer::alloc(len+1);
         ALOG_ASSERT(buf, "Unable to allocate shared buffer");
@@ -68,7 +68,7 @@
             str[len] = 0;
             return str;
         }
-        return NULL;
+        return nullptr;
     }
 
     return getEmptyString();
@@ -126,7 +126,7 @@
 }
 
 String8::String8(StaticLinkage)
-    : mString(0)
+    : mString(nullptr)
 {
     // this constructor is used when we can't rely on the static-initializers
     // having run. In this case we always allocate an empty string. It's less
@@ -147,7 +147,7 @@
 String8::String8(const char* o)
     : mString(allocFromUTF8(o, strlen(o)))
 {
-    if (mString == NULL) {
+    if (mString == nullptr) {
         mString = getEmptyString();
     }
 }
@@ -155,7 +155,7 @@
 String8::String8(const char* o, size_t len)
     : mString(allocFromUTF8(o, len))
 {
-    if (mString == NULL) {
+    if (mString == nullptr) {
         mString = getEmptyString();
     }
 }
@@ -319,7 +319,7 @@
      * second vsnprintf access undefined args.
      */
     va_copy(tmp_args, args);
-    n = vsnprintf(NULL, 0, fmt, tmp_args);
+    n = vsnprintf(nullptr, 0, fmt, tmp_args);
     va_end(tmp_args);
 
     if (n != 0) {
@@ -360,7 +360,7 @@
         mString = str;
         return str;
     }
-    return NULL;
+    return nullptr;
 }
 
 void String8::unlockBuffer()
@@ -512,7 +512,7 @@
     const char*const buf = mString;
 
     cp = strrchr(buf, OS_PATH_SEPARATOR);
-    if (cp == NULL)
+    if (cp == nullptr)
         return String8(*this);
     else
         return String8(cp+1);
@@ -524,7 +524,7 @@
     const char*const str = mString;
 
     cp = strrchr(str, OS_PATH_SEPARATOR);
-    if (cp == NULL)
+    if (cp == nullptr)
         return String8("");
     else
         return String8(str, cp - str);
@@ -543,7 +543,7 @@
         cp = strchr(buf, OS_PATH_SEPARATOR);
     }
 
-    if (cp == NULL) {
+    if (cp == nullptr) {
         String8 res = buf != str ? String8(buf) : *this;
         if (outRemains) *outRemains = String8("");
         return res;
@@ -567,15 +567,15 @@
 
     // only look at the filename
     lastSlash = strrchr(str, OS_PATH_SEPARATOR);
-    if (lastSlash == NULL)
+    if (lastSlash == nullptr)
         lastSlash = str;
     else
         lastSlash++;
 
     // find the last dot
     lastDot = strrchr(lastSlash, '.');
-    if (lastDot == NULL)
-        return NULL;
+    if (lastDot == nullptr)
+        return nullptr;
 
     // looks good, ship it
     return const_cast<char*>(lastDot);
@@ -586,7 +586,7 @@
     char* ext;
 
     ext = find_extension();
-    if (ext != NULL)
+    if (ext != nullptr)
         return String8(ext);
     else
         return String8("");
@@ -598,7 +598,7 @@
     const char* const str = mString;
 
     ext = find_extension();
-    if (ext == NULL)
+    if (ext == nullptr)
         return String8(*this);
     else
         return String8(str, ext - str);
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 7d7f0e2..43ec6c1 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -163,7 +163,7 @@
     // Note that *threadID is directly available to the parent only, as it is
     // assigned after the child starts.  Use memory barrier / lock if the child
     // or other threads also need access.
-    if (threadId != NULL) {
+    if (threadId != nullptr) {
         *threadId = (android_thread_id_t)thread; // XXX: this is not portable
     }
     return 1;
@@ -768,7 +768,7 @@
         strong.clear();
         // And immediately, re-acquire a strong reference for the next loop
         strong = weak.promote();
-    } while(strong != 0);
+    } while(strong != nullptr);
 
     return 0;
 }
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index b2df9a5..c3641ef 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -45,7 +45,7 @@
     // is windows.
     struct timeval t;
     t.tv_sec = t.tv_usec = 0;
-    gettimeofday(&t, NULL);
+    gettimeofday(&t, nullptr);
     return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL;
 }
 #endif
diff --git a/libutils/Tokenizer.cpp b/libutils/Tokenizer.cpp
index b68a2cf..f73d699 100644
--- a/libutils/Tokenizer.cpp
+++ b/libutils/Tokenizer.cpp
@@ -28,7 +28,7 @@
 namespace android {
 
 static inline bool isDelimiter(char ch, const char* delimiters) {
-    return strchr(delimiters, ch) != NULL;
+    return strchr(delimiters, ch) != nullptr;
 }
 
 Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer,
@@ -46,7 +46,7 @@
 }
 
 status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
-    *outTokenizer = NULL;
+    *outTokenizer = nullptr;
 
     int result = NO_ERROR;
     int fd = ::open(filename.string(), O_RDONLY);
@@ -64,12 +64,12 @@
             FileMap* fileMap = new FileMap();
             bool ownBuffer = false;
             char* buffer;
-            if (fileMap->create(NULL, fd, 0, length, true)) {
+            if (fileMap->create(nullptr, fd, 0, length, true)) {
                 fileMap->advise(FileMap::SEQUENTIAL);
                 buffer = static_cast<char*>(fileMap->getDataPtr());
             } else {
                 delete fileMap;
-                fileMap = NULL;
+                fileMap = nullptr;
 
                 // Fall back to reading into a buffer since we can't mmap files in sysfs.
                 // The length we obtained from stat is wrong too (it will always be 4096)
@@ -81,7 +81,7 @@
                     result = -errno;
                     ALOGE("Error reading file '%s': %s", filename.string(), strerror(errno));
                     delete[] buffer;
-                    buffer = NULL;
+                    buffer = nullptr;
                 } else {
                     length = size_t(nrd);
                 }
@@ -98,7 +98,7 @@
 
 status_t Tokenizer::fromContents(const String8& filename,
         const char* contents, Tokenizer** outTokenizer) {
-    *outTokenizer = new Tokenizer(filename, NULL,
+    *outTokenizer = new Tokenizer(filename, nullptr,
             const_cast<char*>(contents), false, strlen(contents));
     return OK;
 }
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index 1086831..e00fb81 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -159,7 +159,7 @@
         return -1;
     }
     size_t dummy_index;
-    if (next_index == NULL) {
+    if (next_index == nullptr) {
         next_index = &dummy_index;
     }
     size_t num_read;
@@ -173,7 +173,7 @@
 
 ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len)
 {
-    if (src == NULL || src_len == 0) {
+    if (src == nullptr || src_len == 0) {
         return -1;
     }
 
@@ -195,7 +195,7 @@
 
 void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len)
 {
-    if (src == NULL || src_len == 0 || dst == NULL) {
+    if (src == nullptr || src_len == 0 || dst == nullptr) {
         return;
     }
 
@@ -363,7 +363,7 @@
 
 void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len)
 {
-    if (src == NULL || src_len == 0 || dst == NULL) {
+    if (src == nullptr || src_len == 0 || dst == nullptr) {
         return;
     }
 
@@ -440,7 +440,7 @@
 
 ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len)
 {
-    if (src == NULL || src_len == 0) {
+    if (src == nullptr || src_len == 0) {
         return -1;
     }
 
@@ -490,7 +490,7 @@
 
 size_t utf8_to_utf32_length(const char *src, size_t src_len)
 {
-    if (src == NULL || src_len == 0) {
+    if (src == nullptr || src_len == 0) {
         return 0;
     }
     size_t ret = 0;
@@ -515,7 +515,7 @@
 
 void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst)
 {
-    if (src == NULL || src_len == 0 || dst == NULL) {
+    if (src == nullptr || src_len == 0 || dst == nullptr) {
         return;
     }
 
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index ef3277f..00a904d 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -44,7 +44,7 @@
 // ----------------------------------------------------------------------------
 
 VectorImpl::VectorImpl(size_t itemSize, uint32_t flags)
-    : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize)
+    : mStorage(nullptr), mCount(0), mFlags(flags), mItemSize(itemSize)
 {
 }
 
@@ -77,7 +77,7 @@
             mCount = rhs.mCount;
             SharedBuffer::bufferFromData(mStorage)->acquire();
         } else {
-            mStorage = 0;
+            mStorage = nullptr;
             mCount = 0;
         }
     }
@@ -89,14 +89,14 @@
     if (mStorage) {
         const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage);
         SharedBuffer* editable = sb->attemptEdit();
-        if (editable == 0) {
+        if (editable == nullptr) {
             // If we're here, we're not the only owner of the buffer.
             // We must make a copy of it.
             editable = SharedBuffer::alloc(sb->size());
             // Fail instead of returning a pointer to storage that's not
             // editable. Otherwise we'd be editing the contents of a buffer
             // for which we're not the only owner, which is undefined behaviour.
-            LOG_ALWAYS_FATAL_IF(editable == NULL);
+            LOG_ALWAYS_FATAL_IF(editable == nullptr);
             _do_copy(editable->data(), mStorage, mCount);
             release_storage();
             mStorage = editable->data();
@@ -141,7 +141,7 @@
 
 ssize_t VectorImpl::insertAt(size_t index, size_t numItems)
 {
-    return insertAt(0, index, numItems);
+    return insertAt(nullptr, index, numItems);
 }
 
 ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems)
@@ -177,7 +177,7 @@
     const ssize_t count = size();
     if (count > 1) {
         void* array = const_cast<void*>(arrayImpl());
-        void* temp = 0;
+        void* temp = nullptr;
         ssize_t i = 1;
         while (i < count) {
             void* item = reinterpret_cast<char*>(array) + mItemSize*(i);
@@ -205,7 +205,7 @@
                     _do_copy(next, curr, 1);
                     next = curr;
                     --j;
-                    curr = NULL;
+                    curr = nullptr;
                     if (j >= 0) {
                         curr = reinterpret_cast<char*>(array) + mItemSize*(j);
                     }
@@ -233,7 +233,7 @@
 
 void VectorImpl::push()
 {
-    push(0);
+    push(nullptr);
 }
 
 void VectorImpl::push(const void* item)
@@ -243,7 +243,7 @@
 
 ssize_t VectorImpl::add()
 {
-    return add(0);
+    return add(nullptr);
 }
 
 ssize_t VectorImpl::add(const void* item)
@@ -253,7 +253,7 @@
 
 ssize_t VectorImpl::replaceAt(size_t index)
 {
-    return replaceAt(0, index);
+    return replaceAt(nullptr, index);
 }
 
 ssize_t VectorImpl::replaceAt(const void* prototype, size_t index)
@@ -267,10 +267,10 @@
 
     void* item = editItemLocation(index);
     if (item != prototype) {
-        if (item == 0)
+        if (item == nullptr)
             return NO_MEMORY;
         _do_destroy(item, 1);
-        if (prototype == 0) {
+        if (prototype == nullptr) {
             _do_construct(item, 1);
         } else {
             _do_copy(item, prototype, 1);
@@ -294,7 +294,7 @@
 void VectorImpl::finish_vector()
 {
     release_storage();
-    mStorage = 0;
+    mStorage = nullptr;
     mCount = 0;
 }
 
@@ -315,7 +315,7 @@
             return reinterpret_cast<char*>(buffer) + index*mItemSize;
         }
     }
-    return 0;
+    return nullptr;
 }
 
 const void* VectorImpl::itemLocation(size_t index) const
@@ -330,7 +330,7 @@
             return reinterpret_cast<const char*>(buffer) + index*mItemSize;
         }
     }
-    return 0;
+    return nullptr;
 }
 
 ssize_t VectorImpl::setCapacity(size_t new_capacity)
@@ -418,7 +418,7 @@
             if (sb) {
                 mStorage = sb->data();
             } else {
-                return NULL;
+                return nullptr;
             }
         } else {
             SharedBuffer* sb = SharedBuffer::alloc(new_alloc_size);
@@ -435,7 +435,7 @@
                 release_storage();
                 mStorage = const_cast<void*>(array);
             } else {
-                return NULL;
+                return nullptr;
             }
         }
     } else {
diff --git a/libutils/include/utils/AndroidThreads.h b/libutils/include/utils/AndroidThreads.h
index dab888d..a8d7851 100644
--- a/libutils/include/utils/AndroidThreads.h
+++ b/libutils/include/utils/AndroidThreads.h
@@ -106,7 +106,7 @@
                             const char* threadName = "android:unnamed_thread",
                             int32_t threadPriority = PRIORITY_DEFAULT,
                             size_t threadStackSize = 0,
-                            thread_id_t *threadId = 0)
+                            thread_id_t *threadId = nullptr)
 {
     return androidCreateThreadEtc(entryFunction, userData, threadName,
         threadPriority, threadStackSize, threadId) ? true : false;
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 9622142..0c1b875 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -49,13 +49,13 @@
     // Dump a stack trace to the log using the supplied logtag.
     void log(const char* logtag,
              android_LogPriority priority = ANDROID_LOG_DEBUG,
-             const char* prefix = 0) const;
+             const char* prefix = nullptr) const;
 
     // Dump a stack trace to the specified file descriptor.
-    void dump(int fd, int indent = 0, const char* prefix = 0) const;
+    void dump(int fd, int indent = 0, const char* prefix = nullptr) const;
 
     // Return a string (possibly very long) containing the complete stack trace.
-    String8 toString(const char* prefix = 0) const;
+    String8 toString(const char* prefix = nullptr) const;
 
     // Dump a serialized representation of the stack trace to the specified printer.
     void print(Printer& printer) const;
diff --git a/libutils/include/utils/Condition.h b/libutils/include/utils/Condition.h
index c8da67c..540ed82 100644
--- a/libutils/include/utils/Condition.h
+++ b/libutils/include/utils/Condition.h
@@ -124,7 +124,7 @@
 #else // __APPLE__
     // Apple doesn't support POSIX clocks.
     struct timeval t;
-    gettimeofday(&t, NULL);
+    gettimeofday(&t, nullptr);
     ts.tv_sec = t.tv_sec;
     ts.tv_nsec = t.tv_usec*1000;
 #endif
diff --git a/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h
index a62e67f..c439c5c 100644
--- a/libutils/include/utils/Looper.h
+++ b/libutils/include/utils/Looper.h
@@ -24,6 +24,8 @@
 
 #include <sys/epoll.h>
 
+#include <android-base/unique_fd.h>
+
 namespace android {
 
 /*
@@ -262,7 +264,7 @@
      */
     int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
     inline int pollOnce(int timeoutMillis) {
-        return pollOnce(timeoutMillis, NULL, NULL, NULL);
+        return pollOnce(timeoutMillis, nullptr, nullptr, nullptr);
     }
 
     /**
@@ -272,7 +274,7 @@
      */
     int pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData);
     inline int pollAll(int timeoutMillis) {
-        return pollAll(timeoutMillis, NULL, NULL, NULL);
+        return pollAll(timeoutMillis, nullptr, nullptr, nullptr);
     }
 
     /**
@@ -447,7 +449,7 @@
 
     const bool mAllowNonCallbacks; // immutable
 
-    int mWakeEventFd;  // immutable
+    android::base::unique_fd mWakeEventFd;  // immutable
     Mutex mLock;
 
     Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock
@@ -457,7 +459,7 @@
     // any use of it is racy anyway.
     volatile bool mPolling;
 
-    int mEpollFd; // guarded by mLock but only modified on the looper thread
+    android::base::unique_fd mEpollFd;  // guarded by mLock but only modified on the looper thread
     bool mEpollRebuildRequired; // guarded by mLock
 
     // Locked list of file descriptor monitoring requests.
diff --git a/libutils/include/utils/LruCache.h b/libutils/include/utils/LruCache.h
index 89dccd6..36775d0 100644
--- a/libutils/include/utils/LruCache.h
+++ b/libutils/include/utils/LruCache.h
@@ -71,7 +71,7 @@
         Entry* parent;
         Entry* child;
 
-        Entry(TKey _key, TValue _value) : key(_key), value(_value), parent(NULL), child(NULL) {
+        Entry(TKey _key, TValue _value) : key(_key), value(_value), parent(nullptr), child(nullptr) {
         }
         const TKey& getKey() const final { return key; }
     };
@@ -162,9 +162,9 @@
 template <typename TKey, typename TValue>
 LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
     : mSet(new LruCacheSet())
-    , mListener(NULL)
-    , mOldest(NULL)
-    , mYoungest(NULL)
+    , mListener(nullptr)
+    , mOldest(nullptr)
+    , mYoungest(nullptr)
     , mMaxCapacity(maxCapacity)
     , mNullValue(0) {
     mSet->max_load_factor(1.0);
@@ -236,7 +236,7 @@
 
 template <typename TKey, typename TValue>
 bool LruCache<TKey, TValue>::removeOldest() {
-    if (mOldest != NULL) {
+    if (mOldest != nullptr) {
         return remove(mOldest->key);
         // TODO: should probably abort if false
     }
@@ -254,12 +254,12 @@
 template <typename TKey, typename TValue>
 void LruCache<TKey, TValue>::clear() {
     if (mListener) {
-        for (Entry* p = mOldest; p != NULL; p = p->child) {
+        for (Entry* p = mOldest; p != nullptr; p = p->child) {
             (*mListener)(p->key, p->value);
         }
     }
-    mYoungest = NULL;
-    mOldest = NULL;
+    mYoungest = nullptr;
+    mOldest = nullptr;
     for (auto entry : *mSet.get()) {
         delete entry;
     }
@@ -268,7 +268,7 @@
 
 template <typename TKey, typename TValue>
 void LruCache<TKey, TValue>::attachToCache(Entry& entry) {
-    if (mYoungest == NULL) {
+    if (mYoungest == nullptr) {
         mYoungest = mOldest = &entry;
     } else {
         entry.parent = mYoungest;
@@ -279,19 +279,19 @@
 
 template <typename TKey, typename TValue>
 void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
-    if (entry.parent != NULL) {
+    if (entry.parent != nullptr) {
         entry.parent->child = entry.child;
     } else {
         mOldest = entry.child;
     }
-    if (entry.child != NULL) {
+    if (entry.child != nullptr) {
         entry.child->parent = entry.parent;
     } else {
         mYoungest = entry.parent;
     }
 
-    entry.parent = NULL;
-    entry.child = NULL;
+    entry.parent = nullptr;
+    entry.child = nullptr;
 }
 
 }
diff --git a/libutils/include/utils/Mutex.h b/libutils/include/utils/Mutex.h
index 1228df4..29c2e8c 100644
--- a/libutils/include/utils/Mutex.h
+++ b/libutils/include/utils/Mutex.h
@@ -100,7 +100,7 @@
 
     Mutex();
     explicit Mutex(const char* name);
-    explicit Mutex(int type, const char* name = NULL);
+    explicit Mutex(int type, const char* name = nullptr);
     ~Mutex();
 
     // lock or unlock the mutex
@@ -160,10 +160,10 @@
 #if !defined(_WIN32)
 
 inline Mutex::Mutex() {
-    pthread_mutex_init(&mMutex, NULL);
+    pthread_mutex_init(&mMutex, nullptr);
 }
 inline Mutex::Mutex(__attribute__((unused)) const char* name) {
-    pthread_mutex_init(&mMutex, NULL);
+    pthread_mutex_init(&mMutex, nullptr);
 }
 inline Mutex::Mutex(int type, __attribute__((unused)) const char* name) {
     if (type == SHARED) {
@@ -173,7 +173,7 @@
         pthread_mutex_init(&mMutex, &attr);
         pthread_mutexattr_destroy(&attr);
     } else {
-        pthread_mutex_init(&mMutex, NULL);
+        pthread_mutex_init(&mMutex, nullptr);
     }
 }
 inline Mutex::~Mutex() {
diff --git a/libutils/include/utils/Printer.h b/libutils/include/utils/Printer.h
index a6f6928..7465927 100644
--- a/libutils/include/utils/Printer.h
+++ b/libutils/include/utils/Printer.h
@@ -45,7 +45,7 @@
     // (Note that the default ALOG behavior is to ignore blank lines)
     LogPrinter(const char* logtag,
                android_LogPriority priority = ANDROID_LOG_DEBUG,
-               const char* prefix = 0,
+               const char* prefix = nullptr,
                bool ignoreBlankLines = false);
 
     // Print the specified line to logcat. No \n at the end is necessary.
@@ -66,7 +66,7 @@
     // Create a printer using the specified file descriptor.
     // - Each line will be prefixed with 'indent' number of blank spaces.
     // - In addition, each line will be prefixed with the 'prefix' string.
-    FdPrinter(int fd, unsigned int indent = 0, const char* prefix = 0);
+    FdPrinter(int fd, unsigned int indent = 0, const char* prefix = nullptr);
 
     // Print the specified line to the file descriptor. \n is appended automatically.
     virtual void printLine(const char* string);
@@ -90,7 +90,7 @@
     // Create a printer using the specified String8 as the target.
     // - In addition, each line will be prefixed with the 'prefix' string.
     // - target's memory lifetime must be a superset of this String8Printer.
-    String8Printer(String8* target, const char* prefix = 0);
+    String8Printer(String8* target, const char* prefix = nullptr);
 
     // Append the specified line to the String8. \n is appended automatically.
     virtual void printLine(const char* string);
diff --git a/libutils/include/utils/ProcessCallStack.h b/libutils/include/utils/ProcessCallStack.h
index b5f2edc..7e06086 100644
--- a/libutils/include/utils/ProcessCallStack.h
+++ b/libutils/include/utils/ProcessCallStack.h
@@ -43,13 +43,13 @@
 
     // Print all stack traces to the log using the supplied logtag.
     void log(const char* logtag, android_LogPriority priority = ANDROID_LOG_DEBUG,
-             const char* prefix = 0) const;
+             const char* prefix = nullptr) const;
 
     // Dump all stack traces to the specified file descriptor.
-    void dump(int fd, int indent = 0, const char* prefix = 0) const;
+    void dump(int fd, int indent = 0, const char* prefix = nullptr) const;
 
     // Return a string (possibly very long) containing all the stack traces.
-    String8 toString(const char* prefix = 0) const;
+    String8 toString(const char* prefix = nullptr) const;
 
     // Dump a serialized representation of all the stack traces to the specified printer.
     void print(Printer& printer) const;
diff --git a/libutils/include/utils/RWLock.h b/libutils/include/utils/RWLock.h
index 7d43e69..64e370e 100644
--- a/libutils/include/utils/RWLock.h
+++ b/libutils/include/utils/RWLock.h
@@ -48,7 +48,7 @@
 
                 RWLock();
     explicit    RWLock(const char* name);
-    explicit    RWLock(int type, const char* name = NULL);
+    explicit    RWLock(int type, const char* name = nullptr);
                 ~RWLock();
 
     status_t    readLock();
@@ -82,10 +82,10 @@
 };
 
 inline RWLock::RWLock() {
-    pthread_rwlock_init(&mRWLock, NULL);
+    pthread_rwlock_init(&mRWLock, nullptr);
 }
 inline RWLock::RWLock(__attribute__((unused)) const char* name) {
-    pthread_rwlock_init(&mRWLock, NULL);
+    pthread_rwlock_init(&mRWLock, nullptr);
 }
 inline RWLock::RWLock(int type, __attribute__((unused)) const char* name) {
     if (type == SHARED) {
@@ -95,7 +95,7 @@
         pthread_rwlock_init(&mRWLock, &attr);
         pthread_rwlockattr_destroy(&attr);
     } else {
-        pthread_rwlock_init(&mRWLock, NULL);
+        pthread_rwlock_init(&mRWLock, nullptr);
     }
 }
 inline RWLock::~RWLock() {
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index e817ee4..1780cf2 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -354,7 +354,7 @@
 public:
     typedef typename RefBase::weakref_type weakref_type;
 
-    inline wp() : m_ptr(0) { }
+    inline wp() : m_ptr(nullptr) { }
 
     wp(T* other);  // NOLINT(implicit)
     wp(const wp<T>& other);
@@ -505,7 +505,7 @@
 wp<T>& wp<T>::operator = (T* other)
 {
     weakref_type* newRefs =
-        other ? other->createWeak(this) : 0;
+        other ? other->createWeak(this) : nullptr;
     if (m_ptr) m_refs->decWeak(this);
     m_ptr = other;
     m_refs = newRefs;
@@ -528,7 +528,7 @@
 wp<T>& wp<T>::operator = (const sp<T>& other)
 {
     weakref_type* newRefs =
-        other != NULL ? other->createWeak(this) : 0;
+        other != nullptr ? other->createWeak(this) : nullptr;
     T* otherPtr(other.m_ptr);
     if (m_ptr) m_refs->decWeak(this);
     m_ptr = otherPtr;
@@ -563,7 +563,7 @@
 wp<T>& wp<T>::operator = (const sp<U>& other)
 {
     weakref_type* newRefs =
-        other != NULL ? other->createWeak(this) : 0;
+        other != nullptr ? other->createWeak(this) : 0;
     U* otherPtr(other.m_ptr);
     if (m_ptr) m_refs->decWeak(this);
     m_ptr = otherPtr;
diff --git a/libutils/include/utils/Singleton.h b/libutils/include/utils/Singleton.h
index 2dd5a47..44d8ad7 100644
--- a/libutils/include/utils/Singleton.h
+++ b/libutils/include/utils/Singleton.h
@@ -51,7 +51,7 @@
     static TYPE& getInstance() {
         Mutex::Autolock _l(sLock);
         TYPE* instance = sInstance;
-        if (instance == 0) {
+        if (instance == nullptr) {
             instance = new TYPE();
             sInstance = instance;
         }
@@ -60,7 +60,7 @@
 
     static bool hasInstance() {
         Mutex::Autolock _l(sLock);
-        return sInstance != 0;
+        return sInstance != nullptr;
     }
     
 protected:
@@ -90,7 +90,7 @@
 #define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE)                 \
     template<> ::android::Mutex  \
         (::android::Singleton< TYPE >::sLock)(::android::Mutex::PRIVATE);  \
-    template<> TYPE* ::android::Singleton< TYPE >::sInstance(0);  /* NOLINT */ \
+    template<> TYPE* ::android::Singleton< TYPE >::sInstance(nullptr);  /* NOLINT */ \
     template class ::android::Singleton< TYPE >;
 
 
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index 94ac32f..c8f584e 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -187,7 +187,7 @@
      * "/tmp" --> "tmp" (remain = "")
      * "bar.c" --> "bar.c" (remain = "")
      */
-    String8 walkPath(String8* outRemains = NULL) const;
+    String8 walkPath(String8* outRemains = nullptr) const;
 
     /*
      * Return the filename extension.  This is the last '.' and any number
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 9cd278f1..360fce5 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -52,7 +52,7 @@
 template<typename T>
 class sp {
 public:
-    inline sp() : m_ptr(0) { }
+    inline sp() : m_ptr(nullptr) { }
 
     sp(T* other);  // NOLINT(implicit)
     sp(const sp<T>& other);
@@ -230,7 +230,7 @@
 void sp<T>::clear() {
     if (m_ptr) {
         m_ptr->decStrong(this);
-        m_ptr = 0;
+        m_ptr = nullptr;
     }
 }
 
diff --git a/libutils/include/utils/VectorImpl.h b/libutils/include/utils/VectorImpl.h
index 55d5d98..41b9f33 100644
--- a/libutils/include/utils/VectorImpl.h
+++ b/libutils/include/utils/VectorImpl.h
@@ -157,7 +157,7 @@
     virtual int             do_compare(const void* lhs, const void* rhs) const = 0;
 
 private:
-            ssize_t         _indexOrderOf(const void* item, size_t* order = 0) const;
+            ssize_t         _indexOrderOf(const void* item, size_t* order = nullptr) const;
 
             // these are made private, because they can't be used on a SortedVector
             // (they don't have an implementation either)
diff --git a/libutils/misc.cpp b/libutils/misc.cpp
index f074341..f77e189 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -41,13 +41,13 @@
 
 #if !defined(_WIN32)
 static pthread_mutex_t gSyspropMutex = PTHREAD_MUTEX_INITIALIZER;
-static Vector<sysprop_change_callback_info>* gSyspropList = NULL;
+static Vector<sysprop_change_callback_info>* gSyspropList = nullptr;
 #endif
 
 #if !defined(_WIN32)
 void add_sysprop_change_callback(sysprop_change_callback cb, int priority) {
     pthread_mutex_lock(&gSyspropMutex);
-    if (gSyspropList == NULL) {
+    if (gSyspropList == nullptr) {
         gSyspropList = new Vector<sysprop_change_callback_info>();
     }
     sysprop_change_callback_info info;
@@ -103,7 +103,7 @@
 #if !defined(_WIN32)
     pthread_mutex_lock(&gSyspropMutex);
     Vector<sysprop_change_callback_info> listeners;
-    if (gSyspropList != NULL) {
+    if (gSyspropList != nullptr) {
         listeners = *gSyspropList;
     }
     pthread_mutex_unlock(&gSyspropMutex);
diff --git a/libutils/tests/Looper_test.cpp b/libutils/tests/Looper_test.cpp
index 8ebcfaf..2282ced 100644
--- a/libutils/tests/Looper_test.cpp
+++ b/libutils/tests/Looper_test.cpp
@@ -339,7 +339,7 @@
     Pipe pipe;
 
     pipe.writeSignal();
-    mLooper->addFd(pipe.receiveFd, expectedIdent, Looper::EVENT_INPUT, NULL, expectedData);
+    mLooper->addFd(pipe.receiveFd, expectedIdent, Looper::EVENT_INPUT, nullptr, expectedData);
 
     StopWatch stopWatch("pollOnce");
     int fd;
@@ -364,7 +364,7 @@
 
 TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) {
     Pipe pipe;
-    int result = mLooper->addFd(pipe.receiveFd, 0, Looper::EVENT_INPUT, NULL, NULL);
+    int result = mLooper->addFd(pipe.receiveFd, 0, Looper::EVENT_INPUT, nullptr, nullptr);
 
     EXPECT_EQ(1, result)
             << "addFd should return 1 because FD was added";
@@ -372,7 +372,7 @@
 
 TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) {
     Pipe pipe;
-    int result = mLooper->addFd(pipe.receiveFd, -1, Looper::EVENT_INPUT, NULL, NULL);
+    int result = mLooper->addFd(pipe.receiveFd, -1, Looper::EVENT_INPUT, nullptr, nullptr);
 
     EXPECT_EQ(-1, result)
             << "addFd should return -1 because arguments were invalid";
@@ -381,7 +381,7 @@
 TEST_F(LooperTest, AddFd_WhenNoCallbackAndAllowNonCallbacksIsFalse_ReturnsError) {
     Pipe pipe;
     sp<Looper> looper = new Looper(false /*allowNonCallbacks*/);
-    int result = looper->addFd(pipe.receiveFd, 0, 0, NULL, NULL);
+    int result = looper->addFd(pipe.receiveFd, 0, 0, nullptr, nullptr);
 
     EXPECT_EQ(-1, result)
             << "addFd should return -1 because arguments were invalid";
diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp
index 4e885bb..c4d917b 100644
--- a/libutils/tests/LruCache_test.cpp
+++ b/libutils/tests/LruCache_test.cpp
@@ -110,7 +110,7 @@
 
 class EntryRemovedCallback : public OnEntryRemoved<SimpleKey, StringValue> {
 public:
-    EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(NULL) { }
+    EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(nullptr) { }
     ~EntryRemovedCallback() {}
     void operator()(SimpleKey& k, StringValue& v) {
         callbackCount += 1;
@@ -153,7 +153,7 @@
 TEST_F(LruCacheTest, Empty) {
     LruCache<SimpleKey, StringValue> cache(100);
 
-    EXPECT_EQ(NULL, cache.get(0));
+    EXPECT_EQ(nullptr, cache.get(0));
     EXPECT_EQ(0u, cache.size());
 }
 
@@ -175,7 +175,7 @@
     cache.put(1, "one");
     cache.put(2, "two");
     cache.put(3, "three");
-    EXPECT_EQ(NULL, cache.get(1));
+    EXPECT_EQ(nullptr, cache.get(1));
     EXPECT_STREQ("two", cache.get(2));
     EXPECT_STREQ("three", cache.get(3));
     EXPECT_EQ(2u, cache.size());
@@ -188,7 +188,7 @@
     cache.put(2, "two");
     cache.put(3, "three");
     cache.removeOldest();
-    EXPECT_EQ(NULL, cache.get(1));
+    EXPECT_EQ(nullptr, cache.get(1));
     EXPECT_STREQ("two", cache.get(2));
     EXPECT_STREQ("three", cache.get(3));
     EXPECT_EQ(2u, cache.size());
@@ -203,7 +203,7 @@
     EXPECT_STREQ("one", cache.get(1));
     cache.removeOldest();
     EXPECT_STREQ("one", cache.get(1));
-    EXPECT_EQ(NULL, cache.get(2));
+    EXPECT_EQ(nullptr, cache.get(2));
     EXPECT_STREQ("three", cache.get(3));
     EXPECT_EQ(2u, cache.size());
 }
@@ -230,7 +230,7 @@
         int index = random() % kNumKeys;
         uint32_t key = hash_int(index);
         const char *val = cache.get(key);
-        if (val != NULL) {
+        if (val != nullptr) {
             EXPECT_EQ(strings[index], val);
             hitCount++;
         } else {
diff --git a/libutils/tests/Vector_test.cpp b/libutils/tests/Vector_test.cpp
index e074a92..5336c40 100644
--- a/libutils/tests/Vector_test.cpp
+++ b/libutils/tests/Vector_test.cpp
@@ -98,7 +98,7 @@
 
   // Checks that the size calculation (not the capacity calculation) doesn't
   // overflow : the size here will be (1 + SIZE_MAX).
-  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "new_size overflow");
+  EXPECT_DEATH(vector.insertArrayAt(nullptr, 0, SIZE_MAX), "new_size overflow");
 }
 
 TEST_F(VectorTest, _grow_OverflowCapacityDoubling) {
@@ -106,14 +106,14 @@
 
   // This should fail because the calculated capacity will overflow even though
   // the size of the vector doesn't.
-  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "new_capacity overflow");
+  EXPECT_DEATH(vector.insertArrayAt(nullptr, 0, (SIZE_MAX - 1)), "new_capacity overflow");
 }
 
 TEST_F(VectorTest, _grow_OverflowBufferAlloc) {
   Vector<int> vector;
   // This should fail because the capacity * sizeof(int) overflows, even
   // though the capacity itself doesn't.
-  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "new_alloc_size overflow");
+  EXPECT_DEATH(vector.insertArrayAt(nullptr, 0, (SIZE_MAX / 2)), "new_alloc_size overflow");
 }
 
 TEST_F(VectorTest, editArray_Shared) {
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 5e5e7af..9536fc7 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -33,6 +33,10 @@
 #include <memory>
 #include <vector>
 
+#if defined(__BIONIC__)
+#include <android/fdsan.h>
+#endif
+
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>  // TEMP_FAILURE_RETRY may or may not be in unistd
@@ -165,6 +169,44 @@
   return 0;
 }
 
+ZipArchive::ZipArchive(const int fd, bool assume_ownership)
+    : mapped_zip(fd),
+      close_file(assume_ownership),
+      directory_offset(0),
+      central_directory(),
+      directory_map(new android::FileMap()),
+      num_entries(0),
+      hash_table_size(0),
+      hash_table(nullptr) {
+#if defined(__BIONIC__)
+  if (assume_ownership) {
+    android_fdsan_exchange_owner_tag(fd, 0, reinterpret_cast<uint64_t>(this));
+  }
+#endif
+}
+
+ZipArchive::ZipArchive(void* address, size_t length)
+    : mapped_zip(address, length),
+      close_file(false),
+      directory_offset(0),
+      central_directory(),
+      directory_map(new android::FileMap()),
+      num_entries(0),
+      hash_table_size(0),
+      hash_table(nullptr) {}
+
+ZipArchive::~ZipArchive() {
+  if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
+#if defined(__BIONIC__)
+    android_fdsan_close_with_tag(mapped_zip.GetFileDescriptor(), reinterpret_cast<uint64_t>(this));
+#else
+    close(mapped_zip.GetFileDescriptor());
+#endif
+  }
+
+  free(hash_table);
+}
+
 static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
                                     off64_t file_length, off64_t read_amount, uint8_t* scan_buffer) {
   const off64_t search_start = file_length - read_amount;
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 18e0229..0a73300 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -156,33 +156,9 @@
   uint32_t hash_table_size;
   ZipString* hash_table;
 
-  ZipArchive(const int fd, bool assume_ownership)
-      : mapped_zip(fd),
-        close_file(assume_ownership),
-        directory_offset(0),
-        central_directory(),
-        directory_map(new android::FileMap()),
-        num_entries(0),
-        hash_table_size(0),
-        hash_table(nullptr) {}
-
-  ZipArchive(void* address, size_t length)
-      : mapped_zip(address, length),
-        close_file(false),
-        directory_offset(0),
-        central_directory(),
-        directory_map(new android::FileMap()),
-        num_entries(0),
-        hash_table_size(0),
-        hash_table(nullptr) {}
-
-  ~ZipArchive() {
-    if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
-      close(mapped_zip.GetFileDescriptor());
-    }
-
-    free(hash_table);
-  }
+  ZipArchive(const int fd, bool assume_ownership);
+  ZipArchive(void* address, size_t length);
+  ~ZipArchive();
 
   bool InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
                                   size_t cd_size);
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 1cfef34..c2487d6 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -332,7 +332,7 @@
         data->fd = -1;
         return -1;
     }
-    ALOG_ASSERT((size_t)size < buf_size - 1, data->filename " too large");
+    ALOG_ASSERT((size_t)size < buf_size - 1, "%s too large", data->filename);
     buf[size] = 0;
 
     return 0;
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 750761f..da8d2d4 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -113,6 +113,9 @@
 # graphics timestamp
 # 60100 - 60199 reserved for surfaceflinger
 
+# audio
+# 61000 - 61199 reserved for audioserver
+
 # 0 for screen off, 1 for screen on, 2 for key-guard done
 70000 screen_toggled (screen_state|1|5)
 
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 06c0ab5..7a843d8 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -288,9 +288,9 @@
         uid = AID_ROOT;
     }
 
-    const char* name = NULL;
-    const char* format = NULL;
-    const char* id = NULL;
+    const char* name = nullptr;
+    const char* format = nullptr;
+    const char* id = nullptr;
     for (int i = 1; i < argc; ++i) {
         static const char _name[] = "name=";
         if (!strncmp(argv[i], _name, strlen(_name))) {
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
old mode 100755
new mode 100644
index 70ecbe0..658e079
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -36,7 +36,7 @@
 // reference counts are used to ensure that individual
 // LogTimeEntry lifetime is managed when not protected.
 void FlushCommand::runSocketCommand(SocketClient* client) {
-    LogTimeEntry* entry = NULL;
+    LogTimeEntry* entry = nullptr;
     LastLogTimes& times = mReader.logbuf().mTimes;
 
     LogTimeEntry::wrlock();
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
old mode 100755
new mode 100644
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
old mode 100755
new mode 100644
index 27cd9a8..4ea7877
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -171,13 +171,13 @@
 }
 
 int LogAudit::logPrint(const char* fmt, ...) {
-    if (fmt == NULL) {
+    if (fmt == nullptr) {
         return -EINVAL;
     }
 
     va_list args;
 
-    char* str = NULL;
+    char* str = nullptr;
     va_start(args, fmt);
     int rc = vasprintf(&str, fmt, args);
     va_end(args);
@@ -228,7 +228,7 @@
         static char* last_str;
         static bool last_info;
 
-        if (last_str != NULL) {
+        if (last_str != nullptr) {
             static const char avc[] = "): avc: ";
             char* avcl = strstr(last_str, avc);
             bool skip = false;
@@ -265,10 +265,10 @@
 
                 writev(fdDmesg, iov, arraysize(iov));
                 free(last_str);
-                last_str = NULL;
+                last_str = nullptr;
             }
         }
-        if (last_str == NULL) {
+        if (last_str == nullptr) {
             count = 0;
             last_str = strdup(str);
             last_info = info;
@@ -357,7 +357,7 @@
     static const char comm_str[] = " comm=\"";
     const char* comm = strstr(str, comm_str);
     const char* estr = str + strlen(str);
-    const char* commfree = NULL;
+    const char* commfree = nullptr;
     if (comm) {
         estr = comm;
         comm += sizeof(comm_str) - 1;
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index f20ac45..2d627b9 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -91,7 +91,7 @@
 
 // caller must own and free character string
 char* android::tidToName(pid_t tid) {
-    char* retval = NULL;
+    char* retval = nullptr;
     char buffer[256];
     snprintf(buffer, sizeof(buffer), "/proc/%u/comm", tid);
     int fd = open(buffer, O_RDONLY);
@@ -114,7 +114,7 @@
     char* name = android::pidToName(tid);
     if (!retval) {
         retval = name;
-        name = NULL;
+        name = nullptr;
     }
 
     // check if comm is truncated, see if cmdline has full representation
@@ -162,15 +162,15 @@
         if (!strncmp(name + 1, commName + 1, len)) {
             if (commName[len + 1] == '\0') {
                 free(const_cast<char*>(commName));
-                commName = NULL;
+                commName = nullptr;
             } else {
                 free(const_cast<char*>(name));
-                name = NULL;
+                name = nullptr;
             }
         }
     }
     if (name) {
-        char* buf = NULL;
+        char* buf = nullptr;
         asprintf(&buf, "(%s)", name);
         if (buf) {
             free(const_cast<char*>(name));
@@ -178,7 +178,7 @@
         }
     }
     if (commName) {
-        char* buf = NULL;
+        char* buf = nullptr;
         asprintf(&buf, " %s", commName);
         if (buf) {
             free(const_cast<char*>(commName));
@@ -187,7 +187,7 @@
     }
     // identical to below to calculate the buffer size required
     const char* type = lastSame ? "identical" : "expire";
-    size_t len = snprintf(NULL, 0, format_uid, mUid, name ? name : "",
+    size_t len = snprintf(nullptr, 0, format_uid, mUid, name ? name : "",
                           commName ? commName : "", type, getDropped(),
                           (getDropped() > 1) ? "s" : "");
 
@@ -247,7 +247,7 @@
     iovec[0].iov_base = &entry;
     iovec[0].iov_len = entry.hdr_size;
 
-    char* buffer = NULL;
+    char* buffer = nullptr;
 
     if (mDropped) {
         entry.len = populateDroppedMessage(buffer, parent, lastSame);
diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp
index 6d7c0a5..8bff9da 100644
--- a/logd/LogCommand.cpp
+++ b/logd/LogCommand.cpp
@@ -44,9 +44,9 @@
     char* ptr;
     static const char ws[] = " \n";
 
-    for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(NULL, ws, &ptr)) {
+    for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(nullptr, ws, &ptr)) {
         errno = 0;
-        gid_t Gid = strtol(buf, NULL, 10);
+        gid_t Gid = strtol(buf, nullptr, 10);
         if (errno != 0) {
             return false;
         }
@@ -98,7 +98,7 @@
             continue;
         }
 
-        char* line = NULL;
+        char* line = nullptr;
         size_t len = 0;
         while (getline(&line, &len, file) > 0) {
             static const char groups_string[] = "Groups:\t";
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
old mode 100755
new mode 100644
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
old mode 100755
new mode 100644
index fc51dcf..e568ddc
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -50,7 +50,7 @@
 
     alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
     struct msghdr hdr = {
-        NULL, 0, &iov, 1, control, sizeof(control), 0,
+        nullptr, 0, &iov, 1, control, sizeof(control), 0,
     };
 
     int socket = cli->getSocket();
@@ -66,10 +66,10 @@
 
     buffer[n] = 0;
 
-    struct ucred* cred = NULL;
+    struct ucred* cred = nullptr;
 
     struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
-    while (cmsg != NULL) {
+    while (cmsg != nullptr) {
         if (cmsg->cmsg_level == SOL_SOCKET &&
             cmsg->cmsg_type == SCM_CREDENTIALS) {
             cred = (struct ucred*)CMSG_DATA(cmsg);
@@ -79,7 +79,7 @@
     }
 
     struct ucred fake_cred;
-    if (cred == NULL) {
+    if (cred == nullptr) {
         cred = &fake_cred;
         cred->pid = 0;
         cred->uid = DEFAULT_OVERFLOWUID;
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
old mode 100755
new mode 100644
diff --git a/logd/LogReader.h b/logd/LogReader.h
old mode 100755
new mode 100644
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index af59ddc..cefacf7 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -56,7 +56,7 @@
 
 // caller must own and free character string
 char* pidToName(pid_t pid) {
-    char* retval = NULL;
+    char* retval = nullptr;
     if (pid == 0) {  // special case from auditd/klogd for kernel
         retval = strdup("logd");
     } else {
@@ -286,7 +286,7 @@
                     name = strdup(nameTmp);
                 } else if (fastcmp<strcmp>(name, nameTmp)) {
                     free(const_cast<char*>(name));
-                    name = NULL;
+                    name = nullptr;
                     break;
                 }
             }
@@ -872,7 +872,7 @@
     pidTable_t& writablePidTable = const_cast<pidTable_t&>(pidTable);
     const char* name = writablePidTable.add(pid)->second.getName();
     if (!name) {
-        return NULL;
+        return nullptr;
     }
     return strdup(name);
 }
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index ff7e762..1ab9dd1 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -91,7 +91,7 @@
         fd = TEMP_FAILURE_RETRY(open(
             filename, O_WRONLY | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY));
         if (fd >= 0) {
-            time_t now = time(NULL);
+            time_t now = time(nullptr);
             struct tm tm;
             localtime_r(&now, &tm);
             char timebuf[20];
@@ -208,7 +208,7 @@
             } else if (lineStart) {
                 if (*cp == '#') {
                     /* comment; just scan to end */
-                    lineStart = NULL;
+                    lineStart = nullptr;
                 } else if (isdigit(*cp)) {
                     unsigned long Tag = strtoul(cp, &cp, 10);
                     if (warn && (Tag > emptyTag)) {
@@ -235,7 +235,7 @@
                     if (hasAlpha &&
                         ((cp >= endp) || (*cp == '#') || isspace(*cp))) {
                         if (Tag > emptyTag) {
-                            if (*cp != '\n') lineStart = NULL;
+                            if (*cp != '\n') lineStart = nullptr;
                             continue;
                         }
                         while ((cp < endp) && (*cp != '\n') && isspace(*cp))
@@ -245,14 +245,14 @@
                         while ((cp < endp) && (*cp != '\n')) {
                             if (*cp == '#') {
                                 uid = sniffUid(cp, endp);
-                                lineStart = NULL;
+                                lineStart = nullptr;
                                 break;
                             }
                             ++cp;
                         }
                         while ((cp > format) && isspace(cp[-1])) {
                             --cp;
-                            lineStart = NULL;
+                            lineStart = nullptr;
                         }
                         std::string Format(format, cp - format);
 
@@ -263,7 +263,7 @@
                             android::prdebug("tag name invalid %.*s",
                                              (int)(cp - name + 1), name);
                         }
-                        lineStart = NULL;
+                        lineStart = nullptr;
                     }
                 } else if (!isspace(*cp)) {
                     break;
@@ -364,7 +364,7 @@
     android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
 
     it = tag2name.find(tag);
-    if ((it == tag2name.end()) || (it->second.length() == 0)) return NULL;
+    if ((it == tag2name.end()) || (it->second.length() == 0)) return nullptr;
 
     return it->second.c_str();
 }
@@ -383,7 +383,7 @@
 const char* android::tagToName(uint32_t tag) {
     LogTags* me = logtags;
 
-    if (!me) return NULL;
+    if (!me) return nullptr;
     me->WritePmsgEventLogTags(tag);
     return me->tagToName(tag);
 }
@@ -412,7 +412,7 @@
     android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
 
     iform = tag2format.find(tag);
-    if (iform == tag2format.end()) return NULL;
+    if (iform == tag2format.end()) return nullptr;
 
     return iform->second.c_str();
 }
@@ -441,7 +441,7 @@
                                    bool& unique) {
     key2tag_const_iterator ik;
 
-    bool write = format != NULL;
+    bool write = format != nullptr;
     unique = write;
 
     if (!write) {
@@ -679,7 +679,7 @@
 // are in readonly mode.
 uint32_t LogTags::nameToTag(uid_t uid, const char* name, const char* format) {
     std::string Name = std::string(name);
-    bool write = format != NULL;
+    bool write = format != nullptr;
     bool updateUid = uid != AID_ROOT;
     bool updateFormat = format && *format;
     bool unique;
@@ -848,7 +848,7 @@
 
     if (!list) {
         // switch to read entry only if format == "*"
-        if (format && (format[0] == '*') && !format[1]) format = NULL;
+        if (format && (format[0] == '*') && !format[1]) format = nullptr;
 
         // WAI: for null format, only works for a single entry, we can have
         // multiple entries, one for each format, so we find first entry
diff --git a/logd/LogTags.h b/logd/LogTags.h
index 203318d..e4d165a 100644
--- a/logd/LogTags.h
+++ b/logd/LogTags.h
@@ -87,14 +87,14 @@
     bool RebuildFileEventLogTags(const char* filename, bool warn = true);
 
     void AddEventLogTags(uint32_t tag, uid_t uid, const std::string& Name,
-                         const std::string& Format, const char* source = NULL,
+                         const std::string& Format, const char* source = nullptr,
                          bool warn = false);
 
     void WriteDynamicEventLogTags(uint32_t tag, uid_t uid);
     void WriteDebugEventLogTags(uint32_t tag, uid_t uid);
     // push tag details to persistent storage
     void WritePersistEventLogTags(uint32_t tag, uid_t uid = AID_ROOT,
-                                  const char* source = NULL);
+                                  const char* source = nullptr);
 
     static const uint32_t emptyTag = uint32_t(-1);
 
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
old mode 100755
new mode 100644
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
old mode 100755
new mode 100644
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index 4b8b080..9d762dc 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -51,7 +51,7 @@
 }
 
 PruneList::PruneList() {
-    init(NULL);
+    init(nullptr);
 }
 
 PruneList::~PruneList() {
@@ -79,7 +79,7 @@
     // default here means take ro.logd.filter, persist.logd.filter then
     // internal default in that order.
     if (str && !strcmp(str, _default)) {
-        str = NULL;
+        str = nullptr;
     }
     static const char _disable[] = "disable";
     if (str && !strcmp(str, _disable)) {
diff --git a/mkbootimg/Android.bp b/mkbootimg/Android.bp
index b494346..576a677 100644
--- a/mkbootimg/Android.bp
+++ b/mkbootimg/Android.bp
@@ -9,6 +9,7 @@
 cc_library_headers {
     name: "bootimg_headers",
     vendor_available: true,
+    recovery_available: true,
     export_include_dirs: ["include/bootimg"],
     host_supported: true,
     target: {
diff --git a/property_service/libpropertyinfoserializer/Android.bp b/property_service/libpropertyinfoserializer/Android.bp
index 3c4bdc3..51c1226 100644
--- a/property_service/libpropertyinfoserializer/Android.bp
+++ b/property_service/libpropertyinfoserializer/Android.bp
@@ -17,6 +17,7 @@
 cc_library_static {
     name: "libpropertyinfoserializer",
     defaults: ["propertyinfoserializer_defaults"],
+    recovery_available: true,
     srcs: [
         "property_info_file.cpp",
         "property_info_serializer.cpp",
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 197047d..d3f038e 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -227,6 +227,8 @@
 
     # pstore/ramoops previous console log
     mount pstore pstore /sys/fs/pstore nodev noexec nosuid
+    chown system log /sys/fs/pstore
+    chmod 0550 /sys/fs/pstore
     chown system log /sys/fs/pstore/console-ramoops
     chmod 0440 /sys/fs/pstore/console-ramoops
     chown system log /sys/fs/pstore/console-ramoops-0
@@ -316,8 +318,8 @@
     start vndservicemanager
 
     # Once everything is setup, no need to modify /.
-    # The bind+ro combination avoids modifying any other mount flags.
-    mount rootfs rootfs / remount bind ro
+    # The bind+remount combination allows this to work in containers.
+    mount rootfs rootfs / remount bind ro nodev
     # Mount shared so changes propagate into child namespaces
     mount rootfs rootfs / shared rec
     # Mount default storage into root namespace
@@ -716,6 +718,9 @@
 
 on property:security.perf_harden=0
     write /proc/sys/kernel/perf_event_paranoid 1
+    write /proc/sys/kernel/perf_event_max_sample_rate ${debug.perf_event_max_sample_rate:-100000}
+    write /proc/sys/kernel/perf_cpu_time_max_percent ${debug.perf_cpu_time_max_percent:-25}
+    write /proc/sys/kernel/perf_event_mlock_kb ${debug.perf_event_mlock_kb:-516}
 
 on property:security.perf_harden=1
     write /proc/sys/kernel/perf_event_paranoid 3
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index b03d83b..2f85dec 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,3 +1,5 @@
+firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
+
 subsystem adf
     devname uevent_devname
 
diff --git a/run-as/Android.bp b/run-as/Android.bp
new file mode 100644
index 0000000..840a43c
--- /dev/null
+++ b/run-as/Android.bp
@@ -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.
+//
+
+cc_binary {
+    name: "run-as",
+    srcs: [
+        "run-as.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libselinux",
+        "libpackagelistparser",
+        "libminijail",
+    ],
+}
diff --git a/run-as/Android.mk b/run-as/Android.mk
deleted file mode 100644
index 7111fbe..0000000
--- a/run-as/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_MODULE := run-as
-LOCAL_SHARED_LIBRARIES := libselinux libpackagelistparser libminijail
-LOCAL_SRC_FILES := run-as.cpp
-include $(BUILD_EXECUTABLE)
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index b27cfad..d005ecf 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -28,6 +28,7 @@
 #include <libminijail.h>
 #include <scoped_minijail.h>
 
+#include <android-base/properties.h>
 #include <packagelistparser/packagelistparser.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
@@ -40,6 +41,7 @@
 //  The 'run-as' binary is installed with CAP_SETUID and CAP_SETGID file
 //  capabilities, but will check the following:
 //
+//  - that the ro.boot.disable_runas property is not set
 //  - that it is invoked from the 'shell' or 'root' user (abort otherwise)
 //  - that '<package-name>' is the name of an installed and debuggable package
 //  - that the package's data directory is well-formed
@@ -139,6 +141,12 @@
     error(1, 0, "only 'shell' or 'root' users can run this program");
   }
 
+  // Some devices can disable running run-as, such as Chrome OS when running in
+  // non-developer mode.
+  if (android::base::GetBoolProperty("ro.boot.disable_runas", false)) {
+      error(1, 0, "run-as is disabled from the kernel commandline");
+  }
+
   char* pkgname = argv[1];
   int cmd_argv_offset = 2;
 
diff --git a/trusty/OWNERS b/trusty/OWNERS
index 357b4f4..e807d71 100644
--- a/trusty/OWNERS
+++ b/trusty/OWNERS
@@ -1,3 +1,7 @@
-bohr@google.com
-swillden@google.com
+arve@google.com
 dkrahn@google.com
+drewry@google.com
+gmar@google.com
+ncbray@google.com
+rpere@google.com
+swillden@google.com