Merge "Revert "Mark libmetricslogger vendor_available.""
diff --git a/adb/Android.bp b/adb/Android.bp
new file mode 100644
index 0000000..41f7b89
--- /dev/null
+++ b/adb/Android.bp
@@ -0,0 +1,51 @@
+// Copyright (C) 2017 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.
+
+python_binary_host {
+  name: "adb_integration_test_adb",
+  main: "test_adb.py",
+  srcs: [
+    "test_adb.py",
+  ],
+  libs: [
+    "adb_py",
+  ],
+  version: {
+    py2: {
+      enabled: true,
+    },
+    py3: {
+      enabled: false,
+    },
+  },
+}
+
+python_binary_host {
+  name: "adb_integration_test_device",
+  main: "test_device.py",
+  srcs: [
+    "test_device.py",
+  ],
+  libs: [
+    "adb_py",
+  ],
+  version: {
+    py2: {
+      enabled: true,
+    },
+    py3: {
+      enabled: false,
+    },
+  },
+}
diff --git a/adb/Android.mk b/adb/Android.mk
index 382c8cb..1f6f194 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -387,4 +387,9 @@
 
 include $(BUILD_EXECUTABLE)
 
+# adb integration test
+# =========================================================
+$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_adb.BUILT))
+$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_device.BUILT))
+
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adb/adb.cpp b/adb/adb.cpp
index a7706a0..ff7b71f 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -49,6 +49,7 @@
 #include "adb_auth.h"
 #include "adb_io.h"
 #include "adb_listeners.h"
+#include "adb_unique_fd.h"
 #include "adb_utils.h"
 #include "sysdeps/chrono.h"
 #include "transport.h"
@@ -656,6 +657,26 @@
 
 #endif
 
+static void ReportServerStartupFailure(pid_t pid) {
+    fprintf(stderr, "ADB server didn't ACK\n");
+    fprintf(stderr, "Full server startup log: %s\n", GetLogFilePath().c_str());
+    fprintf(stderr, "Server had pid: %d\n", pid);
+
+    unique_fd fd(adb_open(GetLogFilePath().c_str(), O_RDONLY));
+    if (fd == -1) return;
+
+    // Let's not show more than 128KiB of log...
+    adb_lseek(fd, -128 * 1024, SEEK_END);
+    std::string content;
+    if (!android::base::ReadFdToString(fd, &content)) return;
+
+    std::string header = android::base::StringPrintf("--- adb starting (pid %d) ---", pid);
+    std::vector<std::string> lines = android::base::Split(content, "\n");
+    int i = lines.size() - 1;
+    while (i >= 0 && lines[i] != header) --i;
+    while (static_cast<size_t>(i) < lines.size()) fprintf(stderr, "%s\n", lines[i++].c_str());
+}
+
 int launch_server(const std::string& socket_spec) {
 #if defined(_WIN32)
     /* we need to start the server in the background                    */
@@ -835,7 +856,8 @@
                 memcmp(temp, expected, expected_length) == 0) {
                 got_ack = true;
             } else {
-                fprintf(stderr, "ADB server didn't ACK\n");
+                ReportServerStartupFailure(GetProcessId(process_handle.get()));
+                return -1;
             }
         } else {
             const DWORD err = GetLastError();
@@ -909,12 +931,9 @@
                            "--reply-fd", reply_fd, NULL);
         // this should not return
         fprintf(stderr, "adb: execl returned %d: %s\n", result, strerror(errno));
-    } else  {
+    } else {
         // parent side of the fork
-
-        char  temp[3];
-
-        temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C';
+        char temp[3] = {};
         // wait for the "OK\n" message
         adb_close(fd[1]);
         int ret = adb_read(fd[0], temp, 3);
@@ -925,7 +944,7 @@
             return -1;
         }
         if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
-            fprintf(stderr, "ADB server didn't ACK\n" );
+            ReportServerStartupFailure(pid);
             return -1;
         }
     }
@@ -1257,6 +1276,10 @@
 void adb_notify_device_scan_complete() {
     {
         std::lock_guard<std::mutex> lock(init_mutex);
+        if (device_scan_complete) {
+            return;
+        }
+
         device_scan_complete = true;
     }
 
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index f5d0f02..e533a00 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -125,7 +125,7 @@
 
 static int _adb_connect(const std::string& service, std::string* error) {
     D("_adb_connect: %s", service.c_str());
-    if (service.empty() || service.size() > MAX_PAYLOAD_V1) {
+    if (service.empty() || service.size() > MAX_PAYLOAD) {
         *error = android::base::StringPrintf("bad service name length (%zd)",
                                              service.size());
         return -1;
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index ca8729e..38e3116 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -31,7 +31,7 @@
 
 bool SendProtocolString(int fd, const std::string& s) {
     unsigned int length = s.size();
-    if (length > MAX_PAYLOAD_V1 - 4) {
+    if (length > MAX_PAYLOAD - 4) {
         errno = EMSGSIZE;
         return false;
     }
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 6f2403d..b236fb3 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -157,7 +157,12 @@
 }
 
 std::string dump_hex(const void* data, size_t byte_count) {
-    byte_count = std::min(byte_count, size_t(16));
+    size_t truncate_len = 16;
+    bool truncated = false;
+    if (byte_count > truncate_len) {
+        byte_count = truncate_len;
+        truncated = true;
+    }
 
     const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
 
@@ -172,6 +177,10 @@
         line.push_back(isprint(ch) ? ch : '.');
     }
 
+    if (truncated) {
+        line += " [truncated]";
+    }
+
     return line;
 }
 
@@ -278,3 +287,29 @@
     fprintf(stderr, "\n");
     return 1;
 }
+
+std::string GetLogFilePath() {
+#if defined(_WIN32)
+    const char log_name[] = "adb.log";
+    WCHAR temp_path[MAX_PATH];
+
+    // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
+    DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
+    if (nchars >= arraysize(temp_path) || nchars == 0) {
+        // If string truncation or some other error.
+        fatal("cannot retrieve temporary file path: %s\n",
+              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+    }
+
+    std::string temp_path_utf8;
+    if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
+        fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8");
+    }
+
+    return temp_path_utf8 + log_name;
+#else
+    const char* tmp_dir = getenv("TMPDIR");
+    if (tmp_dir == nullptr) tmp_dir = "/tmp";
+    return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
+#endif
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index c1d5549..f764a0e 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -17,7 +17,10 @@
 #ifndef _ADB_UTILS_H_
 #define _ADB_UTILS_H_
 
+#include <condition_variable>
+#include <mutex>
 #include <string>
+#include <vector>
 
 #include <android-base/macros.h>
 
@@ -53,4 +56,39 @@
 bool forward_targets_are_valid(const std::string& source, const std::string& dest,
                                std::string* error);
 
+// A thread-safe blocking queue.
+template <typename T>
+class BlockingQueue {
+    std::mutex mutex;
+    std::condition_variable cv;
+    std::vector<T> queue;
+
+  public:
+    void Push(const T& t) {
+        {
+            std::unique_lock<std::mutex> lock(mutex);
+            queue.push_back(t);
+        }
+        cv.notify_one();
+    }
+
+    template <typename Fn>
+    void PopAll(Fn fn) {
+        std::vector<T> popped;
+
+        {
+            std::unique_lock<std::mutex> lock(mutex);
+            cv.wait(lock, [this]() { return !queue.empty(); });
+            popped = std::move(queue);
+            queue.clear();
+        }
+
+        for (const T& t : popped) {
+            fn(t);
+        }
+    }
+};
+
+std::string GetLogFilePath();
+
 #endif
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 62798cd..f0d0ce7 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -39,33 +39,7 @@
 #include "sysdeps/chrono.h"
 #include "transport.h"
 
-static std::string GetLogFilePath() {
-#if defined(_WIN32)
-    const char log_name[] = "adb.log";
-    WCHAR temp_path[MAX_PATH];
-
-    // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
-    DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
-    if (nchars >= arraysize(temp_path) || nchars == 0) {
-        // If string truncation or some other error.
-        fatal("cannot retrieve temporary file path: %s\n",
-              android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    }
-
-    std::string temp_path_utf8;
-    if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
-        fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8");
-    }
-
-    return temp_path_utf8 + log_name;
-#else
-    const char* tmp_dir = getenv("TMPDIR");
-    if (tmp_dir == nullptr) tmp_dir = "/tmp";
-    return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
-#endif
-}
-
-static void setup_daemon_logging(void) {
+static void setup_daemon_logging() {
     const std::string log_file_path(GetLogFilePath());
     int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0640);
     if (fd == -1) {
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index fc32469..e7f44c6 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -37,6 +37,7 @@
 #include <android-base/strings.h>
 
 #include "adb.h"
+#include "adb_utils.h"
 #include "transport.h"
 #include "usb.h"
 
@@ -179,10 +180,6 @@
     if (port_count < 0) return "";
     return StringPrintf("/dev/bus/usb/%03u/%03u", libusb_get_bus_number(device), ports[0]);
 }
-
-static bool is_device_accessible(libusb_device* device) {
-    return access(get_device_dev_path(device).c_str(), R_OK | W_OK) == 0;
-}
 #endif
 
 static bool endpoint_is_output(uint8_t endpoint) {
@@ -376,9 +373,10 @@
     {
         std::unique_lock<std::mutex> lock(usb_handles_mutex);
         usb_handles[device_address] = std::move(result);
-    }
 
-    register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(), writable);
+        register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(),
+                               writable);
+    }
     LOG(INFO) << "registered new usb device '" << device_serial << "'";
 }
 
@@ -389,20 +387,15 @@
     // Android's host linux libusb uses netlink instead of udev for device hotplug notification,
     // which means we can get hotplug notifications before udev has updated ownership/perms on the
     // device. Since we're not going to be able to link against the system's libudev any time soon,
-    // hack around this by checking for accessibility in a loop.
-    ++connecting_devices;
+    // hack around this by inserting a sleep.
     auto thread = std::thread([device]() {
         std::string device_path = get_device_dev_path(device);
-        auto start = std::chrono::steady_clock::now();
-        while (std::chrono::steady_clock::now() - start < 500ms) {
-            if (is_device_accessible(device)) {
-                break;
-            }
-            std::this_thread::sleep_for(10ms);
-        }
+        std::this_thread::sleep_for(1s);
 
         process_device(device);
-        --connecting_devices;
+        if (--connecting_devices == 0) {
+            adb_notify_device_scan_complete();
+        }
     });
     thread.detach();
 #else
@@ -420,18 +413,40 @@
         if (!it->second->device_handle) {
             // If the handle is null, we were never able to open the device.
             unregister_usb_transport(it->second.get());
+            usb_handles.erase(it);
+        } else {
+            // Closure of the transport will erase the usb_handle.
         }
-        usb_handles.erase(it);
+    }
+}
+
+static auto& hotplug_queue = *new BlockingQueue<std::pair<libusb_hotplug_event, libusb_device*>>();
+static void hotplug_thread() {
+    adb_thread_setname("libusb hotplug");
+    while (true) {
+        hotplug_queue.PopAll([](std::pair<libusb_hotplug_event, libusb_device*> pair) {
+            libusb_hotplug_event event = pair.first;
+            libusb_device* device = pair.second;
+            if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
+                device_connected(device);
+            } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
+                device_disconnected(device);
+            }
+        });
     }
 }
 
 static int hotplug_callback(libusb_context*, libusb_device* device, libusb_hotplug_event event,
                             void*) {
+    // We're called with the libusb lock taken. Call these on a separate thread outside of this
+    // function so that the usb_handle mutex is always taken before the libusb mutex.
+    static std::once_flag once;
+    std::call_once(once, []() { std::thread(hotplug_thread).detach(); });
+
     if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
-        device_connected(device);
-    } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
-        device_disconnected(device);
+        ++connecting_devices;
     }
+    hotplug_queue.Push({event, device});
     return 0;
 }
 
@@ -453,13 +468,6 @@
         LOG(FATAL) << "failed to register libusb hotplug callback";
     }
 
-    // Wait for all of the connecting devices to finish.
-    while (connecting_devices != 0) {
-        std::this_thread::sleep_for(10ms);
-    }
-
-    adb_notify_device_scan_complete();
-
     // Spawn a thread for libusb_handle_events.
     std::thread([]() {
         adb_thread_setname("libusb");
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 68ae4af..c9f1ee9 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -599,6 +599,13 @@
     std::string service_string = ShellServiceString(use_shell_protocol,
                                                     type_arg, command);
 
+    // Old devices can't handle a service string that's longer than MAX_PAYLOAD_V1.
+    // Use |use_shell_protocol| to determine whether to allow a command longer than that.
+    if (service_string.size() > MAX_PAYLOAD_V1 && !use_shell_protocol) {
+        fprintf(stderr, "error: shell command too long\n");
+        return 1;
+    }
+
     // Make local stdin raw if the device allocates a PTY, which happens if:
     //   1. We are explicitly asking for a PTY shell, or
     //   2. We don't specify shell type and are starting an interactive session.
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 14ad1ff..e0143c6 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -686,7 +686,7 @@
     }
 
     len = unhex(p->data, 4);
-    if ((len < 1) || (len > MAX_PAYLOAD_V1)) {
+    if ((len < 1) || (len > MAX_PAYLOAD)) {
         D("SS(%d): bad size (%d)", s->id, len);
         goto fail;
     }
diff --git a/adb/test_device.py b/adb/test_device.py
index 737d0c2..9e1a2ec 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -342,6 +342,13 @@
         out = self.device.shell(['echo', 'foo'])[0]
         self.assertEqual(out, 'foo' + self.device.linesep)
 
+    def test_shell_command_length(self):
+        # Devices that have shell_v2 should be able to handle long commands.
+        if self.device.has_shell_protocol():
+            rc, out, err = self.device.shell_nocheck(['echo', 'x' * 16384])
+            self.assertEqual(rc, 0)
+            self.assertTrue(out == ('x' * 16384 + '\n'))
+
     def test_shell_nocheck_failure(self):
         rc, out, _ = self.device.shell_nocheck(['false'])
         self.assertNotEqual(rc, 0)
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 308ee8d..2bbbefd 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -603,15 +603,15 @@
 static void transport_unref(atransport* t) {
     CHECK(t != nullptr);
 
-    std::lock_guard<std::mutex> lock(transport_lock);
-    CHECK_GT(t->ref_count, 0u);
-    t->ref_count--;
-    if (t->ref_count == 0) {
+    size_t old_refcount = t->ref_count--;
+    CHECK_GT(old_refcount, 0u);
+
+    if (old_refcount == 1u) {
         D("transport: %s unref (kicking and closing)", t->serial);
         t->close(t);
         remove_transport(t);
     } else {
-        D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
+        D("transport: %s unref (count=%zu)", t->serial, old_refcount - 1);
     }
 }
 
diff --git a/adb/transport.h b/adb/transport.h
index 57fc988..4a89ed9 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -61,7 +61,7 @@
     // class in one go is a very large change. Given how bad our testing is,
     // it's better to do this piece by piece.
 
-    atransport(ConnectionState state = kCsOffline) : connection_state_(state) {
+    atransport(ConnectionState state = kCsOffline) : ref_count(0), connection_state_(state) {
         transport_fde = {};
         protocol_version = A_VERSION;
         max_payload = MAX_PAYLOAD;
@@ -88,7 +88,7 @@
     int fd = -1;
     int transport_socket = -1;
     fdevent transport_fde;
-    size_t ref_count = 0;
+    std::atomic<size_t> ref_count;
     uint32_t sync_token = 0;
     bool online = false;
     TransportType type = kTransportAny;
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index a9e583f..6cedd92 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -388,6 +388,25 @@
     D("transport: qemu_socket_thread() exiting");
     return;
 }
+
+// If adbd is running inside the emulator, it will normally use QEMUD pipe (aka
+// goldfish) as the transport. This can either be explicitly set by the
+// service.adb.transport property, or be inferred from ro.kernel.qemu that is
+// set to "1" for ranchu/goldfish.
+static bool use_qemu_goldfish() {
+    // Legacy way to detect if adbd should use the goldfish pipe is to check for
+    // ro.kernel.qemu, keep that behaviour for backward compatibility.
+    if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
+        return true;
+    }
+    // If service.adb.transport is present and is set to "goldfish", use the
+    // QEMUD pipe.
+    if (android::base::GetProperty("service.adb.transport", "") == "goldfish") {
+        return true;
+    }
+    return false;
+}
+
 #endif  // !ADB_HOST
 
 void local_init(int port)
@@ -401,13 +420,7 @@
 #else
     // For the adbd daemon in the system image we need to distinguish
     // between the device, and the emulator.
-    if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
-        // Running inside the emulator: use QEMUD pipe as the transport.
-        func = qemu_socket_thread;
-    } else {
-        // Running inside the device: use TCP socket as the transport.
-        func = server_socket_thread;
-    }
+    func = use_qemu_goldfish() ? qemu_socket_thread : server_socket_thread;
     debug_name = "server";
 #endif // !ADB_HOST
 
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 47094b8..2f46920 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -192,7 +192,7 @@
 #if defined(_WIN32) || !ADB_HOST
     return false;
 #else
-    static bool disable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "0") == 0;
-    return !disable;
+    static bool enable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "1") == 0;
+    return enable;
 #endif
 }
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp
index 5eedf3b..b6bf701 100644
--- a/base/chrono_utils.cpp
+++ b/base/chrono_utils.cpp
@@ -33,5 +33,10 @@
 #endif  // __ANDROID__
 }
 
+std::ostream& operator<<(std::ostream& os, const Timer& t) {
+  os << t.duration().count() << "ms";
+  return os;
+}
+
 }  // namespace base
 }  // namespace android
diff --git a/base/chrono_utils_test.cpp b/base/chrono_utils_test.cpp
index 057132d..da442f4 100644
--- a/base/chrono_utils_test.cpp
+++ b/base/chrono_utils_test.cpp
@@ -19,6 +19,9 @@
 #include <time.h>
 
 #include <chrono>
+#include <sstream>
+#include <string>
+#include <thread>
 
 #include <gtest/gtest.h>
 
@@ -42,5 +45,36 @@
   EXPECT_EQ(now, boot_seconds);
 }
 
+template <typename T>
+void ExpectAboutEqual(T expected, T actual) {
+  auto expected_upper_bound = expected * 1.05f;
+  auto expected_lower_bound = expected * .95;
+  EXPECT_GT(expected_upper_bound, actual);
+  EXPECT_LT(expected_lower_bound, actual);
+}
+
+TEST(ChronoUtilsTest, TimerDurationIsSane) {
+  auto start = boot_clock::now();
+  Timer t;
+  std::this_thread::sleep_for(50ms);
+  auto stop = boot_clock::now();
+  auto stop_timer = t.duration();
+
+  auto expected = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
+  ExpectAboutEqual(expected, stop_timer);
+}
+
+TEST(ChronoUtilsTest, TimerOstream) {
+  Timer t;
+  std::this_thread::sleep_for(50ms);
+  auto stop_timer = t.duration().count();
+  std::stringstream os;
+  os << t;
+  decltype(stop_timer) stop_timer_from_stream;
+  os >> stop_timer_from_stream;
+  EXPECT_NE(0, stop_timer);
+  ExpectAboutEqual(stop_timer, stop_timer_from_stream);
+}
+
 }  // namespace base
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
index 0086425..7679d4c 100644
--- a/base/include/android-base/chrono_utils.h
+++ b/base/include/android-base/chrono_utils.h
@@ -18,6 +18,9 @@
 #define ANDROID_BASE_CHRONO_UTILS_H
 
 #include <chrono>
+#include <sstream>
+
+using namespace std::chrono_literals;
 
 namespace android {
 namespace base {
@@ -31,6 +34,20 @@
   static time_point now();
 };
 
+class Timer {
+ public:
+  Timer() : start_(boot_clock::now()) {}
+
+  std::chrono::milliseconds duration() const {
+    return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_);
+  }
+
+ private:
+  boot_clock::time_point start_;
+};
+
+std::ostream& operator<<(std::ostream& os, const Timer& t);
+
 }  // namespace base
 }  // namespace android
 
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index fa0d922..548b286 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -176,15 +176,19 @@
 // Provides an expression that evaluates to the truthiness of `x`, automatically
 // aborting if `c` is true.
 #define ABORT_AFTER_LOG_EXPR_IF(c, x) (((c) && ::android::base::LogAbortAfterFullExpr()) || (x))
+// Note to the static analyzer that we always execute FATAL logs in practice.
+#define MUST_LOG_MESSAGE(severity) (SEVERITY_LAMBDA(severity) == ::android::base::FATAL)
 #else
 #define ABORT_AFTER_LOG_FATAL
 #define ABORT_AFTER_LOG_EXPR_IF(c, x) (x)
+#define MUST_LOG_MESSAGE(severity) false
 #endif
 #define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x)
 
 // Defines whether the given severity will be logged or silently swallowed.
 #define WOULD_LOG(severity) \
-  UNLIKELY((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity())
+  (UNLIKELY((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity()) || \
+   MUST_LOG_MESSAGE(severity))
 
 // Get an ostream that can be used for logging at the given severity and to the default
 // destination.
@@ -315,7 +319,7 @@
 // DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
 // CHECK should be used unless profiling identifies a CHECK as being in
 // performance critical code.
-#if defined(NDEBUG)
+#if defined(NDEBUG) && !defined(__clang_analyzer__)
 static constexpr bool kEnableDChecks = false;
 #else
 static constexpr bool kEnableDChecks = true;
@@ -339,7 +343,7 @@
   if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
 #define DCHECK_STRNE(s1, s2) \
   if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
-#if defined(NDEBUG)
+#if defined(NDEBUG) && !defined(__clang_analyzer__)
 #define DCHECK_CONSTEXPR(x, out, dummy)
 #else
 #define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
index 2d5a6f6..c9cc1ab 100755
--- a/base/include/android-base/utf8.h
+++ b/base/include/android-base/utf8.h
@@ -22,6 +22,8 @@
 #else
 // Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
 #include <fcntl.h>      // open
+#include <stdio.h>      // fopen
+#include <sys/stat.h>   // mkdir
 #include <unistd.h>     // unlink
 #endif
 
@@ -53,6 +55,19 @@
 // Convert a UTF-8 std::string (including any embedded NULL characters) to
 // UTF-16. Returns whether the conversion was done successfully.
 bool UTF8ToWide(const std::string& utf8, std::wstring* utf16);
+
+// Convert a file system path, represented as a NULL-terminated string of
+// UTF-8 characters, to a UTF-16 string representing the same file system
+// path using the Windows extended-lengh path representation.
+//
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#MAXPATH:
+//   ```The Windows API has many functions that also have Unicode versions to
+//   permit an extended-length path for a maximum total path length of 32,767
+//   characters. To specify an extended-length path, use the "\\?\" prefix.
+//   For example, "\\?\D:\very long path".```
+//
+// Returns whether the conversion was done successfully.
+bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16);
 #endif
 
 // The functions in the utf8 namespace take UTF-8 strings. For Windows, these
@@ -73,9 +88,13 @@
 namespace utf8 {
 
 #ifdef _WIN32
+FILE* fopen(const char* name, const char* mode);
+int mkdir(const char* name, mode_t mode);
 int open(const char* name, int flags, ...);
 int unlink(const char* name);
 #else
+using ::fopen;
+using ::mkdir;
 using ::open;
 using ::unlink;
 #endif
diff --git a/base/utf8.cpp b/base/utf8.cpp
index 3cca700..5984fb0 100644
--- a/base/utf8.cpp
+++ b/base/utf8.cpp
@@ -19,7 +19,9 @@
 #include "android-base/utf8.h"
 
 #include <fcntl.h>
+#include <stdio.h>
 
+#include <algorithm>
 #include <string>
 
 #include "android-base/logging.h"
@@ -153,12 +155,58 @@
   return UTF8ToWide(utf8.c_str(), utf8.length(), utf16);
 }
 
+static bool isDriveLetter(wchar_t c) {
+  return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z');
+}
+
+bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16) {
+  if (!UTF8ToWide(utf8, utf16)) {
+    return false;
+  }
+  // Note: Although most Win32 File I/O API are limited to MAX_PATH (260
+  //       characters), the CreateDirectory API is limited to 248 characters.
+  if (utf16->length() >= 248) {
+    // If path is of the form "x:\" or "x:/"
+    if (isDriveLetter((*utf16)[0]) && (*utf16)[1] == L':' &&
+        ((*utf16)[2] == L'\\' || (*utf16)[2] == L'/')) {
+      // Append long path prefix, and make sure there are no unix-style
+      // separators to ensure a fully compliant Win32 long path string.
+      utf16->insert(0, LR"(\\?\)");
+      std::replace(utf16->begin(), utf16->end(), L'/', L'\\');
+    }
+  }
+  return true;
+}
+
 // Versions of standard library APIs that support UTF-8 strings.
 namespace utf8 {
 
+FILE* fopen(const char* name, const char* mode) {
+  std::wstring name_utf16;
+  if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
+    return nullptr;
+  }
+
+  std::wstring mode_utf16;
+  if (!UTF8ToWide(mode, &mode_utf16)) {
+    return nullptr;
+  }
+
+  return _wfopen(name_utf16.c_str(), mode_utf16.c_str());
+}
+
+int mkdir(const char* name, mode_t mode) {
+  std::wstring name_utf16;
+  if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
+    return -1;
+  }
+
+  return _wmkdir(name_utf16.c_str());
+}
+
 int open(const char* name, int flags, ...) {
   std::wstring name_utf16;
-  if (!UTF8ToWide(name, &name_utf16)) {
+  if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
     return -1;
   }
 
@@ -175,7 +223,7 @@
 
 int unlink(const char* name) {
   std::wstring name_utf16;
-  if (!UTF8ToWide(name, &name_utf16)) {
+  if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
     return -1;
   }
 
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
index ae8fc8c..fcb25c3 100644
--- a/base/utf8_test.cpp
+++ b/base/utf8_test.cpp
@@ -18,7 +18,12 @@
 
 #include <gtest/gtest.h>
 
+#include <fcntl.h>
+#include <stdlib.h>
+
 #include "android-base/macros.h"
+#include "android-base/test_utils.h"
+#include "android-base/unique_fd.h"
 
 namespace android {
 namespace base {
@@ -408,5 +413,76 @@
   EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null));
 }
 
+TEST(UTF8PathToWindowsLongPathTest, DontAddPrefixIfShorterThanMaxPath) {
+  std::string utf8 = "c:\\mypath\\myfile.txt";
+
+  std::wstring wide;
+  EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
+
+  EXPECT_EQ(std::string::npos, wide.find(LR"(\\?\)"));
+}
+
+TEST(UTF8PathToWindowsLongPathTest, AddPrefixIfLongerThanMaxPath) {
+  std::string utf8 = "c:\\mypath";
+  while (utf8.length() < 300 /* MAX_PATH is 260 */) {
+    utf8 += "\\mypathsegment";
+  }
+
+  std::wstring wide;
+  EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
+
+  EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
+  EXPECT_EQ(std::string::npos, wide.find(L"/"));
+}
+
+TEST(UTF8PathToWindowsLongPathTest, AddPrefixAndFixSeparatorsIfLongerThanMaxPath) {
+  std::string utf8 = "c:/mypath";
+  while (utf8.length() < 300 /* MAX_PATH is 260 */) {
+    utf8 += "/mypathsegment";
+  }
+
+  std::wstring wide;
+  EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
+
+  EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
+  EXPECT_EQ(std::string::npos, wide.find(L"/"));
+}
+
+namespace utf8 {
+
+TEST(Utf8FilesTest, CanCreateOpenAndDeleteFileWithLongPath) {
+  TemporaryDir td;
+
+  // Create long directory path
+  std::string utf8 = td.path;
+  while (utf8.length() < 300 /* MAX_PATH is 260 */) {
+    utf8 += "\\mypathsegment";
+    EXPECT_EQ(0, mkdir(utf8.c_str(), 0));
+  }
+
+  // Create file
+  utf8 += "\\test-file.bin";
+  int flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
+  int mode = 0666;
+  android::base::unique_fd fd(open(utf8.c_str(), flags, mode));
+  EXPECT_NE(-1, fd.get());
+
+  // Close file
+  fd.reset();
+  EXPECT_EQ(-1, fd.get());
+
+  // Open file with fopen
+  FILE* file = fopen(utf8.c_str(), "rb");
+  EXPECT_NE(nullptr, file);
+
+  if (file) {
+    fclose(file);
+  }
+
+  // Delete file
+  EXPECT_EQ(0, unlink(utf8.c_str()));
+}
+
+}  // namespace utf8
 }  // namespace base
 }  // namespace android
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index d4e215e..344fa9a 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -85,12 +85,13 @@
   fprintf(stderr, "Usage: %s [options]\n", cmd);
   fprintf(stderr,
           "options include:\n"
-          "  -h, --help            Show this help\n"
-          "  -l, --log             Log all metrics to logstorage\n"
-          "  -p, --print           Dump the boot event records to the console\n"
-          "  -r, --record          Record the timestamp of a named boot event\n"
-          "  --value               Optional value to associate with the boot event\n"
-          "  --record_boot_reason  Record the reason why the device booted\n"
+          "  -h, --help              Show this help\n"
+          "  -l, --log               Log all metrics to logstorage\n"
+          "  -p, --print             Dump the boot event records to the console\n"
+          "  -r, --record            Record the timestamp of a named boot event\n"
+          "  --value                 Optional value to associate with the boot event\n"
+          "  --record_boot_complete  Record metrics related to the time for the device boot\n"
+          "  --record_boot_reason    Record the reason why the device booted\n"
           "  --record_time_since_factory_reset Record the time since the device was reset\n");
 }
 
@@ -201,8 +202,10 @@
 
   BootEventRecordStore boot_event_store;
   BootEventRecordStore::BootEventRecord record;
-  if (!boot_event_store.GetBootEvent(kBuildDateKey, &record) ||
-      build_date != record.second) {
+  if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) {
+    boot_complete_prefix = "factory_reset_" + boot_complete_prefix;
+    boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
+  } else if (build_date != record.second) {
     boot_complete_prefix = "ota_" + boot_complete_prefix;
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
   }
@@ -241,7 +244,7 @@
   for (const auto& stageTiming : stages) {
     // |stageTiming| is of the form 'stage:time'.
     auto stageTimingValues = android::base::Split(stageTiming, ":");
-    DCHECK_EQ(2, stageTimingValues.size());
+    DCHECK_EQ(2U, stageTimingValues.size());
 
     std::string stageName = stageTimingValues[0];
     int32_t time_ms;
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 3a80b50..f86aaa0 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -11,6 +11,11 @@
     local_include_dirs: ["include"],
 }
 
+cc_library_headers {
+    name: "libdebuggerd_common_headers",
+    export_include_dirs: ["common/include"]
+}
+
 cc_library_shared {
     name: "libtombstoned_client",
     defaults: ["debuggerd_defaults"],
@@ -19,15 +24,18 @@
         "util.cpp",
     ],
 
+    header_libs: ["libdebuggerd_common_headers"],
+
     static_libs: [
-        "libasync_safe"
+        "libasync_safe",
     ],
 
     shared_libs: [
-        "libcutils",
         "libbase",
+        "libcutils",
     ],
 
+    export_header_lib_headers: ["libdebuggerd_common_headers"],
     export_include_dirs: ["tombstoned/include"]
 }
 
@@ -40,12 +48,15 @@
         "util.cpp",
     ],
 
+    header_libs: ["libdebuggerd_common_headers"],
+
     whole_static_libs: [
         "libasync_safe",
         "libcutils",
         "libbase",
     ],
 
+    export_header_lib_headers: ["libdebuggerd_common_headers"],
     export_include_dirs: ["tombstoned/include"]
 }
 
@@ -55,11 +66,14 @@
     defaults: ["debuggerd_defaults"],
     srcs: ["handler/debuggerd_handler.cpp"],
 
+    header_libs: ["libdebuggerd_common_headers"],
+
     whole_static_libs: [
         "libasync_safe",
         "libdebuggerd",
     ],
 
+    export_header_lib_headers: ["libdebuggerd_common_headers"],
     export_include_dirs: ["include"],
 }
 
@@ -107,11 +121,14 @@
         "util.cpp",
     ],
 
+    header_libs: ["libdebuggerd_common_headers"],
+
     shared_libs: [
         "libbase",
         "libcutils",
     ],
 
+    export_header_lib_headers: ["libdebuggerd_common_headers"],
     export_include_dirs: ["include"],
 }
 
@@ -191,7 +208,8 @@
         "libbase",
         "libcutils",
         "libdebuggerd_client",
-        "liblog"
+        "liblog",
+        "libnativehelper"
     ],
 
     static_libs: [
@@ -256,7 +274,7 @@
         "libbase",
         "libdebuggerd_client",
         "liblog",
-        "libselinux",
+        "libprocinfo",
     ],
 
     local_include_dirs: ["include"],
@@ -271,6 +289,8 @@
     ],
     defaults: ["debuggerd_defaults"],
 
+    header_libs: ["libdebuggerd_common_headers"],
+
     static_libs: [
         "libbase",
         "libcutils",
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 4ce038c..cb7cbbe 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -28,7 +28,9 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 
@@ -40,10 +42,12 @@
 
 using android::base::unique_fd;
 
-static bool send_signal(pid_t pid, bool backtrace) {
+static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
+  const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : DEBUGGER_SIGNAL;
   sigval val;
-  val.sival_int = backtrace;
-  if (sigqueue(pid, DEBUGGER_SIGNAL, val) != 0) {
+  val.sival_int = (dump_type == kDebuggerdNativeBacktrace) ? 1 : 0;
+
+  if (sigqueue(pid, signal, val) != 0) {
     PLOG(ERROR) << "libdebuggerd_client: failed to send signal to pid " << pid;
     return false;
   }
@@ -58,8 +62,8 @@
   tv->tv_usec = static_cast<long>(microseconds.count());
 }
 
-bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
-                            unsigned int timeout_ms) {
+bool debuggerd_trigger_dump(pid_t pid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
+                            unique_fd output_fd) {
   LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
   unique_fd sockfd;
   const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
@@ -102,7 +106,7 @@
     return false;
   }
 
-  InterceptRequest req = {.pid = pid };
+  InterceptRequest req = {.pid = pid, .dump_type = dump_type};
   if (!set_timeout(sockfd)) {
     PLOG(ERROR) << "libdebugger_client: failed to set timeout";
     return false;
@@ -115,6 +119,20 @@
     return false;
   }
 
+  std::string pipe_size_str;
+  int pipe_buffer_size = 1024 * 1024;
+  if (android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
+    pipe_size_str = android::base::Trim(pipe_size_str);
+
+    if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
+      LOG(FATAL) << "failed to parse pipe max size '" << pipe_size_str << "'";
+    }
+  }
+
+  if (fcntl(pipe_read.get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
+    PLOG(ERROR) << "failed to set pipe buffer size";
+  }
+
   if (send_fd(set_timeout(sockfd), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
     PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
     return false;
@@ -140,8 +158,7 @@
     return false;
   }
 
-  bool backtrace = dump_type == kDebuggerdBacktrace;
-  if (!send_signal(pid, backtrace)) {
+  if (!send_signal(pid, dump_type)) {
     return false;
   }
 
@@ -210,15 +227,16 @@
   return true;
 }
 
-int dump_backtrace_to_file(pid_t tid, int fd) {
-  return dump_backtrace_to_file_timeout(tid, fd, 0);
+int dump_backtrace_to_file(pid_t tid, DebuggerdDumpType dump_type, int fd) {
+  return dump_backtrace_to_file_timeout(tid, dump_type, 0, fd);
 }
 
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
+int dump_backtrace_to_file_timeout(pid_t tid, DebuggerdDumpType dump_type, int timeout_secs,
+                                   int fd) {
   android::base::unique_fd copy(dup(fd));
   if (copy == -1) {
     return -1;
   }
   int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
-  return debuggerd_trigger_dump(tid, std::move(copy), kDebuggerdBacktrace, timeout_ms) ? 0 : -1;
+  return debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
 }
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index 8f97db1..9c2f0d6 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -27,6 +27,7 @@
 #include <gtest/gtest.h>
 
 #include <android-base/file.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -36,8 +37,20 @@
 using namespace std::chrono_literals;
 using android::base::unique_fd;
 
+static int getThreadCount() {
+  int threadCount = 1024;
+  std::vector<std::string> characteristics =
+      android::base::Split(android::base::GetProperty("ro.build.characteristics", ""), ",");
+  if (std::find(characteristics.begin(), characteristics.end(), "embedded")
+      != characteristics.end()) {
+    // 128 is the realistic number for iot devices.
+    threadCount = 128;
+  }
+  return threadCount;
+}
+
 TEST(debuggerd_client, race) {
-  static constexpr int THREAD_COUNT = 1024;
+  static int THREAD_COUNT = getThreadCount();
   pid_t forkpid = fork();
 
   ASSERT_NE(-1, forkpid);
@@ -67,7 +80,8 @@
   // Wait for a bit to let the child spawn all of its threads.
   std::this_thread::sleep_for(250ms);
 
-  ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(pipe_write), kDebuggerdBacktrace, 10000));
+  ASSERT_TRUE(
+      debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 10000, std::move(pipe_write)));
   // Immediately kill the forked child, to make sure that the dump didn't return early.
   ASSERT_EQ(0, kill(forkpid, SIGKILL)) << strerror(errno);
 
@@ -107,5 +121,6 @@
 
   unique_fd output_read, output_write;
   ASSERT_TRUE(Pipe(&output_read, &output_write));
-  ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(output_write), kDebuggerdBacktrace, 0));
+  ASSERT_TRUE(
+      debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 0, std::move(output_write)));
 }
diff --git a/debuggerd/common/include/dump_type.h b/debuggerd/common/include/dump_type.h
new file mode 100644
index 0000000..203269e
--- /dev/null
+++ b/debuggerd/common/include/dump_type.h
@@ -0,0 +1,49 @@
+#pragma once
+
+/*
+ * Copyright 2017, 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 <sys/types.h>
+
+#include <ostream>
+
+enum DebuggerdDumpType : uint8_t {
+  kDebuggerdNativeBacktrace,
+  kDebuggerdTombstone,
+  kDebuggerdJavaBacktrace,
+  kDebuggerdAnyIntercept
+};
+
+inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) {
+  switch (rhs) {
+    case kDebuggerdNativeBacktrace:
+      stream << "kDebuggerdNativeBacktrace";
+      break;
+    case kDebuggerdTombstone:
+      stream << "kDebuggerdTombstone";
+      break;
+    case kDebuggerdJavaBacktrace:
+      stream << "kDebuggerdJavaBacktrace";
+      break;
+    case kDebuggerdAnyIntercept:
+      stream << "kDebuggerdAnyIntercept";
+      break;
+    default:
+      stream << "[unknown]";
+  }
+
+  return stream;
+}
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index be28079..4b1e51d 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -41,8 +41,12 @@
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 #include <log/log.h>
+#include <private/android_filesystem_config.h>
 #include <procinfo/process.h>
 
+#define ATRACE_TAG ATRACE_TAG_BIONIC
+#include <utils/Trace.h>
+
 #include "backtrace.h"
 #include "tombstone.h"
 #include "utility.h"
@@ -99,8 +103,10 @@
   return true;
 }
 
-static bool activity_manager_notify(int pid, int signal, const std::string& amfd_data) {
-  android::base::unique_fd amfd(socket_local_client("/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));
+static bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data) {
+  ATRACE_CALL();
+  android::base::unique_fd amfd(socket_local_client(
+      "/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));
   if (amfd.get() == -1) {
     PLOG(ERROR) << "unable to connect to activity manager";
     return false;
@@ -150,13 +156,13 @@
   _exit(1);
 }
 
-static void abort_handler(pid_t target, const bool& tombstoned_connected,
+static void abort_handler(pid_t target, const bool tombstoned_connected,
                           unique_fd& tombstoned_socket, unique_fd& output_fd,
                           const char* abort_msg) {
   // If we abort before we get an output fd, contact tombstoned to let any
   // potential listeners know that we failed.
   if (!tombstoned_connected) {
-    if (!tombstoned_connect(target, &tombstoned_socket, &output_fd)) {
+    if (!tombstoned_connect(target, &tombstoned_socket, &output_fd, kDebuggerdAnyIntercept)) {
       // We failed to connect, not much we can do.
       LOG(ERROR) << "failed to connected to tombstoned to report failure";
       _exit(1);
@@ -174,6 +180,7 @@
 }
 
 static void drop_capabilities() {
+  ATRACE_CALL();
   __user_cap_header_struct capheader;
   memset(&capheader, 0, sizeof(capheader));
   capheader.version = _LINUX_CAPABILITY_VERSION_3;
@@ -192,6 +199,8 @@
 }
 
 int main(int argc, char** argv) {
+  atrace_begin(ATRACE_TAG, "before reparent");
+
   pid_t target = getppid();
   bool tombstoned_connected = false;
   unique_fd tombstoned_socket;
@@ -207,12 +216,19 @@
   action.sa_handler = signal_handler;
   debuggerd_register_handlers(&action);
 
-  if (argc != 3) {
-    return 1;
+  sigset_t mask;
+  sigemptyset(&mask);
+  if (sigprocmask(SIG_SETMASK, &mask, nullptr) != 0) {
+    PLOG(FATAL) << "failed to set signal mask";
+  }
+
+  if (argc != 4) {
+    LOG(FATAL) << "Wrong number of args: " << argc << " (expected 4)";
   }
 
   pid_t main_tid;
   pid_t pseudothread_tid;
+  int dump_type;
 
   if (!android::base::ParseInt(argv[1], &main_tid, 1, std::numeric_limits<pid_t>::max())) {
     LOG(FATAL) << "invalid main tid: " << argv[1];
@@ -222,6 +238,10 @@
     LOG(FATAL) << "invalid pseudothread tid: " << argv[2];
   }
 
+  if (!android::base::ParseInt(argv[3], &dump_type, 0, 1)) {
+    LOG(FATAL) << "invalid requested dump type: " << argv[3];
+  }
+
   if (target == 1) {
     LOG(FATAL) << "target died before we could attach (received main tid = " << main_tid << ")";
   }
@@ -248,6 +268,8 @@
     PLOG(FATAL) << "parent died";
   }
 
+  atrace_end(ATRACE_TAG);
+
   // Reparent ourselves to init, so that the signal handler can waitpid on the
   // original process to avoid leaving a zombie for non-fatal dumps.
   pid_t forkpid = fork();
@@ -257,47 +279,63 @@
     exit(0);
   }
 
+  ATRACE_NAME("after reparent");
+
   // Die if we take too long.
-  alarm(20);
+  //
+  // Note: processes with many threads and minidebug-info can take a bit to
+  //       unwind, do not make this too small. b/62828735
+  alarm(5);
 
   std::string attach_error;
 
-  // Seize the main thread.
-  if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
-    LOG(FATAL) << attach_error;
-  }
-
-  // Seize the siblings.
   std::map<pid_t, std::string> threads;
+
   {
-    std::set<pid_t> siblings;
-    if (!android::procinfo::GetProcessTids(target, &siblings)) {
-      PLOG(FATAL) << "failed to get process siblings";
+    ATRACE_NAME("ptrace");
+    // Seize the main thread.
+    if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
+      LOG(FATAL) << attach_error;
     }
 
-    // but not the already attached main thread.
-    siblings.erase(main_tid);
-    // or the handler pseudothread.
-    siblings.erase(pseudothread_tid);
+    // Seize the siblings.
+    {
+      std::set<pid_t> siblings;
+      if (!android::procinfo::GetProcessTids(target, &siblings)) {
+        PLOG(FATAL) << "failed to get process siblings";
+      }
 
-    for (pid_t sibling_tid : siblings) {
-      if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) {
-        LOG(WARNING) << attach_error;
-      } else {
-        threads.emplace(sibling_tid, get_thread_name(sibling_tid));
+      // but not the already attached main thread.
+      siblings.erase(main_tid);
+      // or the handler pseudothread.
+      siblings.erase(pseudothread_tid);
+
+      for (pid_t sibling_tid : siblings) {
+        if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) {
+          LOG(WARNING) << attach_error;
+        } else {
+          threads.emplace(sibling_tid, get_thread_name(sibling_tid));
+        }
       }
     }
   }
 
   // Collect the backtrace map, open files, and process/thread names, while we still have caps.
-  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(main_tid));
-  if (!backtrace_map) {
-    LOG(FATAL) << "failed to create backtrace map";
+  std::unique_ptr<BacktraceMap> backtrace_map;
+  {
+    ATRACE_NAME("backtrace map");
+    backtrace_map.reset(BacktraceMap::Create(main_tid));
+    if (!backtrace_map) {
+      LOG(FATAL) << "failed to create backtrace map";
+    }
   }
 
   // Collect the list of open files.
   OpenFilesList open_files;
-  populate_open_files_list(target, &open_files);
+  {
+    ATRACE_NAME("open files");
+    populate_open_files_list(target, &open_files);
+  }
 
   std::string process_name = get_process_name(main_tid);
   threads.emplace(main_tid, get_thread_name(main_tid));
@@ -305,8 +343,12 @@
   // Drop our capabilities now that we've attached to the threads we care about.
   drop_capabilities();
 
-  LOG(INFO) << "obtaining output fd from tombstoned";
-  tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd);
+  {
+    ATRACE_NAME("tombstoned_connect");
+    const DebuggerdDumpType dump_type_enum = static_cast<DebuggerdDumpType>(dump_type);
+    LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum;
+    tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum);
+  }
 
   // Write a '\1' to stdout to tell the crashing process to resume.
   // It also restores the value of PR_SET_DUMPABLE at this point.
@@ -335,9 +377,12 @@
   }
 
   siginfo_t siginfo = {};
-  if (!wait_for_signal(main_tid, &siginfo)) {
-    printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
-    exit(1);
+  {
+    ATRACE_NAME("wait_for_signal");
+    if (!wait_for_signal(main_tid, &siginfo)) {
+      printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
+      exit(1);
+    }
   }
 
   int signo = siginfo.si_signo;
@@ -359,8 +404,10 @@
 
   std::string amfd_data;
   if (backtrace) {
+    ATRACE_NAME("dump_backtrace");
     dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, process_name, threads, 0);
   } else {
+    ATRACE_NAME("engrave_tombstone");
     engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid,
                       process_name, threads, abort_address, fatal_signal ? &amfd_data : nullptr);
   }
@@ -401,7 +448,10 @@
   }
 
   if (fatal_signal) {
-    activity_manager_notify(target, signo, amfd_data);
+    // Don't try to notify ActivityManager if it just crashed, or we might hang until timeout.
+    if (target_info.name != "system_server" || target_info.uid != AID_SYSTEM) {
+      activity_manager_notify(target, signo, amfd_data);
+    }
   }
 
   // Close stdout before we notify tombstoned of completion.
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 6970201..f57349b 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -134,6 +134,14 @@
     free(buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
 }
 
+noinline void leak() {
+    while (true) {
+        void* mapping =
+            mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+        static_cast<volatile char*>(mapping)[0] = 'a';
+    }
+}
+
 noinline void sigsegv_non_null() {
     int* a = (int *)(&do_action);
     *a = 42;
@@ -160,8 +168,8 @@
     fprintf(stderr, "  stack-overflow        recurse until the stack overflows\n");
     fprintf(stderr, "  nostack               crash with a NULL stack pointer\n");
     fprintf(stderr, "\n");
-    fprintf(stderr, "  heap-corruption       cause a libc abort by corrupting the heap\n");
     fprintf(stderr, "  heap-usage            cause a libc abort by abusing a heap function\n");
+    fprintf(stderr, "  leak                  leak memory until we get OOM-killed\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "  abort                 call abort()\n");
     fprintf(stderr, "  assert                call assert() without a function\n");
@@ -265,6 +273,8 @@
         return pthread_join(0, nullptr);
     } else if (!strcasecmp(arg, "heap-usage")) {
         abuse_heap();
+    } else if (!strcasecmp(arg, "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));
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 4997dd6..b016e23 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -27,7 +27,7 @@
 #include <android-base/parseint.h>
 #include <android-base/unique_fd.h>
 #include <debuggerd/client.h>
-#include <selinux/selinux.h>
+#include <procinfo/process.h>
 #include "util.h"
 
 using android::base::unique_fd;
@@ -66,14 +66,32 @@
     usage(1);
   }
 
+  if (getuid() != 0) {
+    errx(1, "root is required");
+  }
+
+  // Check to see if the process exists and that we can actually send a signal to it.
+  android::procinfo::ProcessInfo proc_info;
+  if (!android::procinfo::GetProcessInfo(pid, &proc_info)) {
+    err(1, "failed to fetch info for process %d", pid);
+  }
+
+  if (proc_info.state == android::procinfo::kProcessStateZombie) {
+    errx(1, "process %d is a zombie", pid);
+  }
+
+  if (kill(pid, 0) != 0) {
+    err(1, "cannot send signal to process %d", pid);
+  }
+
   unique_fd piperead, pipewrite;
   if (!Pipe(&piperead, &pipewrite)) {
     err(1, "failed to create pipe");
   }
 
   std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
-  if (!debuggerd_trigger_dump(pid, std::move(pipewrite),
-                              backtrace_only ? kDebuggerdBacktrace : kDebuggerdTombstone, 0)) {
+  if (!debuggerd_trigger_dump(pid, backtrace_only ? kDebuggerdNativeBacktrace : kDebuggerdTombstone,
+                              0, std::move(pipewrite))) {
     redirect_thread.join();
     errx(1, "failed to dump process %d", pid);
   }
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index f17724a..b51fc66 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -88,14 +88,19 @@
     }                                                                                       \
   } while (0)
 
-static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd) {
+#define ASSERT_BACKTRACE_FRAME(result, frame_name)                        \
+  ASSERT_MATCH(result, R"(#\d\d pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX \
+                       R"(/libc.so \()" frame_name R"(\+)")
+
+static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
+                                 InterceptStatus* status, DebuggerdDumpType intercept_type) {
   intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
                                           ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
   if (intercept_fd->get() == -1) {
     FAIL() << "failed to contact tombstoned: " << strerror(errno);
   }
 
-  InterceptRequest req = {.pid = target_pid};
+  InterceptRequest req = {.pid = target_pid, .dump_type = intercept_type};
 
   unique_fd output_pipe_write;
   if (!Pipe(output_fd, &output_pipe_write)) {
@@ -118,6 +123,8 @@
     FAIL() << "failed to set pipe size: " << strerror(errno);
   }
 
+  ASSERT_GE(pipe_buffer_size, 1024 * 1024);
+
   if (send_fd(intercept_fd->get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
     FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
   }
@@ -133,7 +140,7 @@
            << ", received " << rc;
   }
 
-  ASSERT_EQ(InterceptStatus::kRegistered, response.status);
+  *status = response.status;
 }
 
 class CrasherTest : public ::testing::Test {
@@ -146,7 +153,7 @@
   CrasherTest();
   ~CrasherTest();
 
-  void StartIntercept(unique_fd* output_fd);
+  void StartIntercept(unique_fd* output_fd, DebuggerdDumpType intercept_type = kDebuggerdTombstone);
 
   // Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
   void FinishIntercept(int* result);
@@ -172,12 +179,14 @@
   android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
 }
 
-void CrasherTest::StartIntercept(unique_fd* output_fd) {
+void CrasherTest::StartIntercept(unique_fd* output_fd, DebuggerdDumpType intercept_type) {
   if (crasher_pid == -1) {
     FAIL() << "crasher hasn't been started";
   }
 
-  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd);
+  InterceptStatus status;
+  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, &status, intercept_type);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
 }
 
 void CrasherTest::FinishIntercept(int* result) {
@@ -302,7 +311,7 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+  ASSERT_BACKTRACE_FRAME(result, "abort");
 }
 
 TEST_F(CrasherTest, signal) {
@@ -428,7 +437,7 @@
   StartProcess([]() {
     abort();
   });
-  StartIntercept(&output_fd);
+  StartIntercept(&output_fd, kDebuggerdNativeBacktrace);
 
   std::this_thread::sleep_for(500ms);
 
@@ -438,7 +447,7 @@
   FinishIntercept(&intercept_result);
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+  /system/lib)" ARCH_SUFFIX R"(/libc.so \(read\+)");
+  ASSERT_BACKTRACE_FRAME(result, "read");
 
   int status;
   ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED));
@@ -449,7 +458,7 @@
   FinishIntercept(&intercept_result);
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+  ASSERT_BACKTRACE_FRAME(result, "abort");
 }
 
 TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
@@ -469,7 +478,7 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+  ASSERT_BACKTRACE_FRAME(result, "abort");
 }
 
 TEST_F(CrasherTest, capabilities) {
@@ -526,7 +535,7 @@
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
   ConsumeFd(std::move(output_fd), &result);
   ASSERT_MATCH(result, R"(name: thread_name\s+>>> .+debuggerd_test(32|64) <<<)");
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+  ASSERT_BACKTRACE_FRAME(result, "tgkill");
 }
 
 TEST_F(CrasherTest, fake_pid) {
@@ -557,7 +566,7 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+  ASSERT_BACKTRACE_FRAME(result, "tgkill");
 }
 
 TEST(crash_dump, zombie) {
@@ -595,11 +604,13 @@
     pid_t pid = 123'456'789 + i;
 
     unique_fd intercept_fd, output_fd;
-    tombstoned_intercept(pid, &intercept_fd, &output_fd);
+    InterceptStatus status;
+    tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+    ASSERT_EQ(InterceptStatus::kRegistered, status);
 
     {
       unique_fd tombstoned_socket, input_fd;
-      ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+      ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd, kDebuggerdTombstone));
       ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
     }
 
@@ -627,7 +638,9 @@
       pid_t pid = pid_base + dump;
 
       unique_fd intercept_fd, output_fd;
-      tombstoned_intercept(pid, &intercept_fd, &output_fd);
+      InterceptStatus status;
+      tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+      ASSERT_EQ(InterceptStatus::kRegistered, status);
 
       // Pretend to crash, and then immediately close the socket.
       unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
@@ -658,11 +671,13 @@
       pid_t pid = pid_base + dump;
 
       unique_fd intercept_fd, output_fd;
-      tombstoned_intercept(pid, &intercept_fd, &output_fd);
+      InterceptStatus status;
+      tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+      ASSERT_EQ(InterceptStatus::kRegistered, status);
 
       {
         unique_fd tombstoned_socket, input_fd;
-        ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+        ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd, kDebuggerdTombstone));
         ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
         tombstoned_notify_completion(tombstoned_socket.get());
       }
@@ -682,3 +697,65 @@
     thread.join();
   }
 }
+
+TEST(tombstoned, java_trace_intercept_smoke) {
+  // Using a "real" PID is a little dangerous here - if the test fails
+  // or crashes, we might end up getting a bogus / unreliable stack
+  // trace.
+  const pid_t self = getpid();
+
+  unique_fd intercept_fd, output_fd;
+  InterceptStatus status;
+  tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+  // First connect to tombstoned requesting a native backtrace. This
+  // should result in a "regular" FD and not the installed intercept.
+  const char native[] = "native";
+  unique_fd tombstoned_socket, input_fd;
+  ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdNativeBacktrace));
+  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), native, sizeof(native)));
+  tombstoned_notify_completion(tombstoned_socket.get());
+
+  // Then, connect to tombstoned asking for a java backtrace. This *should*
+  // trigger the intercept.
+  const char java[] = "java";
+  ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdJavaBacktrace));
+  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), java, sizeof(java)));
+  tombstoned_notify_completion(tombstoned_socket.get());
+
+  char outbuf[sizeof(java)];
+  ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
+  ASSERT_STREQ("java", outbuf);
+}
+
+TEST(tombstoned, multiple_intercepts) {
+  const pid_t fake_pid = 1'234'567;
+  unique_fd intercept_fd, output_fd;
+  InterceptStatus status;
+  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+  unique_fd intercept_fd_2, output_fd_2;
+  tombstoned_intercept(fake_pid, &intercept_fd_2, &output_fd_2, &status, kDebuggerdNativeBacktrace);
+  ASSERT_EQ(InterceptStatus::kFailedAlreadyRegistered, status);
+}
+
+TEST(tombstoned, intercept_any) {
+  const pid_t fake_pid = 1'234'567;
+
+  unique_fd intercept_fd, output_fd;
+  InterceptStatus status;
+  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdNativeBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+  const char any[] = "any";
+  unique_fd tombstoned_socket, input_fd;
+  ASSERT_TRUE(tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdAnyIntercept));
+  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), any, sizeof(any)));
+  tombstoned_notify_completion(tombstoned_socket.get());
+
+  char outbuf[sizeof(any)];
+  ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
+  ASSERT_STREQ("any", outbuf);
+}
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index a9c9862..43104ec 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -151,7 +151,7 @@
 
   // Fetch output fd from tombstoned.
   unique_fd tombstone_socket, output_fd;
-  if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd)) {
+  if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdNativeBacktrace)) {
     goto exit;
   }
 
@@ -215,7 +215,8 @@
   }
 
   unique_fd tombstone_socket, output_fd;
-  bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd);
+  bool tombstoned_connected =
+      tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdTombstone);
   debuggerd_fallback_tombstone(output_fd.get(), ucontext, info, abort_message);
   if (tombstoned_connected) {
     tombstoned_notify_completion(tombstone_socket.get());
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 8fd6e11..55cd03e 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -50,6 +50,8 @@
 
 #include <async_safe/log.h>
 
+#include "dump_type.h"
+
 // see man(2) prctl, specifically the section about PR_GET_NAME
 #define MAX_TASK_NAME_LEN (16)
 
@@ -253,6 +255,14 @@
 // process.
 static void* pseudothread_stack;
 
+static DebuggerdDumpType get_dump_type(const debugger_thread_info* thread_info) {
+  if (thread_info->signal_number == DEBUGGER_SIGNAL && thread_info->info->si_value.sival_int) {
+    return kDebuggerdNativeBacktrace;
+  }
+
+  return kDebuggerdTombstone;
+}
+
 static int debuggerd_dispatch_pseudothread(void* arg) {
   debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
 
@@ -285,11 +295,15 @@
 
     char main_tid[10];
     char pseudothread_tid[10];
+    char debuggerd_dump_type[10];
     async_safe_format_buffer(main_tid, sizeof(main_tid), "%d", thread_info->crashing_tid);
     async_safe_format_buffer(pseudothread_tid, sizeof(pseudothread_tid), "%d",
                              thread_info->pseudothread_tid);
+    async_safe_format_buffer(debuggerd_dump_type, sizeof(debuggerd_dump_type), "%d",
+                             get_dump_type(thread_info));
 
-    execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, nullptr);
+    execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
+          nullptr);
 
     fatal_errno("exec failed");
   } else {
diff --git a/debuggerd/include/debuggerd/client.h b/debuggerd/include/debuggerd/client.h
index 01de57b..b7284b0 100644
--- a/debuggerd/include/debuggerd/client.h
+++ b/debuggerd/include/debuggerd/client.h
@@ -22,15 +22,13 @@
 
 #include <android-base/unique_fd.h>
 
-enum DebuggerdDumpType {
-  kDebuggerdBacktrace,
-  kDebuggerdTombstone,
-};
+#include "dump_type.h"
 
 // Trigger a dump of specified process to output_fd.
 // output_fd is consumed, timeout of 0 will wait forever.
-bool debuggerd_trigger_dump(pid_t pid, android::base::unique_fd output_fd,
-                            enum DebuggerdDumpType dump_type, unsigned int timeout_ms);
+bool debuggerd_trigger_dump(pid_t pid, enum DebuggerdDumpType dump_type, unsigned int timeout_ms,
+                            android::base::unique_fd output_fd);
 
-int dump_backtrace_to_file(pid_t tid, int fd);
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
+int dump_backtrace_to_file(pid_t tid, enum DebuggerdDumpType dump_type, int output_fd);
+int dump_backtrace_to_file_timeout(pid_t tid, enum DebuggerdDumpType dump_type, int timeout_secs,
+                                   int output_fd);
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index edc7be5..996d714 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -168,6 +168,26 @@
         case TRAP_BRANCH: return "TRAP_BRANCH";
         case TRAP_HWBKPT: return "TRAP_HWBKPT";
       }
+      if ((code & 0xff) == SIGTRAP) {
+        switch ((code >> 8) & 0xff) {
+          case PTRACE_EVENT_FORK:
+            return "PTRACE_EVENT_FORK";
+          case PTRACE_EVENT_VFORK:
+            return "PTRACE_EVENT_VFORK";
+          case PTRACE_EVENT_CLONE:
+            return "PTRACE_EVENT_CLONE";
+          case PTRACE_EVENT_EXEC:
+            return "PTRACE_EVENT_EXEC";
+          case PTRACE_EVENT_VFORK_DONE:
+            return "PTRACE_EVENT_VFORK_DONE";
+          case PTRACE_EVENT_EXIT:
+            return "PTRACE_EVENT_EXIT";
+          case PTRACE_EVENT_SECCOMP:
+            return "PTRACE_EVENT_SECCOMP";
+          case PTRACE_EVENT_STOP:
+            return "PTRACE_EVENT_STOP";
+        }
+      }
       static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
       break;
   }
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index 144efc8..7e1961e 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -18,6 +18,8 @@
 
 #include <stdint.h>
 
+#include "dump_type.h"
+
 // Sockets in the ANDROID_SOCKET_NAMESPACE_RESERVED namespace.
 // Both sockets are SOCK_SEQPACKET sockets, so no explicit length field is needed.
 constexpr char kTombstonedCrashSocketName[] = "tombstoned_crash";
@@ -40,6 +42,7 @@
 };
 
 struct DumpRequest {
+  DebuggerdDumpType dump_type;
   int32_t pid;
 };
 
@@ -54,10 +57,15 @@
 // Comes with a file descriptor via SCM_RIGHTS.
 // This packet should be sent before an actual dump happens.
 struct InterceptRequest {
+  DebuggerdDumpType dump_type;
   int32_t pid;
 };
 
 enum class InterceptStatus : uint8_t {
+  // Returned when an intercept of a different type has already been
+  // registered (and is active) for a given PID.
+  kFailedAlreadyRegistered,
+  // Returned in all other failure cases.
   kFailed,
   kStarted,
   kRegistered,
diff --git a/debuggerd/tombstoned/include/tombstoned/tombstoned.h b/debuggerd/tombstoned/include/tombstoned/tombstoned.h
index 908517d..6403dbe 100644
--- a/debuggerd/tombstoned/include/tombstoned/tombstoned.h
+++ b/debuggerd/tombstoned/include/tombstoned/tombstoned.h
@@ -20,7 +20,9 @@
 
 #include <android-base/unique_fd.h>
 
+#include "dump_type.h"
+
 bool tombstoned_connect(pid_t pid, android::base::unique_fd* tombstoned_socket,
-                        android::base::unique_fd* output_fd, bool is_native_crash = true);
+                        android::base::unique_fd* output_fd, DebuggerdDumpType dump_type);
 
 bool tombstoned_notify_completion(int tombstoned_socket);
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index 4d4eb9e..24960bc 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -61,11 +61,24 @@
       reason = "due to input";
     }
 
-    LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " terminated " << reason;
+    LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " and type "
+              << intercept->dump_type << " terminated: " << reason;
     intercept_manager->intercepts.erase(it);
   }
 }
 
+static bool is_intercept_request_valid(const InterceptRequest& request) {
+  if (request.pid <= 0 || request.pid > std::numeric_limits<pid_t>::max()) {
+    return false;
+  }
+
+  if (request.dump_type < 0 || request.dump_type > kDebuggerdJavaBacktrace) {
+    return false;
+  }
+
+  return true;
+}
+
 static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
   auto intercept = reinterpret_cast<Intercept*>(arg);
   InterceptManager* intercept_manager = intercept->intercept_manager;
@@ -103,23 +116,24 @@
     rcv_fd.reset(moved_fd);
 
     // We trust the other side, so only do minimal validity checking.
-    if (intercept_request.pid <= 0 || intercept_request.pid > std::numeric_limits<pid_t>::max()) {
+    if (!is_intercept_request_valid(intercept_request)) {
       InterceptResponse response = {};
       response.status = InterceptStatus::kFailed;
-      snprintf(response.error_message, sizeof(response.error_message), "invalid pid %" PRId32,
-               intercept_request.pid);
+      snprintf(response.error_message, sizeof(response.error_message), "invalid intercept request");
       TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
       goto fail;
     }
 
     intercept->intercept_pid = intercept_request.pid;
+    intercept->dump_type = intercept_request.dump_type;
 
     // Check if it's already registered.
     if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
       InterceptResponse response = {};
-      response.status = InterceptStatus::kFailed;
+      response.status = InterceptStatus::kFailedAlreadyRegistered;
       snprintf(response.error_message, sizeof(response.error_message),
-               "pid %" PRId32 " already intercepted", intercept_request.pid);
+               "pid %" PRId32 " already intercepted, type %d", intercept_request.pid,
+               intercept_request.dump_type);
       TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
       LOG(WARNING) << response.error_message;
       goto fail;
@@ -138,7 +152,8 @@
     intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
     intercept->registered = true;
 
-    LOG(INFO) << "tombstoned registered intercept for pid " << intercept_request.pid;
+    LOG(INFO) << "registered intercept for pid " << intercept_request.pid << " and type "
+              << intercept_request.dump_type;
 
     // Register a different read event on the socket so that we can remove intercepts if the socket
     // closes (e.g. if a user CTRL-C's the process that requested the intercept).
@@ -174,16 +189,27 @@
                                       intercept_socket);
 }
 
-bool InterceptManager::GetIntercept(pid_t pid, android::base::unique_fd* out_fd) {
+bool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type,
+                                    android::base::unique_fd* out_fd) {
   auto it = this->intercepts.find(pid);
   if (it == this->intercepts.end()) {
     return false;
   }
 
+  if (dump_type == kDebuggerdAnyIntercept) {
+    LOG(INFO) << "found registered intercept of type " << it->second->dump_type
+              << " for requested type kDebuggerdAnyIntercept";
+  } else if (it->second->dump_type != dump_type) {
+    LOG(WARNING) << "found non-matching intercept of type " << it->second->dump_type
+                 << " for requested type: " << dump_type;
+    return false;
+  }
+
   auto intercept = std::move(it->second);
   this->intercepts.erase(it);
 
-  LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid;
+  LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid
+            << " and type " << intercept->dump_type;
   InterceptResponse response = {};
   response.status = InterceptStatus::kStarted;
   TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
diff --git a/debuggerd/tombstoned/intercept_manager.h b/debuggerd/tombstoned/intercept_manager.h
index cb5db62..a11d565 100644
--- a/debuggerd/tombstoned/intercept_manager.h
+++ b/debuggerd/tombstoned/intercept_manager.h
@@ -25,6 +25,8 @@
 
 #include <android-base/unique_fd.h>
 
+#include "dump_type.h"
+
 struct InterceptManager;
 
 struct Intercept {
@@ -39,6 +41,7 @@
   pid_t intercept_pid = -1;
   android::base::unique_fd output_fd;
   bool registered = false;
+  DebuggerdDumpType dump_type = kDebuggerdNativeBacktrace;
 };
 
 struct InterceptManager {
@@ -50,5 +53,5 @@
   InterceptManager(InterceptManager& copy) = delete;
   InterceptManager(InterceptManager&& move) = delete;
 
-  bool GetIntercept(pid_t pid, android::base::unique_fd* out_fd);
+  bool GetIntercept(pid_t pid, DebuggerdDumpType dump_type, android::base::unique_fd* out_fd);
 };
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 05df9f2..93c7fb5 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -23,23 +23,28 @@
 
 #include <array>
 #include <deque>
+#include <string>
 #include <unordered_map>
+#include <utility>
 
 #include <event2/event.h>
 #include <event2/listener.h>
 #include <event2/thread.h>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 
 #include "debuggerd/handler.h"
+#include "dump_type.h"
 #include "protocol.h"
 #include "util.h"
 
 #include "intercept_manager.h"
 
+using android::base::GetIntProperty;
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
@@ -50,12 +55,24 @@
   kCrashStatusQueued,
 };
 
-struct Crash;
+// Ownership of Crash is a bit messy.
+// It's either owned by an active event that must have a timeout, or owned by
+// queued_requests, in the case that multiple crashes come in at the same time.
+struct Crash {
+  ~Crash() { event_free(crash_event); }
 
-class CrashType {
+  unique_fd crash_fd;
+  pid_t crash_pid;
+  event* crash_event = nullptr;
+  std::string crash_path;
+
+  DebuggerdDumpType crash_type;
+};
+
+class CrashQueue {
  public:
-  CrashType(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts,
-            size_t max_concurrent_dumps)
+  CrashQueue(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts,
+             size_t max_concurrent_dumps)
       : file_name_prefix_(file_name_prefix),
         dir_path_(dir_path),
         dir_fd_(open(dir_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)),
@@ -74,23 +91,42 @@
     find_oldest_artifact();
   }
 
-  unique_fd get_output_fd() {
+  static CrashQueue* for_crash(const Crash* crash) {
+    return (crash->crash_type == kDebuggerdJavaBacktrace) ? for_anrs() : for_tombstones();
+  }
+
+  static CrashQueue* for_tombstones() {
+    static CrashQueue queue("/data/tombstones", "tombstone_" /* file_name_prefix */,
+                            GetIntProperty("tombstoned.max_tombstone_count", 10),
+                            1 /* max_concurrent_dumps */);
+    return &queue;
+  }
+
+  static CrashQueue* for_anrs() {
+    static CrashQueue queue("/data/anr", "trace_" /* file_name_prefix */,
+                            GetIntProperty("tombstoned.max_anr_count", 64),
+                            4 /* max_concurrent_dumps */);
+    return &queue;
+  }
+
+  std::pair<unique_fd, std::string> get_output() {
     unique_fd result;
-    char buf[PATH_MAX];
-    snprintf(buf, sizeof(buf), "%s%02d", file_name_prefix_.c_str(), next_artifact_);
+    std::string file_name = StringPrintf("%s%02d", file_name_prefix_.c_str(), next_artifact_);
+
     // Unlink and create the file, instead of using O_TRUNC, to avoid two processes
     // interleaving their output in case we ever get into that situation.
-    if (unlinkat(dir_fd_, buf, 0) != 0 && errno != ENOENT) {
-      PLOG(FATAL) << "failed to unlink tombstone at " << dir_path_ << buf;
+    if (unlinkat(dir_fd_, file_name.c_str(), 0) != 0 && errno != ENOENT) {
+      PLOG(FATAL) << "failed to unlink tombstone at " << dir_path_ << "/" << file_name;
     }
 
-    result.reset(openat(dir_fd_, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
+    result.reset(openat(dir_fd_, file_name.c_str(),
+                        O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
     if (result == -1) {
-      PLOG(FATAL) << "failed to create tombstone at " << dir_path_ << buf;
+      PLOG(FATAL) << "failed to create tombstone at " << dir_path_ << "/" << file_name;
     }
 
     next_artifact_ = (next_artifact_ + 1) % max_artifacts_;
-    return result;
+    return {std::move(result), dir_path_ + "/" + file_name};
   }
 
   bool maybe_enqueue_crash(Crash* crash) {
@@ -114,17 +150,13 @@
 
   void on_crash_completed() { --num_concurrent_dumps_; }
 
-  static CrashType* const tombstone;
-  static CrashType* const java_trace;
-
  private:
   void find_oldest_artifact() {
     size_t oldest_tombstone = 0;
     time_t oldest_time = std::numeric_limits<time_t>::max();
 
     for (size_t i = 0; i < max_artifacts_; ++i) {
-      std::string path = android::base::StringPrintf("%s/%s%02zu", dir_path_.c_str(),
-                                                     file_name_prefix_.c_str(), i);
+      std::string path = StringPrintf("%s/%s%02zu", dir_path_.c_str(), file_name_prefix_.c_str(), i);
       struct stat st;
       if (stat(path.c_str(), &st) != 0) {
         if (errno == ENOENT) {
@@ -158,34 +190,11 @@
 
   std::deque<Crash*> queued_requests_;
 
-  DISALLOW_COPY_AND_ASSIGN(CrashType);
+  DISALLOW_COPY_AND_ASSIGN(CrashQueue);
 };
 
 // Whether java trace dumps are produced via tombstoned.
-static constexpr bool kJavaTraceDumpsEnabled = false;
-
-/* static */ CrashType* const CrashType::tombstone =
-    new CrashType("/data/tombstones", "tombstone_" /* file_name_prefix */, 10 /* max_artifacts */,
-                  1 /* max_concurrent_dumps */);
-
-/* static */ CrashType* const CrashType::java_trace =
-    (kJavaTraceDumpsEnabled ? new CrashType("/data/anr", "anr_" /* file_name_prefix */,
-                                            64 /* max_artifacts */, 4 /* max_concurrent_dumps */)
-                            : nullptr);
-
-// Ownership of Crash is a bit messy.
-// It's either owned by an active event that must have a timeout, or owned by
-// queued_requests, in the case that multiple crashes come in at the same time.
-struct Crash {
-  ~Crash() { event_free(crash_event); }
-
-  unique_fd crash_fd;
-  pid_t crash_pid;
-  event* crash_event = nullptr;
-
-  // Not owned by |Crash|.
-  CrashType* crash_type = nullptr;
-};
+static constexpr bool kJavaTraceDumpsEnabled = true;
 
 // Forward declare the callbacks so they can be placed in a sensible order.
 static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int, void*);
@@ -194,10 +203,8 @@
 
 static void perform_request(Crash* crash) {
   unique_fd output_fd;
-  // Note that java traces are not interceptible.
-  if ((crash->crash_type == CrashType::java_trace) ||
-      !intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
-    output_fd = crash->crash_type->get_output_fd();
+  if (!intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd)) {
+    std::tie(output_fd, crash->crash_path) = CrashQueue::for_crash(crash)->get_output();
   }
 
   TombstonedCrashPacket response = {
@@ -220,7 +227,7 @@
     event_add(crash->crash_event, &timeout);
   }
 
-  crash->crash_type->on_crash_started();
+  CrashQueue::for_crash(crash)->on_crash_started();
   return;
 
 fail:
@@ -228,22 +235,22 @@
 }
 
 static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
-                            void* crash_type) {
+                            void*) {
   event_base* base = evconnlistener_get_base(listener);
   Crash* crash = new Crash();
 
+  // TODO: Make sure that only java crashes come in on the java socket
+  // and only native crashes on the native socket.
   struct timeval timeout = { 1, 0 };
   event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);
   crash->crash_fd.reset(sockfd);
   crash->crash_event = crash_event;
-  crash->crash_type = static_cast<CrashType*>(crash_type);
   event_add(crash_event, &timeout);
 }
 
 static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
   ssize_t rc;
   Crash* crash = static_cast<Crash*>(arg);
-  CrashType* type = crash->crash_type;
 
   TombstonedCrashPacket request = {};
 
@@ -271,7 +278,13 @@
     goto fail;
   }
 
-  if (type == CrashType::tombstone) {
+  crash->crash_type = request.packet.dump_request.dump_type;
+  if (crash->crash_type < 0 || crash->crash_type > kDebuggerdAnyIntercept) {
+    LOG(WARNING) << "unexpected crash dump type: " << crash->crash_type;
+    goto fail;
+  }
+
+  if (crash->crash_type != kDebuggerdJavaBacktrace) {
     crash->crash_pid = request.packet.dump_request.pid;
   } else {
     // Requests for java traces are sent from untrusted processes, so we
@@ -290,7 +303,7 @@
 
   LOG(INFO) << "received crash request for pid " << crash->crash_pid;
 
-  if (type->maybe_enqueue_crash(crash)) {
+  if (CrashQueue::for_crash(crash)->maybe_enqueue_crash(crash)) {
     LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
   } else {
     perform_request(crash);
@@ -307,7 +320,7 @@
   Crash* crash = static_cast<Crash*>(arg);
   TombstonedCrashPacket request = {};
 
-  crash->crash_type->on_crash_completed();
+  CrashQueue::for_crash(crash)->on_crash_completed();
 
   if ((ev & EV_READ) == 0) {
     goto fail;
@@ -329,12 +342,23 @@
     goto fail;
   }
 
+  if (!crash->crash_path.empty()) {
+    if (crash->crash_type == kDebuggerdJavaBacktrace) {
+      LOG(ERROR) << "Traces for pid " << crash->crash_pid << " written to: " << crash->crash_path;
+    } else {
+      // NOTE: Several tools parse this log message to figure out where the
+      // tombstone associated with a given native crash was written. Any changes
+      // to this message must be carefully considered.
+      LOG(ERROR) << "Tombstone written to: " << crash->crash_path;
+    }
+  }
+
 fail:
-  CrashType* type = crash->crash_type;
+  CrashQueue* queue = CrashQueue::for_crash(crash);
   delete crash;
 
   // If there's something queued up, let them proceed.
-  type->maybe_dequeue_crashes(perform_request);
+  queue->maybe_dequeue_crashes(perform_request);
 }
 
 int main(int, char* []) {
@@ -366,7 +390,7 @@
   intercept_manager = new InterceptManager(base, intercept_socket);
 
   evconnlistener* tombstone_listener = evconnlistener_new(
-      base, crash_accept_cb, CrashType::tombstone, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
+      base, crash_accept_cb, CrashQueue::for_tombstones(), -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
   if (!tombstone_listener) {
     LOG(FATAL) << "failed to create evconnlistener for tombstones.";
   }
@@ -379,7 +403,7 @@
 
     evutil_make_socket_nonblocking(java_trace_socket);
     evconnlistener* java_trace_listener = evconnlistener_new(
-        base, crash_accept_cb, CrashType::java_trace, -1, LEV_OPT_CLOSE_ON_FREE, java_trace_socket);
+        base, crash_accept_cb, CrashQueue::for_anrs(), -1, LEV_OPT_CLOSE_ON_FREE, java_trace_socket);
     if (!java_trace_listener) {
       LOG(FATAL) << "failed to create evconnlistener for java traces.";
     }
diff --git a/debuggerd/tombstoned/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp
index 39dc6eb..bdb4c1a 100644
--- a/debuggerd/tombstoned/tombstoned_client.cpp
+++ b/debuggerd/tombstoned/tombstoned_client.cpp
@@ -31,10 +31,11 @@
 using android::base::unique_fd;
 
 bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
-                        bool is_native_crash) {
-  unique_fd sockfd(socket_local_client(
-      (is_native_crash ? kTombstonedCrashSocketName : kTombstonedJavaTraceSocketName),
-      ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+                        DebuggerdDumpType dump_type) {
+  unique_fd sockfd(
+      socket_local_client((dump_type != kDebuggerdJavaBacktrace ? kTombstonedCrashSocketName
+                                                                : kTombstonedJavaTraceSocketName),
+                          ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
   if (sockfd == -1) {
     async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to tombstoned: %s",
                           strerror(errno));
@@ -44,6 +45,7 @@
   TombstonedCrashPacket packet = {};
   packet.packet_type = CrashPacketType::kDumpRequest;
   packet.packet.dump_request.pid = pid;
+  packet.packet.dump_request.dump_type = dump_type;
   if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
     async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write DumpRequest packet: %s",
                           strerror(errno));
diff --git a/demangle/Android.bp b/demangle/Android.bp
index 96ab57d..e55c886 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -29,6 +29,7 @@
 cc_library {
     name: "libdemangle",
     defaults: ["libdemangle_defaults"],
+    vendor_available: true,
 
     srcs: [
         "Demangler.cpp",
diff --git a/demangle/Android.mk b/demangle/Android.mk
new file mode 100644
index 0000000..e3cfc2a
--- /dev/null
+++ b/demangle/Android.mk
@@ -0,0 +1,32 @@
+#
+# Copyright (C) 2017 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := demangle_fuzzer
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := \
+    Demangler.cpp \
+    demangle_fuzzer.cpp \
+
+LOCAL_CFLAGS := \
+    -Wall \
+    -Werror \
+    -Wextra \
+
+include $(BUILD_FUZZ_TEST)
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
index fb68119..f56a9be 100644
--- a/demangle/DemangleTest.cpp
+++ b/demangle/DemangleTest.cpp
@@ -22,7 +22,14 @@
 
 #include "Demangler.h"
 
-TEST(DemangleTest, VoidArgumentTest) {
+TEST(DemangleTest, IllegalArgumentModifiers) {
+  Demangler demangler;
+
+  ASSERT_EQ("_Zpp4FUNKK", demangler.Parse("_Zpp4FUNKK"));
+  ASSERT_EQ("_Zpp4FUNVV", demangler.Parse("_Zpp4FUNVV"));
+}
+
+TEST(DemangleTest, VoidArgument) {
   Demangler demangler;
 
   ASSERT_EQ("func()", demangler.Parse("_ZN4funcEv"));
@@ -189,6 +196,14 @@
   ASSERT_EQ("value(one, signed char)", demangler.Parse("_Z5value3onea"));
 }
 
+TEST(DemangleTest, FunctionStartsWithLPlusNumber) {
+  Demangler demangler;
+
+  ASSERT_EQ("value(char, int)", demangler.Parse("_ZL5valueci"));
+  ASSERT_EQ("abcdefjklmn(signed char)", demangler.Parse("_ZL11abcdefjklmna"));
+  ASSERT_EQ("value(one, signed char)", demangler.Parse("_ZL5value3onea"));
+}
+
 TEST(DemangleTest, StdTypes) {
   Demangler demangler;
 
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
index 77cfd3b..c0a96aa 100644
--- a/demangle/Demangler.cpp
+++ b/demangle/Demangler.cpp
@@ -542,9 +542,8 @@
     } else {
       suffix = " volatile";
     }
-    if (name[-1] == 'K' || name[-1] == 'V') {
+    if (!cur_state_.suffixes.empty() && (name[-1] == 'K' || name[-1] == 'V')) {
       // Special case, const/volatile apply as a single entity.
-      assert(!cur_state_.suffixes.empty());
       size_t index = cur_state_.suffixes.size();
       cur_state_.suffixes[index-1].insert(0, suffix);
     } else {
@@ -699,6 +698,8 @@
 
   if (std::isdigit(*name)) {
     name = GetStringFromLength(name, &function_name_);
+  } else if (*name == 'L' && std::isdigit(name[1])) {
+    name = GetStringFromLength(name + 1, &function_name_);
   } else {
     name = AppendOperatorString(name);
     function_name_ = cur_state_.str;
@@ -723,7 +724,8 @@
       && static_cast<size_t>(cur_name - name) < max_length) {
     cur_name = (this->*parse_func_)(cur_name);
   }
-  if (cur_name == nullptr || *cur_name != '\0' || function_name_.empty()) {
+  if (cur_name == nullptr || *cur_name != '\0' || function_name_.empty() ||
+      !cur_state_.suffixes.empty()) {
     return name;
   }
 
diff --git a/demangle/demangle_fuzzer.cpp b/demangle/demangle_fuzzer.cpp
new file mode 100644
index 0000000..83fafc2
--- /dev/null
+++ b/demangle/demangle_fuzzer.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 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 <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+
+#include "Demangler.h"
+
+extern "C" void LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  std::vector<char> data_str(size + 1);
+  memcpy(data_str.data(), data, size);
+  data_str[size] = '\0';
+
+  Demangler demangler;
+  std::string demangled_name = demangler.Parse(data_str.data());
+  if (size != 0 && data_str[0] != '\0' && demangled_name.empty()) {
+    abort();
+  }
+}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 3e890c7..271ca95 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -53,6 +53,7 @@
 #include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
@@ -1350,7 +1351,8 @@
     struct fastboot_buffer buf;
     const char* errMsg = nullptr;
     const struct fs_generator* gen = nullptr;
-    int fd;
+    TemporaryFile output;
+    unique_fd fd;
 
     unsigned int limit = INT_MAX;
     if (target_sparse_limit > 0 && target_sparse_limit < limit) {
@@ -1403,22 +1405,23 @@
         return;
     }
 
-    fd = make_temporary_fd();
-    if (fd == -1) return;
-
     unsigned eraseBlkSize, logicalBlkSize;
     eraseBlkSize = fb_get_flash_block_size(transport, "erase-block-size");
     logicalBlkSize = fb_get_flash_block_size(transport, "logical-block-size");
 
-    if (fs_generator_generate(gen, fd, size, initial_dir, eraseBlkSize, logicalBlkSize)) {
+    if (fs_generator_generate(gen, output.path, size, initial_dir,
+            eraseBlkSize, logicalBlkSize)) {
         fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
-        close(fd);
         return;
     }
 
-    if (!load_buf_fd(transport, fd, &buf)) {
+    fd.reset(open(output.path, O_RDONLY));
+    if (fd == -1) {
+        fprintf(stderr, "Cannot open generated image: %s\n", strerror(errno));
+        return;
+    }
+    if (!load_buf_fd(transport, fd.release(), &buf)) {
         fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
-        close(fd);
         return;
     }
     flash_buf(partition, &buf);
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 5d9ccfe..99ca7dd 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -4,6 +4,7 @@
 #include "make_f2fs.h"
 
 #include <errno.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -11,12 +12,20 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/unique_fd.h>
 #include <ext4_utils/make_ext4fs.h>
 #include <sparse/sparse.h>
 
-static int generate_ext4_image(int fd, long long partSize, const std::string& initial_dir,
+using android::base::unique_fd;
+
+static int generate_ext4_image(const char* fileName, long long partSize, const std::string& initial_dir,
                                        unsigned eraseBlkSize, unsigned logicalBlkSize)
 {
+    unique_fd fd(open(fileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
+    if (fd == -1) {
+        fprintf(stderr, "Unable to open output file for EXT4 filesystem: %s\n", strerror(errno));
+        return -1;
+    }
     if (initial_dir.empty()) {
         make_ext4fs_sparse_fd_align(fd, partSize, NULL, NULL, eraseBlkSize, logicalBlkSize);
     } else {
@@ -27,11 +36,16 @@
 }
 
 #ifdef USE_F2FS
-static int generate_f2fs_image(int fd, long long partSize, const std::string& initial_dir,
+static int generate_f2fs_image(const char* fileName, long long partSize, const std::string& initial_dir,
                                unsigned /* unused */, unsigned /* unused */)
 {
     if (!initial_dir.empty()) {
-        fprintf(stderr, "Unable to set initial directory on F2FS filesystem\n");
+        fprintf(stderr, "Unable to set initial directory on F2FS filesystem: %s\n", strerror(errno));
+        return -1;
+    }
+    unique_fd fd(open(fileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
+    if (fd == -1) {
+        fprintf(stderr, "Unable to open output file for F2FS filesystem: %s\n", strerror(errno));
         return -1;
     }
     return make_f2fs_sparse_fd(fd, partSize, NULL, NULL);
@@ -42,7 +56,7 @@
     const char* fs_type;  //must match what fastboot reports for partition type
 
     //returns 0 or error value
-    int (*generate)(int fd, long long partSize, const std::string& initial_dir,
+    int (*generate)(const char* fileName, long long partSize, const std::string& initial_dir,
                     unsigned eraseBlkSize, unsigned logicalBlkSize);
 
 } generators[] = {
@@ -61,8 +75,8 @@
     return nullptr;
 }
 
-int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
+int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
     const std::string& initial_dir, unsigned eraseBlkSize, unsigned logicalBlkSize)
 {
-    return gen->generate(tmpFileNo, partSize, initial_dir, eraseBlkSize, logicalBlkSize);
+    return gen->generate(fileName, partSize, initial_dir, eraseBlkSize, logicalBlkSize);
 }
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 0a5f5a4..c6baa7f 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -7,7 +7,7 @@
 struct fs_generator;
 
 const struct fs_generator* fs_get_generator(const std::string& fs_type);
-int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
+int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
     const std::string& initial_dir, unsigned eraseBlkSize = 0, unsigned logicalBlkSize = 0);
 
 #endif
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 0af6159..608917a 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -21,18 +21,6 @@
     },
     local_include_dirs: ["include/"],
     cppflags: ["-Werror"],
-    static_libs: [
-        "liblogwrap",
-        "libfec",
-        "libfec_rs",
-        "libbase",
-        "libcrypto_utils",
-        "libcrypto",
-        "libext4_utils",
-        "libsquashfs_utils",
-        "libselinux",
-        "libavb",
-    ],
 }
 
 cc_library_static {
@@ -44,12 +32,28 @@
         "fs_mgr.cpp",
         "fs_mgr_dm_ioctl.cpp",
         "fs_mgr_format.cpp",
-        "fs_mgr_fstab.cpp",
-        "fs_mgr_slotselect.cpp",
         "fs_mgr_verity.cpp",
         "fs_mgr_avb.cpp",
         "fs_mgr_avb_ops.cpp",
-        "fs_mgr_boot_config.cpp",
+    ],
+    static_libs: [
+        "liblogwrap",
+        "libfec",
+        "libfec_rs",
+        "libbase",
+        "libcrypto_utils",
+        "libcrypto",
+        "libext4_utils",
+        "libsquashfs_utils",
+        "libselinux",
+        "libavb",
+        "libfstab",
+    ],
+    export_static_lib_headers: [
+        "libfstab",
+    ],
+    whole_static_libs: [
+        "libfstab",
     ],
     product_variables: {
         debuggable: {
@@ -60,3 +64,16 @@
         },
     },
 }
+
+cc_library_static {
+    name: "libfstab",
+    vendor_available: true,
+    defaults: ["fs_mgr_defaults"],
+    srcs: [
+        "fs_mgr_fstab.cpp",
+        "fs_mgr_boot_config.cpp",
+        "fs_mgr_slotselect.cpp",
+    ],
+    export_include_dirs: ["include_fstab"],
+    header_libs: ["libbase_headers"],
+}
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 73bdc7a..ff9b84f 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -32,6 +32,7 @@
 #include <unistd.h>
 
 #include <memory>
+#include <thread>
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
@@ -78,43 +79,33 @@
     FS_STAT_E2FSCK_F_ALWAYS = 0x0004,
     FS_STAT_UNCLEAN_SHUTDOWN = 0x0008,
     FS_STAT_QUOTA_ENABLED = 0x0010,
-    FS_STAT_TUNE2FS_FAILED = 0x0020,
     FS_STAT_RO_MOUNT_FAILED = 0x0040,
     FS_STAT_RO_UNMOUNT_FAILED = 0x0080,
     FS_STAT_FULL_MOUNT_FAILED = 0x0100,
     FS_STAT_E2FSCK_FAILED = 0x0200,
     FS_STAT_E2FSCK_FS_FIXED = 0x0400,
     FS_STAT_EXT4_INVALID_MAGIC = 0x0800,
+    FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000,
+    FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
+    FS_STAT_ENABLE_ENCRYPTION_FAILED = 0x40000,
 };
 
-/*
- * gettime() - returns the time in seconds of the system's monotonic clock or
- * zero on error.
- */
-static time_t gettime(void)
-{
-    struct timespec ts;
-    int ret;
+// TODO: switch to inotify()
+bool fs_mgr_wait_for_file(const std::string& filename,
+                          const std::chrono::milliseconds relative_timeout) {
+    auto start_time = std::chrono::steady_clock::now();
 
-    ret = clock_gettime(CLOCK_MONOTONIC, &ts);
-    if (ret < 0) {
-        PERROR << "clock_gettime(CLOCK_MONOTONIC) failed";
-        return 0;
+    while (true) {
+        if (!access(filename.c_str(), F_OK) || errno != ENOENT) {
+            return true;
+        }
+
+        std::this_thread::sleep_for(50ms);
+
+        auto now = std::chrono::steady_clock::now();
+        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+        if (time_elapsed > relative_timeout) return false;
     }
-
-    return ts.tv_sec;
-}
-
-static int wait_for_file(const char *filename, int timeout)
-{
-    struct stat info;
-    time_t timeout_time = gettime() + timeout;
-    int ret = -1;
-
-    while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0))
-        usleep(10000);
-
-    return ret;
 }
 
 static void log_fs_stat(const char* blk_device, int fs_stat)
@@ -128,10 +119,16 @@
     }
 }
 
+static bool is_extfs(const std::string& fs_type) {
+    return fs_type == "ext4" || fs_type == "ext3" || fs_type == "ext2";
+}
+
 static bool should_force_check(int fs_stat) {
-    return fs_stat & (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
-                      FS_STAT_TUNE2FS_FAILED | FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED |
-                      FS_STAT_FULL_MOUNT_FAILED | FS_STAT_E2FSCK_FAILED);
+    return fs_stat &
+           (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
+            FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED | FS_STAT_FULL_MOUNT_FAILED |
+            FS_STAT_E2FSCK_FAILED | FS_STAT_TOGGLE_QUOTAS_FAILED |
+            FS_STAT_SET_RESERVED_BLOCKS_FAILED | FS_STAT_ENABLE_ENCRYPTION_FAILED);
 }
 
 static void check_fs(const char *blk_device, char *fs_type, char *target, int *fs_stat)
@@ -144,7 +141,7 @@
     const char* e2fsck_forced_argv[] = {E2FSCK_BIN, "-f", "-y", blk_device};
 
     /* Check for the types of filesystems we know how to check */
-    if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
+    if (is_extfs(fs_type)) {
         if (*fs_stat & FS_STAT_EXT4_INVALID_MAGIC) {  // will fail, so do not try
             return;
         }
@@ -242,186 +239,208 @@
     return;
 }
 
-/* Function to read the primary superblock */
-static int read_super_block(int fd, struct ext4_super_block *sb)
-{
-    off64_t ret;
-
-    ret = lseek64(fd, 1024, SEEK_SET);
-    if (ret < 0)
-        return ret;
-
-    ret = read(fd, sb, sizeof(*sb));
-    if (ret < 0)
-        return ret;
-    if (ret != sizeof(*sb))
-        return ret;
-
-    return 0;
-}
-
-static ext4_fsblk_t ext4_blocks_count(struct ext4_super_block *es)
-{
+static ext4_fsblk_t ext4_blocks_count(const struct ext4_super_block* es) {
     return ((ext4_fsblk_t)le32_to_cpu(es->s_blocks_count_hi) << 32) |
-            le32_to_cpu(es->s_blocks_count_lo);
+           le32_to_cpu(es->s_blocks_count_lo);
 }
 
-static ext4_fsblk_t ext4_r_blocks_count(struct ext4_super_block *es)
-{
+static ext4_fsblk_t ext4_r_blocks_count(const struct ext4_super_block* es) {
     return ((ext4_fsblk_t)le32_to_cpu(es->s_r_blocks_count_hi) << 32) |
-            le32_to_cpu(es->s_r_blocks_count_lo);
+           le32_to_cpu(es->s_r_blocks_count_lo);
 }
 
-static int do_quota_with_shutdown_check(char *blk_device, char *fs_type,
-                                        struct fstab_rec *rec, int *fs_stat)
-{
-    int force_check = 0;
-    if (!strcmp(fs_type, "ext4")) {
-        /*
-         * Some system images do not have tune2fs for licensing reasons
-         * Detect these and skip reserve blocks.
-         */
-        if (access(TUNE2FS_BIN, X_OK)) {
-            LERROR << "Not running " << TUNE2FS_BIN << " on "
-                   << blk_device << " (executable not in system image)";
-        } else {
-            const char* arg1 = nullptr;
-            const char* arg2 = nullptr;
-            int status = 0;
-            int ret = 0;
-            android::base::unique_fd fd(
-                TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
-            if (fd >= 0) {
-                struct ext4_super_block sb;
-                ret = read_super_block(fd, &sb);
-                if (ret < 0) {
-                    PERROR << "Can't read '" << blk_device << "' super block";
-                    return force_check;
-                }
-                if (sb.s_magic != EXT4_SUPER_MAGIC) {
-                    LINFO << "Invalid ext4 magic:0x" << std::hex << sb.s_magic << "," << blk_device;
-                    *fs_stat |= FS_STAT_EXT4_INVALID_MAGIC;
-                    return 0;  // not a valid fs, tune2fs, fsck, and mount  will all fail.
-                }
-                *fs_stat |= FS_STAT_IS_EXT4;
-                LINFO << "superblock s_max_mnt_count:" << sb.s_max_mnt_count << "," << blk_device;
-                if (sb.s_max_mnt_count == 0xffff) {  // -1 (int16) in ext2, but uint16 in ext4
-                    *fs_stat |= FS_STAT_NEW_IMAGE_VERSION;
-                }
-                if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 ||
-                    (sb.s_state & EXT4_VALID_FS) == 0) {
-                    LINFO << __FUNCTION__ << "(): was not clealy shutdown, state flag:"
-                          << std::hex << sb.s_state
-                          << "incompat flag:" << std::hex << sb.s_feature_incompat;
-                    force_check = 1;
-                    *fs_stat |= FS_STAT_UNCLEAN_SHUTDOWN;
-                }
-                int has_quota = (sb.s_feature_ro_compat
-                        & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
-                int want_quota = fs_mgr_is_quota(rec) != 0;
+// Read the primary superblock from an ext4 filesystem.  On failure return
+// false.  If it's not an ext4 filesystem, also set FS_STAT_EXT4_INVALID_MAGIC.
+static bool read_ext4_superblock(const char* blk_device, struct ext4_super_block* sb, int* fs_stat) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
 
-                if (has_quota == want_quota) {
-                    LINFO << "Requested quota status is match on " << blk_device;
-                    return force_check;
-                } else if (want_quota) {
-                    LINFO << "Enabling quota on " << blk_device;
-                    arg1 = "-Oquota";
-                    arg2 = "-Qusrquota,grpquota";
-                    force_check = 1;
-                    *fs_stat |= FS_STAT_QUOTA_ENABLED;
-                } else {
-                    LINFO << "Disabling quota on " << blk_device;
-                    arg1 = "-Q^usrquota,^grpquota";
-                    arg2 = "-O^quota";
-                }
-            } else {
-                PERROR << "Failed to open '" << blk_device << "'";
-                return force_check;
-            }
-
-            const char *tune2fs_argv[] = {
-                TUNE2FS_BIN,
-                arg1,
-                arg2,
-                blk_device,
-            };
-            ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv),
-                                          const_cast<char **>(tune2fs_argv),
-                                          &status, true, LOG_KLOG | LOG_FILE,
-                                          true, NULL, NULL, 0);
-            if (ret < 0) {
-                /* No need to check for error in fork, we can't really handle it now */
-                LERROR << "Failed trying to run " << TUNE2FS_BIN;
-                *fs_stat |= FS_STAT_TUNE2FS_FAILED;
-            }
-        }
+    if (fd < 0) {
+        PERROR << "Failed to open '" << blk_device << "'";
+        return false;
     }
-    return force_check;
+
+    if (pread(fd, sb, sizeof(*sb), 1024) != sizeof(*sb)) {
+        PERROR << "Can't read '" << blk_device << "' superblock";
+        return false;
+    }
+
+    if (sb->s_magic != EXT4_SUPER_MAGIC) {
+        LINFO << "Invalid ext4 magic:0x" << std::hex << sb->s_magic << " "
+              << "on '" << blk_device << "'";
+        // not a valid fs, tune2fs, fsck, and mount  will all fail.
+        *fs_stat |= FS_STAT_EXT4_INVALID_MAGIC;
+        return false;
+    }
+    *fs_stat |= FS_STAT_IS_EXT4;
+    LINFO << "superblock s_max_mnt_count:" << sb->s_max_mnt_count << "," << blk_device;
+    if (sb->s_max_mnt_count == 0xffff) {  // -1 (int16) in ext2, but uint16 in ext4
+        *fs_stat |= FS_STAT_NEW_IMAGE_VERSION;
+    }
+    return true;
 }
 
-static void do_reserved_size(char *blk_device, char *fs_type, struct fstab_rec *rec, int *fs_stat)
-{
-    /* Check for the types of filesystems we know how to check */
-    if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
-        /*
-         * Some system images do not have tune2fs for licensing reasons
-         * Detect these and skip reserve blocks.
-         */
-        if (access(TUNE2FS_BIN, X_OK)) {
-            LERROR << "Not running " << TUNE2FS_BIN << " on "
-                   << blk_device << " (executable not in system image)";
+// Some system images do not have tune2fs for licensing reasons.
+// Detect these and skip running it.
+static bool tune2fs_available(void) {
+    return access(TUNE2FS_BIN, X_OK) == 0;
+}
+
+static bool run_tune2fs(const char* argv[], int argc) {
+    int ret;
+
+    ret = android_fork_execvp_ext(argc, const_cast<char**>(argv), nullptr, true,
+                                  LOG_KLOG | LOG_FILE, true, nullptr, nullptr, 0);
+    return ret == 0;
+}
+
+// Enable/disable quota support on the filesystem if needed.
+static void tune_quota(const char* blk_device, const struct fstab_rec* rec,
+                       const struct ext4_super_block* sb, int* fs_stat) {
+    bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
+    bool want_quota = fs_mgr_is_quota(rec) != 0;
+
+    if (has_quota == want_quota) {
+        return;
+    }
+
+    if (!tune2fs_available()) {
+        LERROR << "Unable to " << (want_quota ? "enable" : "disable") << " quotas on " << blk_device
+               << " because " TUNE2FS_BIN " is missing";
+        return;
+    }
+
+    const char* argv[] = {TUNE2FS_BIN, nullptr, nullptr, blk_device};
+
+    if (want_quota) {
+        LINFO << "Enabling quotas on " << blk_device;
+        argv[1] = "-Oquota";
+        argv[2] = "-Qusrquota,grpquota";
+        *fs_stat |= FS_STAT_QUOTA_ENABLED;
+    } else {
+        LINFO << "Disabling quotas on " << blk_device;
+        argv[1] = "-O^quota";
+        argv[2] = "-Q^usrquota,^grpquota";
+    }
+
+    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+        LERROR << "Failed to run " TUNE2FS_BIN " to " << (want_quota ? "enable" : "disable")
+               << " quotas on " << blk_device;
+        *fs_stat |= FS_STAT_TOGGLE_QUOTAS_FAILED;
+    }
+}
+
+// Set the number of reserved filesystem blocks if needed.
+static void tune_reserved_size(const char* blk_device, const struct fstab_rec* rec,
+                               const struct ext4_super_block* sb, int* fs_stat) {
+    if (!(rec->fs_mgr_flags & MF_RESERVEDSIZE)) {
+        return;
+    }
+
+    // The size to reserve is given in the fstab, but we won't reserve more
+    // than 2% of the filesystem.
+    const uint64_t max_reserved_blocks = ext4_blocks_count(sb) * 0.02;
+    uint64_t reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(sb);
+
+    if (reserved_blocks > max_reserved_blocks) {
+        LWARNING << "Reserved blocks " << reserved_blocks << " is too large; "
+                 << "capping to " << max_reserved_blocks;
+        reserved_blocks = max_reserved_blocks;
+    }
+
+    if (ext4_r_blocks_count(sb) == reserved_blocks) {
+        return;
+    }
+
+    if (!tune2fs_available()) {
+        LERROR << "Unable to set the number of reserved blocks on " << blk_device
+               << " because " TUNE2FS_BIN " is missing";
+        return;
+    }
+
+    char buf[32];
+    const char* argv[] = {TUNE2FS_BIN, "-r", buf, blk_device};
+
+    snprintf(buf, sizeof(buf), "%" PRIu64, reserved_blocks);
+    LINFO << "Setting reserved block count on " << blk_device << " to " << reserved_blocks;
+    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+        LERROR << "Failed to run " TUNE2FS_BIN " to set the number of reserved blocks on "
+               << blk_device;
+        *fs_stat |= FS_STAT_SET_RESERVED_BLOCKS_FAILED;
+    }
+}
+
+// Enable file-based encryption if needed.
+static void tune_encrypt(const char* blk_device, const struct fstab_rec* rec,
+                         const struct ext4_super_block* sb, int* fs_stat) {
+    bool has_encrypt = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) != 0;
+    bool want_encrypt = fs_mgr_is_file_encrypted(rec) != 0;
+
+    if (has_encrypt || !want_encrypt) {
+        return;
+    }
+
+    if (!tune2fs_available()) {
+        LERROR << "Unable to enable ext4 encryption on " << blk_device
+               << " because " TUNE2FS_BIN " is missing";
+        return;
+    }
+
+    const char* argv[] = {TUNE2FS_BIN, "-Oencrypt", blk_device};
+
+    LINFO << "Enabling ext4 encryption on " << blk_device;
+    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+        LERROR << "Failed to run " TUNE2FS_BIN " to enable "
+               << "ext4 encryption on " << blk_device;
+        *fs_stat |= FS_STAT_ENABLE_ENCRYPTION_FAILED;
+    }
+}
+
+//
+// Prepare the filesystem on the given block device to be mounted.
+//
+// If the "check" option was given in the fstab record, or it seems that the
+// filesystem was uncleanly shut down, we'll run fsck on the filesystem.
+//
+// If needed, we'll also enable (or disable) filesystem features as specified by
+// the fstab record.
+//
+static int prepare_fs_for_mount(const char* blk_device, const struct fstab_rec* rec) {
+    int fs_stat = 0;
+
+    if (is_extfs(rec->fs_type)) {
+        struct ext4_super_block sb;
+
+        if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
+            if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 ||
+                (sb.s_state & EXT4_VALID_FS) == 0) {
+                LINFO << "Filesystem on " << blk_device << " was not cleanly shutdown; "
+                      << "state flags: 0x" << std::hex << sb.s_state << ", "
+                      << "incompat feature flags: 0x" << std::hex << sb.s_feature_incompat;
+                fs_stat |= FS_STAT_UNCLEAN_SHUTDOWN;
+            }
+
+            // Note: quotas should be enabled before running fsck.
+            tune_quota(blk_device, rec, &sb, &fs_stat);
         } else {
-            LINFO << "Running " << TUNE2FS_BIN << " on " << blk_device;
-
-            int status = 0;
-            int ret = 0;
-            unsigned long reserved_blocks = 0;
-            android::base::unique_fd fd(
-                TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
-            if (fd >= 0) {
-                struct ext4_super_block sb;
-                ret = read_super_block(fd, &sb);
-                if (ret < 0) {
-                    PERROR << "Can't read '" << blk_device << "' super block";
-                    return;
-                }
-                reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(&sb);
-                unsigned long reserved_threshold = ext4_blocks_count(&sb) * 0.02;
-                if (reserved_threshold < reserved_blocks) {
-                    LWARNING << "Reserved blocks " << reserved_blocks
-                             << " is too large";
-                    reserved_blocks = reserved_threshold;
-                }
-
-                if (ext4_r_blocks_count(&sb) == reserved_blocks) {
-                    LINFO << "Have reserved same blocks";
-                    return;
-                }
-            } else {
-                PERROR << "Failed to open '" << blk_device << "'";
-                return;
-            }
-
-            char buf[16] = {0};
-            snprintf(buf, sizeof (buf), "-r %lu", reserved_blocks);
-            const char *tune2fs_argv[] = {
-                TUNE2FS_BIN,
-                buf,
-                blk_device,
-            };
-
-            ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv),
-                                          const_cast<char **>(tune2fs_argv),
-                                          &status, true, LOG_KLOG | LOG_FILE,
-                                          true, NULL, NULL, 0);
-
-            if (ret < 0) {
-                /* No need to check for error in fork, we can't really handle it now */
-                LERROR << "Failed trying to run " << TUNE2FS_BIN;
-                *fs_stat |= FS_STAT_TUNE2FS_FAILED;
-            }
+            return fs_stat;
         }
     }
+
+    if ((rec->fs_mgr_flags & MF_CHECK) ||
+        (fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) {
+        check_fs(blk_device, rec->fs_type, rec->mount_point, &fs_stat);
+    }
+
+    if (is_extfs(rec->fs_type) && (rec->fs_mgr_flags & (MF_RESERVEDSIZE | MF_FILEENCRYPTION))) {
+        struct ext4_super_block sb;
+
+        if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
+            tune_reserved_size(blk_device, rec, &sb, &fs_stat);
+            tune_encrypt(blk_device, rec, &sb, &fs_stat);
+        }
+    }
+
+    return fs_stat;
 }
 
 static void remove_trailing_slashes(char *n)
@@ -457,6 +476,16 @@
     return rc;
 }
 
+// Orange state means the device is unlocked, see the following link for details.
+// https://source.android.com/security/verifiedboot/verified-boot#device_state
+bool fs_mgr_is_device_unlocked() {
+    std::string verified_boot_state;
+    if (fs_mgr_get_boot_config("verifiedbootstate", &verified_boot_state)) {
+        return verified_boot_state == "orange";
+    }
+    return false;
+}
+
 /*
  * __mount(): wrapper around the mount() system call which also
  * sets the underlying block device to read-only if the mount is read-only.
@@ -476,10 +505,11 @@
         if ((info.st_mode & S_IFMT) == S_IFLNK)
             unlink(target);
     mkdir(target, 0755);
+    errno = 0;
     ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
     save_errno = errno;
-    LINFO << __FUNCTION__ << "(source=" << source << ",target="
-          << target << ",type=" << rec->fs_type << ")=" << ret;
+    PINFO << __FUNCTION__ << "(source=" << source << ",target=" << target
+          << ",type=" << rec->fs_type << ")=" << ret;
     if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
         fs_mgr_set_blk_ro(source);
     }
@@ -559,10 +589,7 @@
                 continue;
             }
 
-            int fs_stat = 0;
-            int force_check = do_quota_with_shutdown_check(fstab->recs[i].blk_device,
-                                                           fstab->recs[i].fs_type,
-                                                           &fstab->recs[i], &fs_stat);
+            int fs_stat = prepare_fs_for_mount(fstab->recs[i].blk_device, &fstab->recs[i]);
             if (fs_stat & FS_STAT_EXT4_INVALID_MAGIC) {
                 LERROR << __FUNCTION__ << "(): skipping mount, invalid ext4, mountpoint="
                        << fstab->recs[i].mount_point << " rec[" << i
@@ -570,15 +597,6 @@
                 mount_errno = EINVAL;  // continue bootup for FDE
                 continue;
             }
-            if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
-                check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
-                         fstab->recs[i].mount_point, &fs_stat);
-            }
-
-            if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) {
-                do_reserved_size(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
-                                 &fstab->recs[i], &fs_stat);
-            }
 
             int retry_count = 2;
             while (retry_count-- > 0) {
@@ -744,19 +762,6 @@
     }
 }
 
-// TODO: add ueventd notifiers if they don't exist.
-// This is just doing a wait_for_device for maximum of 1s
-int fs_mgr_test_access(const char *device) {
-    int tries = 25;
-    while (tries--) {
-        if (!access(device, F_OK) || errno != ENOENT) {
-            return 0;
-        }
-        usleep(40 * 1000);
-    }
-    return -1;
-}
-
 bool is_device_secure() {
     int ret = -1;
     char value[PROP_VALUE_MAX];
@@ -817,9 +822,7 @@
         }
 
         /* Translate LABEL= file system labels into block devices */
-        if (!strcmp(fstab->recs[i].fs_type, "ext2") ||
-            !strcmp(fstab->recs[i].fs_type, "ext3") ||
-            !strcmp(fstab->recs[i].fs_type, "ext4")) {
+        if (is_extfs(fstab->recs[i].fs_type)) {
             int tret = translate_ext_labels(&fstab->recs[i]);
             if (tret < 0) {
                 LERROR << "Could not translate label to block device";
@@ -827,8 +830,10 @@
             }
         }
 
-        if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
-            wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+        if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
+            !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
+            LERROR << "Skipping '" << fstab->recs[i].blk_device << "' during mount_all";
+            continue;
         }
 
         if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
@@ -839,7 +844,8 @@
                     return FS_MGR_MNTALL_FAIL;
                 }
             }
-            if (!avb_handle->SetUpAvb(&fstab->recs[i], true /* wait_for_verity_dev */)) {
+            if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
+                SetUpAvbHashtreeResult::kFail) {
                 LERROR << "Failed to set up AVB on partition: "
                        << fstab->recs[i].mount_point << ", skipping!";
                 /* Skips mounting the device. */
@@ -847,7 +853,9 @@
             }
         } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
             int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
-            if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
+            if (__android_log_is_debuggable() &&
+                    (rc == FS_MGR_SETUP_VERITY_DISABLED ||
+                     rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
                 LINFO << "Verity disabled";
             } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
                 LERROR << "Could not set up verified partition, skipping!";
@@ -1028,22 +1036,12 @@
         }
 
         /* First check the filesystem if requested */
-        if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
-            wait_for_file(n_blk_device, WAIT_TIMEOUT);
+        if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
+            LERROR << "Skipping mounting '" << n_blk_device << "'";
+            continue;
         }
 
-        int fs_stat = 0;
-        int force_check = do_quota_with_shutdown_check(n_blk_device, fstab->recs[i].fs_type,
-                                                       &fstab->recs[i], &fs_stat);
-
-        if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
-            check_fs(n_blk_device, fstab->recs[i].fs_type,
-                     fstab->recs[i].mount_point, &fs_stat);
-        }
-
-        if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) {
-            do_reserved_size(n_blk_device, fstab->recs[i].fs_type, &fstab->recs[i], &fs_stat);
-        }
+        int fs_stat = prepare_fs_for_mount(n_blk_device, &fstab->recs[i]);
 
         if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
             if (!avb_handle) {
@@ -1053,7 +1051,8 @@
                     return FS_MGR_DOMNT_FAILED;
                 }
             }
-            if (!avb_handle->SetUpAvb(&fstab->recs[i], true /* wait_for_verity_dev */)) {
+            if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
+                SetUpAvbHashtreeResult::kFail) {
                 LERROR << "Failed to set up AVB on partition: "
                        << fstab->recs[i].mount_point << ", skipping!";
                 /* Skips mounting the device. */
@@ -1061,7 +1060,9 @@
             }
         } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
             int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
-            if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
+            if (__android_log_is_debuggable() &&
+                    (rc == FS_MGR_SETUP_VERITY_DISABLED ||
+                     rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
                 LINFO << "Verity disabled";
             } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
                 LERROR << "Could not set up verified partition, skipping!";
@@ -1195,12 +1196,15 @@
                 ret = -1;
                 continue;
             }
-            fprintf(zram_fp, "%d\n", fstab->recs[i].zram_size);
+            fprintf(zram_fp, "%u\n", fstab->recs[i].zram_size);
             fclose(zram_fp);
         }
 
-        if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
-            wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+        if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
+            !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
+            LERROR << "Skipping mkswap for '" << fstab->recs[i].blk_device << "'";
+            ret = -1;
+            continue;
         }
 
         /* Initialize the swap area */
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 6618003..7824cfa 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -114,7 +114,6 @@
 
 // Reads the following values from kernel cmdline and provides the
 // VerifyVbmetaImages() to verify AvbSlotVerifyData.
-//   - androidboot.vbmeta.device_state
 //   - androidboot.vbmeta.hash_alg
 //   - androidboot.vbmeta.size
 //   - androidboot.vbmeta.digest
@@ -123,7 +122,6 @@
     // The factory method to return a unique_ptr<FsManagerAvbVerifier>
     static std::unique_ptr<FsManagerAvbVerifier> Create();
     bool VerifyVbmetaImages(const AvbSlotVerifyData& verify_data);
-    bool IsDeviceUnlocked() { return is_device_unlocked_; }
 
   protected:
     FsManagerAvbVerifier() = default;
@@ -138,7 +136,6 @@
     HashAlgorithm hash_alg_;
     uint8_t digest_[SHA512_DIGEST_LENGTH];
     size_t vbmeta_size_;
-    bool is_device_unlocked_;
 };
 
 std::unique_ptr<FsManagerAvbVerifier> FsManagerAvbVerifier::Create() {
@@ -161,9 +158,7 @@
         const std::string& key = pieces[0];
         const std::string& value = pieces[1];
 
-        if (key == "androidboot.vbmeta.device_state") {
-            avb_verifier->is_device_unlocked_ = (value == "unlocked");
-        } else if (key == "androidboot.vbmeta.hash_alg") {
+        if (key == "androidboot.vbmeta.hash_alg") {
             hash_alg = value;
         } else if (key == "androidboot.vbmeta.size") {
             if (!android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
@@ -402,7 +397,7 @@
     fstab_entry->blk_device = strdup(verity_blk_name.c_str());
 
     // Makes sure we've set everything up properly.
-    if (wait_for_verity_dev && fs_mgr_test_access(verity_blk_name.c_str()) < 0) {
+    if (wait_for_verity_dev && !fs_mgr_wait_for_file(verity_blk_name, 1s)) {
         return false;
     }
 
@@ -493,12 +488,7 @@
 }
 
 FsManagerAvbUniquePtr FsManagerAvbHandle::DoOpen(FsManagerAvbOps* avb_ops) {
-    // Gets the expected hash value of vbmeta images from kernel cmdline.
-    std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
-    if (!avb_verifier) {
-        LERROR << "Failed to create FsManagerAvbVerifier";
-        return nullptr;
-    }
+    bool is_device_unlocked = fs_mgr_is_device_unlocked();
 
     FsManagerAvbUniquePtr avb_handle(new FsManagerAvbHandle());
     if (!avb_handle) {
@@ -506,9 +496,8 @@
         return nullptr;
     }
 
-    AvbSlotVerifyFlags flags = avb_verifier->IsDeviceUnlocked()
-                                   ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
-                                   : AVB_SLOT_VERIFY_FLAGS_NONE;
+    AvbSlotVerifyFlags flags = is_device_unlocked ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
+                                                  : AVB_SLOT_VERIFY_FLAGS_NONE;
     AvbSlotVerifyResult verify_result =
         avb_ops->AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->avb_slot_data_);
 
@@ -526,62 +515,81 @@
     //     for more details.
     switch (verify_result) {
         case AVB_SLOT_VERIFY_RESULT_OK:
-            avb_handle->status_ = kFsManagerAvbHandleSuccess;
+            avb_handle->status_ = kAvbHandleSuccess;
             break;
         case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
-            if (!avb_verifier->IsDeviceUnlocked()) {
+            if (!is_device_unlocked) {
                 LERROR << "ERROR_VERIFICATION isn't allowed when the device is LOCKED";
                 return nullptr;
             }
-            avb_handle->status_ = kFsManagerAvbHandleErrorVerification;
+            avb_handle->status_ = kAvbHandleVerificationError;
             break;
         default:
             LERROR << "avb_slot_verify failed, result: " << verify_result;
             return nullptr;
     }
 
-    // Verifies vbmeta images against the digest passed from bootloader.
-    if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
-        LERROR << "VerifyVbmetaImages failed";
-        return nullptr;
-    }
-
     // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
     avb_handle->avb_version_ =
         android::base::StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
 
-    // Checks whether FLAGS_HASHTREE_DISABLED is set.
+    // Checks whether FLAGS_VERIFICATION_DISABLED is set:
+    //   - Only the top-level vbmeta struct is read.
+    //   - vbmeta struct in other partitions are NOT processed, including AVB HASH descriptor(s)
+    //     and AVB HASHTREE descriptor(s).
     AvbVBMetaImageHeader vbmeta_header;
     avb_vbmeta_image_header_to_host_byte_order(
         (AvbVBMetaImageHeader*)avb_handle->avb_slot_data_->vbmeta_images[0].vbmeta_data,
         &vbmeta_header);
+    bool verification_disabled =
+        ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
 
-    bool hashtree_disabled =
-        ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
-    if (hashtree_disabled) {
-        avb_handle->status_ = kFsManagerAvbHandleHashtreeDisabled;
+    if (verification_disabled) {
+        avb_handle->status_ = kAvbHandleVerificationDisabled;
+    } else {
+        // Verifies vbmeta structs against the digest passed from bootloader in kernel cmdline.
+        std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
+        if (!avb_verifier) {
+            LERROR << "Failed to create FsManagerAvbVerifier";
+            return nullptr;
+        }
+        if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
+            LERROR << "VerifyVbmetaImages failed";
+            return nullptr;
+        }
+
+        // Checks whether FLAGS_HASHTREE_DISABLED is set.
+        bool hashtree_disabled =
+            ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+        if (hashtree_disabled) {
+            avb_handle->status_ = kAvbHandleHashtreeDisabled;
+        }
     }
 
     LINFO << "Returning avb_handle with status: " << avb_handle->status_;
     return avb_handle;
 }
 
-bool FsManagerAvbHandle::SetUpAvb(struct fstab_rec* fstab_entry, bool wait_for_verity_dev) {
-    if (!fstab_entry) return false;
-    if (!avb_slot_data_ || avb_slot_data_->num_vbmeta_images < 1) {
-        return false;
+SetUpAvbHashtreeResult FsManagerAvbHandle::SetUpAvbHashtree(struct fstab_rec* fstab_entry,
+                                                            bool wait_for_verity_dev) {
+    if (!fstab_entry || status_ == kAvbHandleUninitialized || !avb_slot_data_ ||
+        avb_slot_data_->num_vbmeta_images < 1) {
+        return SetUpAvbHashtreeResult::kFail;
     }
 
-    if (status_ == kFsManagerAvbHandleUninitialized) return false;
-    if (status_ == kFsManagerAvbHandleHashtreeDisabled) {
-        LINFO << "AVB HASHTREE disabled on:" << fstab_entry->mount_point;
-        return true;
+    if (status_ == kAvbHandleHashtreeDisabled || status_ == kAvbHandleVerificationDisabled) {
+        LINFO << "AVB HASHTREE disabled on: " << fstab_entry->mount_point;
+        return SetUpAvbHashtreeResult::kDisabled;
     }
 
-    std::string partition_name(basename(fstab_entry->mount_point));
-    if (!avb_validate_utf8((const uint8_t*)partition_name.c_str(), partition_name.length())) {
-        LERROR << "Partition name: " << partition_name.c_str() << " is not valid UTF-8.";
-        return false;
+    // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor
+    // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.
+    std::string partition_name(basename(fstab_entry->blk_device));
+    if (fstab_entry->fs_mgr_flags & MF_SLOTSELECT) {
+        auto ab_suffix = partition_name.rfind(fs_mgr_get_slot_suffix());
+        if (ab_suffix != std::string::npos) {
+            partition_name.erase(ab_suffix);
+        }
     }
 
     AvbHashtreeDescriptor hashtree_descriptor;
@@ -589,13 +597,14 @@
     std::string root_digest;
     if (!get_hashtree_descriptor(partition_name, *avb_slot_data_, &hashtree_descriptor, &salt,
                                  &root_digest)) {
-        return false;
+        return SetUpAvbHashtreeResult::kFail;
     }
 
     // Converts HASHTREE descriptor to verity_table_params.
     if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt, root_digest,
                                   wait_for_verity_dev)) {
-        return false;
+        return SetUpAvbHashtreeResult::kFail;
     }
-    return true;
+
+    return SetUpAvbHashtreeResult::kSuccess;
 }
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index 512839b..43879fe 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -89,6 +89,15 @@
     return AVB_IO_RESULT_OK;
 }
 
+static AvbIOResult dummy_get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+                                               const char* partition ATTRIBUTE_UNUSED,
+                                               uint64_t* out_size_num_byte) {
+    // The function is for bootloader to load entire content of AVB HASH partitions.
+    // In user-space, returns 0 as we only need to set up AVB HASHTHREE partitions.
+    *out_size_num_byte = 0;
+    return AVB_IO_RESULT_OK;
+}
+
 void FsManagerAvbOps::InitializeAvbOps() {
     // We only need to provide the implementation of read_from_partition()
     // operation since that's all what is being used by the avb_slot_verify().
@@ -101,6 +110,7 @@
     avb_ops_.validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
     avb_ops_.read_is_device_unlocked = dummy_read_is_device_unlocked;
     avb_ops_.get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
+    avb_ops_.get_size_of_partition = dummy_get_size_of_partition;
 
     // Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
     avb_ops_.user_data = this;
@@ -132,10 +142,8 @@
     }
     std::string path = iter->second;
 
-    // Ensures the device path (a symlink created by init) is ready to
-    // access. fs_mgr_test_access() will test a few iterations if the
-    // path doesn't exist yet.
-    if (fs_mgr_test_access(path.c_str()) < 0) {
+    // Ensures the device path (a symlink created by init) is ready to access.
+    if (!fs_mgr_wait_for_file(path, 1s)) {
         return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
     }
 
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index ab5beed..9117667 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <string>
+
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -51,9 +53,11 @@
     // lastly, check the device tree
     if (is_dt_compatible()) {
         std::string file_name = kAndroidDtDir + "/" + key;
-        // DT entries terminate with '\0' but so do the properties
         if (android::base::ReadFileToString(file_name, out_val)) {
-            return true;
+            if (!out_val->empty()) {
+                out_val->pop_back();  // Trims the trailing '\0' out.
+                return true;
+            }
         }
     }
 
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index dc73c24..b68875b 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -337,21 +337,22 @@
 
     dirent* dp;
     while ((dp = readdir(fstabdir.get())) != NULL) {
-        // skip over name and compatible
-        if (dp->d_type != DT_DIR) {
-            continue;
-        }
-
-        // skip if its not 'vendor', 'odm' or 'system'
-        if (strcmp(dp->d_name, "odm") && strcmp(dp->d_name, "system") &&
-            strcmp(dp->d_name, "vendor")) {
-            continue;
-        }
+        // skip over name, compatible and .
+        if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue;
 
         // create <dev> <mnt_point>  <type>  <mnt_flags>  <fsmgr_flags>\n
         std::vector<std::string> fstab_entry;
         std::string file_name;
         std::string value;
+        // skip a partition entry if the status property is present and not set to ok
+        file_name = android::base::StringPrintf("%s/%s/status", fstabdir_name.c_str(), dp->d_name);
+        if (read_dt_file(file_name, &value)) {
+            if (value != "okay" && value != "ok") {
+                LINFO << "dt_fstab: Skip disabled entry for partition " << dp->d_name;
+                continue;
+            }
+        }
+
         file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
         if (!read_dt_file(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
@@ -809,32 +810,26 @@
     return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
 }
 
-int fs_mgr_is_notrim(struct fstab_rec *fstab)
-{
+int fs_mgr_is_notrim(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_NOTRIM;
 }
 
-int fs_mgr_is_formattable(struct fstab_rec *fstab)
-{
+int fs_mgr_is_formattable(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & (MF_FORMATTABLE);
 }
 
-int fs_mgr_is_slotselect(struct fstab_rec *fstab)
-{
+int fs_mgr_is_slotselect(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_SLOTSELECT;
 }
 
-int fs_mgr_is_nofail(struct fstab_rec *fstab)
-{
+int fs_mgr_is_nofail(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_NOFAIL;
 }
 
-int fs_mgr_is_latemount(struct fstab_rec *fstab)
-{
+int fs_mgr_is_latemount(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_LATEMOUNT;
 }
 
-int fs_mgr_is_quota(struct fstab_rec *fstab)
-{
+int fs_mgr_is_quota(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_QUOTA;
 }
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index c985462..5ea6e98 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -17,8 +17,12 @@
 #ifndef __CORE_FS_MGR_PRIV_H
 #define __CORE_FS_MGR_PRIV_H
 
+#include <chrono>
+#include <string>
+
 #include <android-base/logging.h>
-#include <fs_mgr.h>
+
+#include "fs_mgr.h"
 #include "fs_mgr_priv_boot_config.h"
 
 /* The CHECK() in logging.h will use program invocation name as the tag.
@@ -43,8 +47,6 @@
 
 #define CRYPTO_TMPFS_OPTIONS "size=256m,mode=0771,uid=1000,gid=1000"
 
-#define WAIT_TIMEOUT 20
-
 /* fstab has the following format:
  *
  * Any line starting with a # is a comment and ignored
@@ -110,9 +112,13 @@
 
 #define DM_BUF_SIZE 4096
 
+using namespace std::chrono_literals;
+
 int fs_mgr_set_blk_ro(const char *blockdev);
-int fs_mgr_test_access(const char *device);
+bool fs_mgr_wait_for_file(const std::string& filename,
+                          const std::chrono::milliseconds relative_timeout);
 bool fs_mgr_update_for_slotselect(struct fstab *fstab);
+bool fs_mgr_is_device_unlocked();
 bool is_dt_compatible();
 bool is_device_secure();
 int load_verity_state(struct fstab_rec* fstab, int* mode);
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 5fa10bc..5de0903 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -348,10 +348,13 @@
 
 static int was_verity_restart()
 {
-    static const char *files[] = {
+    static const char* files[] = {
+        // clang-format off
+        "/sys/fs/pstore/console-ramoops-0",
         "/sys/fs/pstore/console-ramoops",
         "/proc/last_kmsg",
         NULL
+        // clang-format on
     };
     int i;
 
@@ -689,27 +692,55 @@
     return read_verity_state(fstab->verity_loc, offset, mode);
 }
 
-static void update_verity_table_blk_device(char *blk_device, char **table)
-{
-    std::string result, word;
+// Update the verity table using the actual block device path.
+// Two cases:
+// Case-1: verity table is shared for devices with different by-name prefix.
+// Example:
+//   verity table token:       /dev/block/bootdevice/by-name/vendor
+//   blk_device-1 (non-A/B):   /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+//   blk_device-2 (A/B):       /dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor_a
+//
+// Case-2: append A/B suffix in the verity table.
+// Example:
+//   verity table token: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+//   blk_device:         /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor_a
+static void update_verity_table_blk_device(const std::string& blk_device, char** table,
+                                           bool slot_select) {
+    bool updated = false;
+    std::string result, ab_suffix;
     auto tokens = android::base::Split(*table, " ");
 
+    // If slot_select is set, it means blk_device is already updated with ab_suffix.
+    if (slot_select) ab_suffix = fs_mgr_get_slot_suffix();
+
     for (const auto& token : tokens) {
-        if (android::base::StartsWith(token, "/dev/block/") &&
-            android::base::StartsWith(blk_device, token.c_str())) {
-            word = blk_device;
+        std::string new_token;
+        if (android::base::StartsWith(token, "/dev/block/")) {
+            if (token == blk_device) return;  // no need to update if they're already the same.
+            std::size_t found1 = blk_device.find("by-name");
+            std::size_t found2 = token.find("by-name");
+            if (found1 != std::string::npos && found2 != std::string::npos &&
+                blk_device.substr(found1) == token.substr(found2) + ab_suffix) {
+                new_token = blk_device;
+            }
+        }
+
+        if (!new_token.empty()) {
+            updated = true;
+            LINFO << "Verity table: updated block device from '" << token << "' to '" << new_token
+                  << "'";
         } else {
-            word = token;
+            new_token = token;
         }
 
         if (result.empty()) {
-            result = word;
+            result = new_token;
         } else {
-            result += " " + word;
+            result += " " + new_token;
         }
     }
 
-    if (result.empty()) {
+    if (!updated) {
         return;
     }
 
@@ -738,7 +769,7 @@
     // setup is needed at all.
     if (!is_device_secure()) {
         LINFO << "Verity setup skipped for " << mount_point;
-        return FS_MGR_SETUP_VERITY_SUCCESS;
+        return FS_MGR_SETUP_VERITY_SKIPPED;
     }
 
     if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
@@ -751,8 +782,8 @@
     if (fec_verity_get_metadata(f, &verity) < 0) {
         PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
         // Allow verity disabled when the device is unlocked without metadata
-        if ("0" == android::base::GetProperty("ro.boot.flash.locked", "")) {
-            retval = FS_MGR_SETUP_VERITY_DISABLED;
+        if (fs_mgr_is_device_unlocked()) {
+            retval = FS_MGR_SETUP_VERITY_SKIPPED;
             LWARNING << "Allow invalid metadata when the device is unlocked";
         }
         goto out;
@@ -825,10 +856,9 @@
     LINFO << "Enabling dm-verity for " << mount_point.c_str()
           << " (mode " << params.mode << ")";
 
-    if (fstab->fs_mgr_flags & MF_SLOTSELECT) {
-        // Update the verity params using the actual block device path
-        update_verity_table_blk_device(fstab->blk_device, &params.table);
-    }
+    // Update the verity params using the actual block device path
+    update_verity_table_blk_device(fstab->blk_device, &params.table,
+                                   fstab->fs_mgr_flags & MF_SLOTSELECT);
 
     // load the verity mapping table
     if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
@@ -899,7 +929,7 @@
     }
 
     // make sure we've set everything up properly
-    if (wait_for_verity_dev && fs_mgr_test_access(fstab->blk_device) < 0) {
+    if (wait_for_verity_dev && !fs_mgr_wait_for_file(fstab->blk_device, 1s)) {
         goto out;
     }
 
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 9079a43..3d3faf3 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -22,11 +22,7 @@
 #include <stdbool.h>
 #include <linux/dm-ioctl.h>
 
-// C++ only headers
-// TODO: move this into separate header files under include/fs_mgr/*.h
-#ifdef __cplusplus
-#include <string>
-#endif
+#include <fstab/fstab.h>
 
 // Magic number at start of verity metadata
 #define VERITY_METADATA_MAGIC_NUMBER 0xb001b001
@@ -53,47 +49,10 @@
     MOUNT_MODE_LATE = 2
 };
 
-/*
- * The entries must be kept in the same order as they were seen in the fstab.
- * Unless explicitly requested, a lookup on mount point should always
- * return the 1st one.
- */
-struct fstab {
-    int num_entries;
-    struct fstab_rec *recs;
-    char *fstab_filename;
-};
-
-struct fstab_rec {
-    char *blk_device;
-    char *mount_point;
-    char *fs_type;
-    unsigned long flags;
-    char *fs_options;
-    int fs_mgr_flags;
-    char *key_loc;
-    char *verity_loc;
-    long long length;
-    char *label;
-    int partnum;
-    int swap_prio;
-    int max_comp_streams;
-    unsigned int zram_size;
-    uint64_t reserved_size;
-    unsigned int file_encryption_mode;
-    unsigned int erase_blk_size;
-    unsigned int logical_blk_size;
-};
-
 // Callback function for verity status
 typedef void (*fs_mgr_verity_state_callback)(struct fstab_rec *fstab,
         const char *mount_point, int mode, int status);
 
-struct fstab *fs_mgr_read_fstab_default();
-struct fstab *fs_mgr_read_fstab_dt();
-struct fstab *fs_mgr_read_fstab(const char *fstab_path);
-void fs_mgr_free_fstab(struct fstab *fstab);
-
 #define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5
 #define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 4
 #define FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION 3
@@ -116,30 +75,11 @@
                           char *real_blk_device, int size);
 bool fs_mgr_load_verity_state(int* mode);
 bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback);
-int fs_mgr_add_entry(struct fstab *fstab,
-                     const char *mount_point, const char *fs_type,
-                     const char *blk_device);
-struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path);
-int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab);
-int fs_mgr_is_nonremovable(const struct fstab_rec *fstab);
-int fs_mgr_is_verified(const struct fstab_rec *fstab);
-int fs_mgr_is_verifyatboot(const struct fstab_rec *fstab);
-int fs_mgr_is_avb(const struct fstab_rec *fstab);
-int fs_mgr_is_encryptable(const struct fstab_rec *fstab);
-int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab);
-const char* fs_mgr_get_file_encryption_mode(const struct fstab_rec *fstab);
-int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab);
-int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab);
-int fs_mgr_is_notrim(struct fstab_rec *fstab);
-int fs_mgr_is_formattable(struct fstab_rec *fstab);
-int fs_mgr_is_slotselect(struct fstab_rec *fstab);
-int fs_mgr_is_nofail(struct fstab_rec *fstab);
-int fs_mgr_is_latemount(struct fstab_rec *fstab);
-int fs_mgr_is_quota(struct fstab_rec *fstab);
 int fs_mgr_swapon_all(struct fstab *fstab);
 
 int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
 
+#define FS_MGR_SETUP_VERITY_SKIPPED  (-3)
 #define FS_MGR_SETUP_VERITY_DISABLED (-2)
 #define FS_MGR_SETUP_VERITY_FAIL (-1)
 #define FS_MGR_SETUP_VERITY_SUCCESS 0
@@ -147,10 +87,4 @@
 
 __END_DECLS
 
-// C++ only functions
-// TODO: move this into separate header files under include/fs_mgr/*.h
-#ifdef __cplusplus
-std::string fs_mgr_get_slot_suffix();
-#endif
-
 #endif /* __CORE_FS_MGR_H */
diff --git a/fs_mgr/include/fs_mgr_avb.h b/fs_mgr/include/fs_mgr_avb.h
index bbafe1a..73a22c8 100644
--- a/fs_mgr/include/fs_mgr_avb.h
+++ b/fs_mgr/include/fs_mgr_avb.h
@@ -25,11 +25,10 @@
 
 #include "fs_mgr.h"
 
-enum FsManagerAvbHandleStatus {
-    kFsManagerAvbHandleUninitialized = -1,
-    kFsManagerAvbHandleSuccess = 0,
-    kFsManagerAvbHandleHashtreeDisabled = 1,
-    kFsManagerAvbHandleErrorVerification = 2,
+enum class SetUpAvbHashtreeResult {
+    kSuccess = 0,
+    kFail,
+    kDisabled,
 };
 
 class FsManagerAvbOps;
@@ -40,8 +39,8 @@
 using ByNameSymlinkMap = std::map<std::string, std::string>;
 
 // Provides a factory method to return a unique_ptr pointing to itself and the
-// SetUpAvb() function to extract dm-verity parameters from AVB metadata to
-// load verity table into kernel through ioctl.
+// SetUpAvbHashtree() function to extract dm-verity parameters from AVB HASHTREE
+// descriptors to load verity table into kernel through ioctl.
 class FsManagerAvbHandle {
   public:
     // The factory method to return a FsManagerAvbUniquePtr that holds
@@ -65,12 +64,22 @@
     //   - nullptr: any error when reading and verifying the metadata,
     //     e.g., I/O error, digest value mismatch, size mismatch, etc.
     //
-    //   - a valid unique_ptr with status kFsMgrAvbHandleHashtreeDisabled:
+    //   - a valid unique_ptr with status kAvbHandleHashtreeDisabled:
     //     to support the existing 'adb disable-verity' feature in Android.
     //     It's very helpful for developers to make the filesystem writable to
     //     allow replacing binaries on the device.
     //
-    //   - a valid unique_ptr with status kFsMgrAvbHandleSuccess: the metadata
+    //   - a valid unique_ptr with status kAvbHandleVerificationDisabled:
+    //     to support 'avbctl disable-verification': only the top-level
+    //     vbmeta is read, vbmeta structs in other partitions are not processed.
+    //     It's needed to bypass AVB when using the generic system.img to run
+    //     VTS for project Treble.
+    //
+    //   - a valid unique_ptr with status kAvbHandleVerificationError:
+    //     there is verification error when libavb loads vbmeta from each
+    //     partition. This is only allowed when the device is unlocked.
+    //
+    //   - a valid unique_ptr with status kAvbHandleSuccess: the metadata
     //     is verified and can be trusted.
     //
     static FsManagerAvbUniquePtr Open(const fstab& fstab);
@@ -79,14 +88,15 @@
     // Sets up dm-verity on the given fstab entry.
     // The 'wait_for_verity_dev' parameter makes this function wait for the
     // verity device to get created before return.
-    // Returns true if the mount point is eligible to mount, it includes:
-    //   - status_ is kFsMgrAvbHandleHashtreeDisabled or
-    //   - status_ is kFsMgrAvbHandleSuccess and sending ioctl DM_TABLE_LOAD
-    //     to load verity table is success.
-    // Otherwise, returns false.
-    bool SetUpAvb(fstab_rec* fstab_entry, bool wait_for_verity_dev);
+    //
+    // Return value:
+    //   - kSuccess: successfully loads dm-verity table into kernel.
+    //   - kFailed: failed to setup dm-verity, e.g., vbmeta verification error,
+    //     failed to get the HASHTREE descriptor, runtime error when set up
+    //     device-mapper, etc.
+    //   - kDisabled: hashtree is disabled.
+    SetUpAvbHashtreeResult SetUpAvbHashtree(fstab_rec* fstab_entry, bool wait_for_verity_dev);
 
-    bool hashtree_disabled() const { return status_ == kFsManagerAvbHandleHashtreeDisabled; }
     const std::string& avb_version() const { return avb_version_; }
 
     FsManagerAvbHandle(const FsManagerAvbHandle&) = delete;             // no copy
@@ -102,11 +112,19 @@
     };
 
   private:
-    FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kFsManagerAvbHandleUninitialized) {}
+    enum AvbHandleStatus {
+        kAvbHandleSuccess = 0,
+        kAvbHandleUninitialized,
+        kAvbHandleHashtreeDisabled,
+        kAvbHandleVerificationDisabled,
+        kAvbHandleVerificationError,
+    };
+
+    FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kAvbHandleUninitialized) {}
     static FsManagerAvbUniquePtr DoOpen(FsManagerAvbOps* avb_ops);
 
     AvbSlotVerifyData* avb_slot_data_;
-    FsManagerAvbHandleStatus status_;
+    AvbHandleStatus status_;
     std::string avb_version_;
 };
 
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
new file mode 100644
index 0000000..17e1fb1
--- /dev/null
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2012 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_TAB_H
+#define __CORE_FS_TAB_H
+
+#include <linux/dm-ioctl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+// C++ only headers
+// TODO: move this into separate header files under include/fs_mgr/*.h
+#ifdef __cplusplus
+#include <string>
+#endif
+
+__BEGIN_DECLS
+
+/*
+ * The entries must be kept in the same order as they were seen in the fstab.
+ * Unless explicitly requested, a lookup on mount point should always
+ * return the 1st one.
+ */
+struct fstab {
+    int num_entries;
+    struct fstab_rec* recs;
+    char* fstab_filename;
+};
+
+struct fstab_rec {
+    char* blk_device;
+    char* mount_point;
+    char* fs_type;
+    unsigned long flags;
+    char* fs_options;
+    int fs_mgr_flags;
+    char* key_loc;
+    char* verity_loc;
+    long long length;
+    char* label;
+    int partnum;
+    int swap_prio;
+    int max_comp_streams;
+    unsigned int zram_size;
+    uint64_t reserved_size;
+    unsigned int file_encryption_mode;
+    unsigned int erase_blk_size;
+    unsigned int logical_blk_size;
+};
+
+struct fstab* fs_mgr_read_fstab_default();
+struct fstab* fs_mgr_read_fstab_dt();
+struct fstab* fs_mgr_read_fstab(const char* fstab_path);
+void fs_mgr_free_fstab(struct fstab* fstab);
+
+int fs_mgr_add_entry(struct fstab* fstab, const char* mount_point, const char* fs_type,
+                     const char* blk_device);
+struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const char* path);
+int fs_mgr_is_voldmanaged(const struct fstab_rec* fstab);
+int fs_mgr_is_nonremovable(const struct fstab_rec* fstab);
+int fs_mgr_is_verified(const struct fstab_rec* fstab);
+int fs_mgr_is_verifyatboot(const struct fstab_rec* fstab);
+int fs_mgr_is_avb(const struct fstab_rec* fstab);
+int fs_mgr_is_encryptable(const struct fstab_rec* fstab);
+int fs_mgr_is_file_encrypted(const struct fstab_rec* fstab);
+const char* fs_mgr_get_file_encryption_mode(const struct fstab_rec* fstab);
+int fs_mgr_is_convertible_to_fbe(const struct fstab_rec* fstab);
+int fs_mgr_is_noemulatedsd(const struct fstab_rec* fstab);
+int fs_mgr_is_notrim(const struct fstab_rec* fstab);
+int fs_mgr_is_formattable(const struct fstab_rec* fstab);
+int fs_mgr_is_slotselect(const struct fstab_rec* fstab);
+int fs_mgr_is_nofail(const struct fstab_rec* fstab);
+int fs_mgr_is_latemount(const struct fstab_rec* fstab);
+int fs_mgr_is_quota(const struct fstab_rec* fstab);
+
+__END_DECLS
+
+// C++ only functions
+// TODO: move this into separate header files under include/fs_mgr/*.h
+#ifdef __cplusplus
+std::string fs_mgr_get_slot_suffix();
+#endif
+
+#endif /* __CORE_FS_TAB_H */
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 45b6eda..4e3b885 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -116,6 +116,10 @@
         { "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE },
         { "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE },
         { "Cold", BATTERY_HEALTH_COLD },
+        // battery health values from JEITA spec
+        { "Warm", BATTERY_HEALTH_GOOD },
+        { "Cool", BATTERY_HEALTH_GOOD },
+        { "Hot", BATTERY_HEALTH_OVERHEAT },
         { NULL, 0 },
     };
 
diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
index 523e1f1..e51a06d 100644
--- a/healthd/BatteryPropertiesRegistrar.cpp
+++ b/healthd/BatteryPropertiesRegistrar.cpp
@@ -36,9 +36,19 @@
 }
 
 void BatteryPropertiesRegistrar::notifyListeners(const struct BatteryProperties& props) {
-    Mutex::Autolock _l(mRegistrationLock);
-    for (size_t i = 0; i < mListeners.size(); i++) {
-        mListeners[i]->batteryPropertiesChanged(props);
+    Vector<sp<IBatteryPropertiesListener> > listenersCopy;
+
+    // Binder currently may service an incoming oneway transaction whenever an
+    // outbound oneway call is made (if there is already a pending incoming
+    // oneway call waiting).  This is considered a bug and may change in the
+    // future.  For now, avoid recursive mutex lock while making outbound
+    // calls by making a local copy of the current list of listeners.
+    {
+        Mutex::Autolock _l(mRegistrationLock);
+        listenersCopy = mListeners;
+    }
+    for (size_t i = 0; i < listenersCopy.size(); i++) {
+        listenersCopy[i]->batteryPropertiesChanged(props);
     }
 }
 
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 91774c6..d7d1454 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -33,6 +33,7 @@
 #include <functional>
 
 #include <android-base/file.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 
 #include <sys/socket.h>
@@ -76,8 +77,6 @@
 #define POWER_ON_KEY_TIME       (2 * MSEC_PER_SEC)
 #define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
 
-#define LAST_KMSG_PATH          "/proc/last_kmsg"
-#define LAST_KMSG_PSTORE_PATH   "/sys/fs/pstore/console-ramoops"
 #define LAST_KMSG_MAX_SZ        (32 * 1024)
 
 #define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
@@ -217,14 +216,21 @@
     LOGW("\n");
     LOGW("*************** LAST KMSG ***************\n");
     LOGW("\n");
-    buf = (char *)load_file(LAST_KMSG_PSTORE_PATH, &sz);
+    const char* kmsg[] = {
+        // clang-format off
+        "/sys/fs/pstore/console-ramoops-0",
+        "/sys/fs/pstore/console-ramoops",
+        "/proc/last_kmsg",
+        // clang-format on
+    };
+    for (size_t i = 0; i < arraysize(kmsg); ++i) {
+        buf = (char*)load_file(kmsg[i], &sz);
+        if (buf && sz) break;
+    }
 
     if (!buf || !sz) {
-        buf = (char *)load_file(LAST_KMSG_PATH, &sz);
-        if (!buf || !sz) {
-            LOGW("last_kmsg not found. Cold reset?\n");
-            goto out;
-        }
+        LOGW("last_kmsg not found. Cold reset?\n");
+        goto out;
     }
 
     len = min(sz, LAST_KMSG_MAX_SZ);
diff --git a/init/Android.bp b/init/Android.bp
index 80d5c42..47578ea 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -20,7 +20,6 @@
     sanitize: {
         misc_undefined: ["integer"],
     },
-    tidy_checks: ["-misc-forwarding-reference-overload"],
     cppflags: [
         "-DLOG_UEVENTS=0",
         "-Wall",
@@ -66,11 +65,14 @@
         "capabilities.cpp",
         "descriptors.cpp",
         "devices.cpp",
+        "firmware_handler.cpp",
         "import_parser.cpp",
         "init_parser.cpp",
         "log.cpp",
         "parser.cpp",
         "service.cpp",
+        "uevent_listener.cpp",
+        "ueventd_parser.cpp",
         "util.cpp",
     ],
     whole_static_libs: ["libcap"],
@@ -151,6 +153,7 @@
         "init_test.cpp",
         "property_service_test.cpp",
         "service_test.cpp",
+        "ueventd_test.cpp",
         "util_test.cpp",
     ],
     shared_libs: [
diff --git a/init/Android.mk b/init/Android.mk
index 489d076..6cd47f4 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -40,8 +40,6 @@
 # --
 
 include $(CLEAR_VARS)
-# b/38002385, work around clang-tidy segmentation fault.
-LOCAL_TIDY_CHECKS := -misc-forwarding-reference-overload
 LOCAL_CPPFLAGS := $(init_cflags)
 LOCAL_SRC_FILES:= \
     bootchart.cpp \
diff --git a/init/README.md b/init/README.md
index 72b6c6b..422fdad 100644
--- a/init/README.md
+++ b/init/README.md
@@ -260,6 +260,14 @@
 > Sets the child's /proc/self/oom\_score\_adj to the specified value,
   which must range from -1000 to 1000.
 
+`shutdown <shutdown_behavior>`
+> Set shutdown behavior of the service process. When this is not specified,
+  the service is killed during shutdown process by using SIGTERM and SIGKILL.
+  The service with shutdown_behavior of "critical" is not killed during shutdown
+  until shutdown times out. When shutdown times out, even services tagged with
+  "shutdown critical" will be killed. When the service tagged with "shutdown critical"
+  is not running when shut down starts, it will be started.
+
 
 Triggers
 --------
diff --git a/init/action.cpp b/init/action.cpp
index 21abe02..4ec5f17 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -16,15 +16,17 @@
 
 #include "action.h"
 
+#include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
-#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
 #include "util.h"
 
 using android::base::Join;
-using android::base::StringPrintf;
+
+namespace android {
+namespace init {
 
 Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
     : func_(f), args_(args), line_(line) {}
@@ -89,19 +91,18 @@
 }
 
 void Action::ExecuteCommand(const Command& command) const {
-    Timer t;
+    android::base::Timer t;
     int result = command.InvokeFunc();
 
-    double duration_ms = t.duration_s() * 1000;
+    auto duration = t.duration();
     // Any action longer than 50ms will be warned to user as slow operation
-    if (duration_ms > 50.0 ||
-        android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
+    if (duration > 50ms || android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
         std::string trigger_name = BuildTriggersString();
         std::string cmd_str = command.BuildCommandString();
-        std::string source = StringPrintf(" (%s:%d)", filename_.c_str(), command.line());
 
-        LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << source
-                  << " returned " << result << " took " << duration_ms << "ms.";
+        LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
+                  << ":" << command.line() << ") returned " << result << " took "
+                  << duration.count() << "ms.";
     }
 }
 
@@ -325,6 +326,13 @@
     }
 }
 
+void ActionManager::ClearQueue() {
+    // We are shutting down so don't claim the oneshot builtin actions back
+    current_executing_actions_ = {};
+    event_queue_ = {};
+    current_command_ = 0;
+}
+
 bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                 int line, std::string* err) {
     std::vector<std::string> triggers(args.begin() + 1, args.end());
@@ -351,3 +359,6 @@
         action_manager_->AddAction(std::move(action_));
     }
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/action.h b/init/action.h
index d006c50..ad15f3f 100644
--- a/init/action.h
+++ b/init/action.h
@@ -27,6 +27,9 @@
 #include "init_parser.h"
 #include "keyword_map.h"
 
+namespace android {
+namespace init {
+
 class Command {
   public:
     Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
@@ -101,6 +104,7 @@
     void ExecuteOneCommand();
     bool HasMoreCommands() const;
     void DumpState() const;
+    void ClearQueue();
 
   private:
     ActionManager(ActionManager const&) = delete;
@@ -126,4 +130,7 @@
     std::unique_ptr<Action> action_;
 };
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 825603a..4727f92 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -40,6 +40,9 @@
 using android::base::StringPrintf;
 using namespace std::chrono_literals;
 
+namespace android {
+namespace init {
+
 static std::thread* g_bootcharting_thread;
 
 static std::mutex g_bootcharting_finished_mutex;
@@ -192,3 +195,6 @@
   if (args[1] == "start") return do_bootchart_start();
   return do_bootchart_stop();
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/bootchart.h b/init/bootchart.h
index 0e3593d..e4f7b59 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -20,6 +20,12 @@
 #include <string>
 #include <vector>
 
+namespace android {
+namespace init {
+
 int do_bootchart(const std::vector<std::string>& args);
 
+}  // namespace init
+}  // namespace android
+
 #endif /* _BOOTCHART_H */
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 1eacb36..dfd7b73 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -39,11 +39,11 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
-#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
@@ -68,6 +68,9 @@
 
 #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
 
+namespace android {
+namespace init {
+
 static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
 
 static int insmod(const char *filename, const char *options, int flags) {
@@ -117,7 +120,7 @@
         LOG(ERROR) << "failed to set bootloader message: " << err;
         return -1;
     }
-    DoReboot(ANDROID_RB_RESTART2, "reboot", "recovery", false);
+    property_set("sys.powerctl", "reboot,recovery");
     return 0;
 }
 
@@ -535,11 +538,10 @@
         }
     }
 
-    std::string prop_name = android::base::StringPrintf("ro.boottime.init.mount_all.%s",
-                                                        prop_post_fix);
-    Timer t;
+    std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
+    android::base::Timer t;
     int ret =  mount_fstab(fstabfile, mount_mode);
-    property_set(prop_name.c_str(), std::to_string(t.duration_ms()).c_str());
+    property_set(prop_name, std::to_string(t.duration().count()));
 
     if (import_rc) {
         /* Paths of .rc files are specified at the 2nd argument and beyond */
@@ -567,9 +569,7 @@
 }
 
 static int do_setprop(const std::vector<std::string>& args) {
-    const char* name = args[1].c_str();
-    const char* value = args[2].c_str();
-    property_set(name, value);
+    property_set(args[1], args[2]);
     return 0;
 }
 
@@ -652,8 +652,7 @@
 
 static void verity_update_property(fstab_rec *fstab, const char *mount_point,
                                    int mode, int status) {
-    property_set(android::base::StringPrintf("partition.%s.verified", mount_point).c_str(),
-                 android::base::StringPrintf("%d", mode).c_str());
+    property_set("partition."s + mount_point + ".verified", std::to_string(mode));
 }
 
 static int do_verity_update_state(const std::vector<std::string>& args) {
@@ -929,3 +928,6 @@
     // clang-format on
     return builtin_functions;
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/builtins.h b/init/builtins.h
index e1f0567..b110f61 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -24,6 +24,9 @@
 
 #include "keyword_map.h"
 
+namespace android {
+namespace init {
+
 using BuiltinFunction = std::function<int(const std::vector<std::string>&)>;
 class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
   public:
@@ -33,4 +36,7 @@
     const Map& map() const override;
 };
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index b8a9ec0..642a364 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -25,6 +25,9 @@
 
 #define CAP_MAP_ENTRY(cap) { #cap, CAP_##cap }
 
+namespace android {
+namespace init {
+
 static const std::map<std::string, int> cap_map = {
     CAP_MAP_ENTRY(CHOWN),
     CAP_MAP_ENTRY(DAC_OVERRIDE),
@@ -104,17 +107,15 @@
 }
 
 static bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) {
-    cap_t caps = cap_init();
-    auto deleter = [](cap_t* p) { cap_free(*p); };
-    std::unique_ptr<cap_t, decltype(deleter)> ptr_caps(&caps, deleter);
+    ScopedCaps caps(cap_init());
 
-    cap_clear(caps);
+    cap_clear(caps.get());
     cap_value_t value[1];
     for (size_t cap = 0; cap < to_keep.size(); ++cap) {
         if (to_keep.test(cap)) {
             value[0] = cap;
-            if (cap_set_flag(caps, CAP_INHERITABLE, arraysize(value), value, CAP_SET) != 0 ||
-                cap_set_flag(caps, CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0) {
+            if (cap_set_flag(caps.get(), CAP_INHERITABLE, arraysize(value), value, CAP_SET) != 0 ||
+                cap_set_flag(caps.get(), CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0) {
                 PLOG(ERROR) << "cap_set_flag(INHERITABLE|PERMITTED, " << cap << ") failed";
                 return false;
             }
@@ -123,14 +124,14 @@
 
     if (add_setpcap) {
         value[0] = CAP_SETPCAP;
-        if (cap_set_flag(caps, CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0 ||
-            cap_set_flag(caps, CAP_EFFECTIVE, arraysize(value), value, CAP_SET) != 0) {
+        if (cap_set_flag(caps.get(), CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0 ||
+            cap_set_flag(caps.get(), CAP_EFFECTIVE, arraysize(value), value, CAP_SET) != 0) {
             PLOG(ERROR) << "cap_set_flag(PERMITTED|EFFECTIVE, " << CAP_SETPCAP << ") failed";
             return false;
         }
     }
 
-    if (cap_set_proc(caps) != 0) {
+    if (cap_set_proc(caps.get()) != 0) {
         PLOG(ERROR) << "cap_set_proc(" << to_keep.to_ulong() << ") failed";
         return false;
     }
@@ -192,3 +193,6 @@
     // See http://man7.org/linux/man-pages/man7/capabilities.7.html.
     return SetAmbientCaps(to_keep);
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/capabilities.h b/init/capabilities.h
index abd7fb2..ede85c3 100644
--- a/init/capabilities.h
+++ b/init/capabilities.h
@@ -15,16 +15,28 @@
 #ifndef _INIT_CAPABILITIES_H
 #define _INIT_CAPABILITIES_H
 
-#include <linux/capability.h>
+#include <sys/capability.h>
 
 #include <bitset>
 #include <string>
+#include <type_traits>
+
+namespace android {
+namespace init {
+
+struct CapDeleter {
+    void operator()(cap_t caps) const { cap_free(caps); }
+};
 
 using CapSet = std::bitset<CAP_LAST_CAP + 1>;
+using ScopedCaps = std::unique_ptr<std::remove_pointer<cap_t>::type, CapDeleter>;
 
 int LookupCap(const std::string& cap_name);
 bool CapAmbientSupported();
 unsigned int GetLastValidCap();
 bool SetCapsForExec(const CapSet& to_keep);
 
+}  // namespace init
+}  // namespace android
+
 #endif  // _INIT_CAPABILITIES_H
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
index 6f729a3..0cb639a 100644
--- a/init/descriptors.cpp
+++ b/init/descriptors.cpp
@@ -31,6 +31,9 @@
 #include "init.h"
 #include "util.h"
 
+namespace android {
+namespace init {
+
 DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
                                gid_t gid, int perm, const std::string& context)
         : name_(name), type_(type), uid_(uid), gid_(gid), perm_(perm), context_(context) {
@@ -58,7 +61,7 @@
   std::for_each(publishedName.begin(), publishedName.end(),
                 [] (char& c) { c = isalnum(c) ? c : '_'; });
 
-  std::string val = android::base::StringPrintf("%d", fd);
+  std::string val = std::to_string(fd);
   add_environment(publishedName.c_str(), val.c_str());
 
   // make sure we don't close on exec
@@ -74,7 +77,8 @@
 }
 
 void SocketInfo::Clean() const {
-  unlink(android::base::StringPrintf(ANDROID_SOCKET_DIR "/%s", name().c_str()).c_str());
+    std::string path = android::base::StringPrintf("%s/%s", ANDROID_SOCKET_DIR, name().c_str());
+    unlink(path.c_str());
 }
 
 int SocketInfo::Create(const std::string& context) const {
@@ -125,3 +129,6 @@
 const std::string FileInfo::key() const {
   return ANDROID_FILE_ENV_PREFIX;
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/descriptors.h b/init/descriptors.h
index ff276fb..3bdddfe 100644
--- a/init/descriptors.h
+++ b/init/descriptors.h
@@ -22,6 +22,9 @@
 
 #include <string>
 
+namespace android {
+namespace init {
+
 class DescriptorInfo {
  public:
   DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
@@ -75,4 +78,7 @@
   virtual const std::string key() const override;
 };
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/devices.cpp b/init/devices.cpp
index e0351a3..13cf991 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2014 The Android Open Source Project
+ * Copyright (C) 2007 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.
@@ -16,42 +16,20 @@
 
 #include "devices.h"
 
-#include <dirent.h>
 #include <errno.h>
-#include <fcntl.h>
 #include <fnmatch.h>
-#include <grp.h>
-#include <libgen.h>
-#include <linux/netlink.h>
-#include <pwd.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/sendfile.h>
-#include <sys/socket.h>
 #include <sys/sysmacros.h>
-#include <sys/time.h>
-#include <sys/un.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
-#include <algorithm>
 #include <memory>
-#include <thread>
 
-#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <cutils/uevent.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
-#include <selinux/label.h>
 #include <selinux/selinux.h>
 
-#include "keyword_map.h"
 #include "ueventd.h"
 #include "util.h"
 
@@ -59,305 +37,23 @@
 #error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
 #endif
 
-static selabel_handle* sehandle;
+using android::base::Basename;
+using android::base::Dirname;
+using android::base::Readlink;
+using android::base::Realpath;
+using android::base::StartsWith;
+using android::base::StringPrintf;
 
-static android::base::unique_fd device_fd;
-
-Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
-    : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
-    // If the first * is the last character, then we'll treat name_ as a prefix
-    // Otherwise, if a * is present, then we do a full fnmatch().
-    auto wildcard_position = name_.find('*');
-    if (wildcard_position == name_.length() - 1) {
-        prefix_ = true;
-        name_.pop_back();
-    } else if (wildcard_position != std::string::npos) {
-        wildcard_ = true;
-    }
-}
-
-bool Permissions::Match(const std::string& path) const {
-    if (prefix_) {
-        return android::base::StartsWith(path, name_.c_str());
-    } else if (wildcard_) {
-        return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
-    } else {
-        return path == name_;
-    }
-
-    return false;
-}
-
-bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
-                                          const std::string& subsystem) const {
-    std::string path_basename = android::base::Basename(path);
-    if (name().find(subsystem) != std::string::npos) {
-        if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
-        if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
-    }
-    return Match(path);
-}
-
-void SysfsPermissions::SetPermissions(const std::string& path) const {
-    std::string attribute_file = path + "/" + attribute_;
-    LOG(INFO) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
-              << perm();
-    chown(attribute_file.c_str(), uid(), gid());
-    chmod(attribute_file.c_str(), perm());
-}
-
-// TODO: Move these to be member variables of a future devices class.
-std::vector<Permissions> dev_permissions;
-std::vector<SysfsPermissions> sysfs_permissions;
-
-bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs) {
-    if (is_sysfs && args.size() != 5) {
-        *err = "/sys/ lines must have 5 entries";
-        return false;
-    }
-
-    if (!is_sysfs && args.size() != 4) {
-        *err = "/dev/ lines must have 4 entries";
-        return false;
-    }
-
-    auto it = args.begin();
-    const std::string& name = *it++;
-
-    std::string sysfs_attribute;
-    if (is_sysfs) sysfs_attribute = *it++;
-
-    // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>
-    std::string& perm_string = *it++;
-    char* end_pointer = 0;
-    mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
-    if (end_pointer == nullptr || *end_pointer != '\0') {
-        *err = "invalid mode '" + perm_string + "'";
-        return false;
-    }
-
-    std::string& uid_string = *it++;
-    passwd* pwd = getpwnam(uid_string.c_str());
-    if (!pwd) {
-        *err = "invalid uid '" + uid_string + "'";
-        return false;
-    }
-    uid_t uid = pwd->pw_uid;
-
-    std::string& gid_string = *it++;
-    struct group* grp = getgrnam(gid_string.c_str());
-    if (!grp) {
-        *err = "invalid gid '" + gid_string + "'";
-        return false;
-    }
-    gid_t gid = grp->gr_gid;
-
-    if (is_sysfs) {
-        sysfs_permissions.emplace_back(name, sysfs_attribute, perm, uid, gid);
-    } else {
-        dev_permissions.emplace_back(name, perm, uid, gid);
-    }
-    return true;
-}
-
-// TODO: Move this to be a member variable of a future devices class.
-static std::vector<Subsystem> subsystems;
-
-std::string Subsystem::ParseDevPath(uevent* uevent) const {
-    std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
-                              ? uevent->device_name
-                              : android::base::Basename(uevent->path);
-
-    return dir_name_ + "/" + devname;
-}
-
-bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                   int line, std::string* err) {
-    if (args.size() != 2) {
-        *err = "subsystems must have exactly one name";
-        return false;
-    }
-
-    if (std::find(subsystems.begin(), subsystems.end(), args[1]) != subsystems.end()) {
-        *err = "ignoring duplicate subsystem entry";
-        return false;
-    }
-
-    subsystem_.name_ = args[1];
-
-    return true;
-}
-
-bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
-    if (args[1] == "uevent_devname") {
-        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
-        return true;
-    }
-    if (args[1] == "uevent_devpath") {
-        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
-        return true;
-    }
-
-    *err = "invalid devname '" + args[1] + "'";
-    return false;
-}
-
-bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
-    if (args[1].front() != '/') {
-        *err = "dirname '" + args[1] + " ' does not start with '/'";
-        return false;
-    }
-
-    subsystem_.dir_name_ = args[1];
-    return true;
-}
-
-bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
-    using OptionParser =
-        bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
-    static class OptionParserMap : public KeywordMap<OptionParser> {
-      private:
-        const Map& map() const override {
-            // clang-format off
-            static const Map option_parsers = {
-                {"devname",     {1,     1,      &SubsystemParser::ParseDevName}},
-                {"dirname",     {1,     1,      &SubsystemParser::ParseDirName}},
-            };
-            // clang-format on
-            return option_parsers;
-        }
-    } parser_map;
-
-    auto parser = parser_map.FindFunction(args, err);
-
-    if (!parser) {
-        return false;
-    }
-
-    return (this->*parser)(std::move(args), err);
-}
-
-void SubsystemParser::EndSection() {
-    subsystems.emplace_back(std::move(subsystem_));
-}
-
-static void fixup_sys_permissions(const std::string& upath, const std::string& subsystem) {
-    // upaths omit the "/sys" that paths in this list
-    // contain, so we prepend it...
-    std::string path = "/sys" + upath;
-
-    for (const auto& s : sysfs_permissions) {
-        if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
-    }
-
-    if (access(path.c_str(), F_OK) == 0) {
-        LOG(VERBOSE) << "restorecon_recursive: " << path;
-        selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
-    }
-}
-
-static std::tuple<mode_t, uid_t, gid_t> get_device_permissions(
-    const std::string& path, const std::vector<std::string>& links) {
-    // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
-    for (auto it = dev_permissions.rbegin(); it != dev_permissions.rend(); ++it) {
-        if (it->Match(path) || std::any_of(links.begin(), links.end(),
-                                           [it](const auto& link) { return it->Match(link); })) {
-            return {it->perm(), it->uid(), it->gid()};
-        }
-    }
-    /* Default if nothing found. */
-    return {0600, 0, 0};
-}
-
-static void make_device(const std::string& path, int block, int major, int minor,
-                        const std::vector<std::string>& links) {
-    dev_t dev;
-    char *secontext = NULL;
-
-    auto [mode, uid, gid] = get_device_permissions(path, links);
-    mode |= (block ? S_IFBLK : S_IFCHR);
-
-    if (sehandle) {
-        std::vector<const char*> c_links;
-        for (const auto& link : links) {
-            c_links.emplace_back(link.c_str());
-        }
-        c_links.emplace_back(nullptr);
-        if (selabel_lookup_best_match(sehandle, &secontext, path.c_str(), &c_links[0], mode)) {
-            PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
-            return;
-        }
-        setfscreatecon(secontext);
-    }
-
-    dev = makedev(major, minor);
-    /* Temporarily change egid to avoid race condition setting the gid of the
-     * device node. Unforunately changing the euid would prevent creation of
-     * some device nodes, so the uid has to be set with chown() and is still
-     * racy. Fixing the gid race at least fixed the issue with system_server
-     * opening dynamic input devices under the AID_INPUT gid. */
-    if (setegid(gid)) {
-        PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
-        goto out;
-    }
-    /* If the node already exists update its SELinux label to handle cases when
-     * it was created with the wrong context during coldboot procedure. */
-    if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
-        char* fcon = nullptr;
-        int rc = lgetfilecon(path.c_str(), &fcon);
-        if (rc < 0) {
-            PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
-            goto out;
-        }
-
-        bool different = strcmp(fcon, secontext) != 0;
-        freecon(fcon);
-
-        if (different && lsetfilecon(path.c_str(), secontext)) {
-            PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path << "' device";
-        }
-    }
-
-out:
-    chown(path.c_str(), uid, -1);
-    if (setegid(AID_ROOT)) {
-        PLOG(FATAL) << "setegid(AID_ROOT) failed";
-    }
-
-    if (secontext) {
-        freecon(secontext);
-        setfscreatecon(NULL);
-    }
-}
-
-// TODO: Move this to be a member variable of a future devices class.
-std::vector<std::string> platform_devices;
-
-// Given a path that may start with a platform device, find the length of the
-// platform device prefix.  If it doesn't start with a platform device, return false
-bool find_platform_device(const std::string& path, std::string* out_path) {
-    out_path->clear();
-    // platform_devices is searched backwards, since parents are added before their children,
-    // and we want to match as deep of a child as we can.
-    for (auto it = platform_devices.rbegin(); it != platform_devices.rend(); ++it) {
-        auto platform_device_path_length = it->length();
-        if (platform_device_path_length < path.length() &&
-            path[platform_device_path_length] == '/' &&
-            android::base::StartsWith(path, it->c_str())) {
-            *out_path = *it;
-            return true;
-        }
-    }
-    return false;
-}
+namespace android {
+namespace init {
 
 /* Given a path that may start with a PCI device, populate the supplied buffer
  * with the PCI domain/bus number and the peripheral ID and return 0.
  * If it doesn't start with a PCI device, or there is some error, return -1 */
-static bool find_pci_device_prefix(const std::string& path, std::string* result) {
+static bool FindPciDevicePrefix(const std::string& path, std::string* result) {
     result->clear();
 
-    if (!android::base::StartsWith(path, "/devices/pci")) return false;
+    if (!StartsWith(path, "/devices/pci")) return false;
 
     /* Beginning of the prefix is the initial "pci" after "/devices/" */
     std::string::size_type start = 9;
@@ -385,10 +81,10 @@
  * the supplied buffer with the virtual block device ID and return 0.
  * If it doesn't start with a virtual block device, or there is some
  * error, return -1 */
-static bool find_vbd_device_prefix(const std::string& path, std::string* result) {
+static bool FindVbdDevicePrefix(const std::string& path, std::string* result) {
     result->clear();
 
-    if (!android::base::StartsWith(path, "/devices/vbd-")) return false;
+    if (!StartsWith(path, "/devices/vbd-")) return false;
 
     /* Beginning of the prefix is the initial "vbd-" after "/devices/" */
     std::string::size_type start = 13;
@@ -405,95 +101,187 @@
     return true;
 }
 
-void parse_event(const char* msg, uevent* uevent) {
-    uevent->partition_num = -1;
-    uevent->major = -1;
-    uevent->minor = -1;
-    // currently ignoring SEQNUM
-    while(*msg) {
-        if(!strncmp(msg, "ACTION=", 7)) {
-            msg += 7;
-            uevent->action = msg;
-        } else if(!strncmp(msg, "DEVPATH=", 8)) {
-            msg += 8;
-            uevent->path = msg;
-        } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
-            msg += 10;
-            uevent->subsystem = msg;
-        } else if(!strncmp(msg, "FIRMWARE=", 9)) {
-            msg += 9;
-            uevent->firmware = msg;
-        } else if(!strncmp(msg, "MAJOR=", 6)) {
-            msg += 6;
-            uevent->major = atoi(msg);
-        } else if(!strncmp(msg, "MINOR=", 6)) {
-            msg += 6;
-            uevent->minor = atoi(msg);
-        } else if(!strncmp(msg, "PARTN=", 6)) {
-            msg += 6;
-            uevent->partition_num = atoi(msg);
-        } else if(!strncmp(msg, "PARTNAME=", 9)) {
-            msg += 9;
-            uevent->partition_name = msg;
-        } else if(!strncmp(msg, "DEVNAME=", 8)) {
-            msg += 8;
-            uevent->device_name = msg;
+Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
+    : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
+    // Set 'prefix_' or 'wildcard_' based on the below cases:
+    //
+    // 1) No '*' in 'name' -> Neither are set and Match() checks a given path for strict
+    //    equality with 'name'
+    //
+    // 2) '*' only appears as the last character in 'name' -> 'prefix'_ is set to true and
+    //    Match() checks if 'name' is a prefix of a given path.
+    //
+    // 3) '*' appears elsewhere -> 'wildcard_' is set to true and Match() uses fnmatch()
+    //    with FNM_PATHNAME to compare 'name' to a given path.
+
+    auto wildcard_position = name_.find('*');
+    if (wildcard_position != std::string::npos) {
+        if (wildcard_position == name_.length() - 1) {
+            prefix_ = true;
+            name_.pop_back();
+        } else {
+            wildcard_ = true;
         }
-
-        // advance to after the next \0
-        while(*msg++)
-            ;
-    }
-
-    if (LOG_UEVENTS) {
-        LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
-                  << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
-                  << ", " << uevent->minor << " }";
     }
 }
 
-std::vector<std::string> get_character_device_symlinks(uevent* uevent) {
-    std::string parent_device;
-    if (!find_platform_device(uevent->path, &parent_device)) return {};
+bool Permissions::Match(const std::string& path) const {
+    if (prefix_) return StartsWith(path, name_.c_str());
+    if (wildcard_) return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
+    return path == name_;
+}
 
-    // skip path to the parent driver
-    std::string path = uevent->path.substr(parent_device.length());
+bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
+                                          const std::string& subsystem) const {
+    std::string path_basename = Basename(path);
+    if (name().find(subsystem) != std::string::npos) {
+        if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
+        if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
+    }
+    return Match(path);
+}
 
-    if (!android::base::StartsWith(path, "/usb")) return {};
+void SysfsPermissions::SetPermissions(const std::string& path) const {
+    std::string attribute_file = path + "/" + attribute_;
+    LOG(VERBOSE) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
+                 << perm();
 
-    // skip root hub name and device. use device interface
-    // skip 3 slashes, including the first / by starting the search at the 1st character, not 0th.
-    // then extract what comes between the 3rd and 4th slash
-    // e.g. "/usb/usb_device/name/tty2-1:1.0" -> "name"
+    if (access(attribute_file.c_str(), F_OK) == 0) {
+        if (chown(attribute_file.c_str(), uid(), gid()) != 0) {
+            PLOG(ERROR) << "chown(" << attribute_file << ", " << uid() << ", " << gid()
+                        << ") failed";
+        }
+        if (chmod(attribute_file.c_str(), perm()) != 0) {
+            PLOG(ERROR) << "chmod(" << attribute_file << ", " << perm() << ") failed";
+        }
+    }
+}
 
-    std::string::size_type start = 0;
-    start = path.find('/', start + 1);
-    if (start == std::string::npos) return {};
+// Given a path that may start with a platform device, find the parent platform device by finding a
+// parent directory with a 'subsystem' symlink that points to the platform bus.
+// If it doesn't start with a platform device, return false
+bool DeviceHandler::FindPlatformDevice(std::string path, std::string* platform_device_path) const {
+    platform_device_path->clear();
 
-    start = path.find('/', start + 1);
-    if (start == std::string::npos) return {};
+    // Uevents don't contain the mount point, so we need to add it here.
+    path.insert(0, sysfs_mount_point_);
 
-    auto end = path.find('/', start + 1);
-    if (end == std::string::npos) return {};
+    std::string directory = Dirname(path);
 
-    start++;  // Skip the first '/'
+    while (directory != "/" && directory != ".") {
+        std::string subsystem_link_path;
+        if (Realpath(directory + "/subsystem", &subsystem_link_path) &&
+            subsystem_link_path == sysfs_mount_point_ + "/bus/platform") {
+            // We need to remove the mount point that we added above before returning.
+            directory.erase(0, sysfs_mount_point_.size());
+            *platform_device_path = directory;
+            return true;
+        }
 
-    auto length = end - start;
-    if (length == 0) return {};
+        auto last_slash = path.rfind('/');
+        if (last_slash == std::string::npos) return false;
 
-    auto name_string = path.substr(start, length);
+        path.erase(last_slash);
+        directory = Dirname(path);
+    }
 
-    std::vector<std::string> links;
-    links.emplace_back("/dev/usb/" + uevent->subsystem + name_string);
+    return false;
+}
 
-    mkdir("/dev/usb", 0755);
+void DeviceHandler::FixupSysPermissions(const std::string& upath,
+                                        const std::string& subsystem) const {
+    // upaths omit the "/sys" that paths in this list
+    // contain, so we prepend it...
+    std::string path = "/sys" + upath;
 
-    return links;
+    for (const auto& s : sysfs_permissions_) {
+        if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
+    }
+
+    if (!skip_restorecon_ && access(path.c_str(), F_OK) == 0) {
+        LOG(VERBOSE) << "restorecon_recursive: " << path;
+        if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+            PLOG(ERROR) << "selinux_android_restorecon(" << path << ") failed";
+        }
+    }
+}
+
+std::tuple<mode_t, uid_t, gid_t> DeviceHandler::GetDevicePermissions(
+    const std::string& path, const std::vector<std::string>& links) const {
+    // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
+    for (auto it = dev_permissions_.crbegin(); it != dev_permissions_.crend(); ++it) {
+        if (it->Match(path) || std::any_of(links.cbegin(), links.cend(),
+                                           [it](const auto& link) { return it->Match(link); })) {
+            return {it->perm(), it->uid(), it->gid()};
+        }
+    }
+    /* Default if nothing found. */
+    return {0600, 0, 0};
+}
+
+void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, int minor,
+                               const std::vector<std::string>& links) const {
+    auto[mode, uid, gid] = GetDevicePermissions(path, links);
+    mode |= (block ? S_IFBLK : S_IFCHR);
+
+    char* secontext = nullptr;
+    if (sehandle_) {
+        std::vector<const char*> c_links;
+        for (const auto& link : links) {
+            c_links.emplace_back(link.c_str());
+        }
+        c_links.emplace_back(nullptr);
+        if (selabel_lookup_best_match(sehandle_, &secontext, path.c_str(), &c_links[0], mode)) {
+            PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
+            return;
+        }
+        setfscreatecon(secontext);
+    }
+
+    dev_t dev = makedev(major, minor);
+    /* Temporarily change egid to avoid race condition setting the gid of the
+     * device node. Unforunately changing the euid would prevent creation of
+     * some device nodes, so the uid has to be set with chown() and is still
+     * racy. Fixing the gid race at least fixed the issue with system_server
+     * opening dynamic input devices under the AID_INPUT gid. */
+    if (setegid(gid)) {
+        PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
+        goto out;
+    }
+    /* If the node already exists update its SELinux label to handle cases when
+     * it was created with the wrong context during coldboot procedure. */
+    if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
+        char* fcon = nullptr;
+        int rc = lgetfilecon(path.c_str(), &fcon);
+        if (rc < 0) {
+            PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
+            goto out;
+        }
+
+        bool different = strcmp(fcon, secontext) != 0;
+        freecon(fcon);
+
+        if (different && lsetfilecon(path.c_str(), secontext)) {
+            PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
+                        << "' device";
+        }
+    }
+
+out:
+    chown(path.c_str(), uid, -1);
+    if (setegid(AID_ROOT)) {
+        PLOG(FATAL) << "setegid(AID_ROOT) failed";
+    }
+
+    if (secontext) {
+        freecon(secontext);
+        setfscreatecon(nullptr);
+    }
 }
 
 // replaces any unacceptable characters with '_', the
 // length of the resulting string is equal to the input string
-void sanitize_partition_name(std::string* string) {
+void SanitizePartitionName(std::string* string) {
     const char* accept =
         "abcdefghijklmnopqrstuvwxyz"
         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -508,25 +296,25 @@
     }
 }
 
-std::vector<std::string> get_block_device_symlinks(uevent* uevent) {
+std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {
     std::string device;
     std::string type;
 
-    if (find_platform_device(uevent->path, &device)) {
+    if (FindPlatformDevice(uevent.path, &device)) {
         // Skip /devices/platform or /devices/ if present
         static const std::string devices_platform_prefix = "/devices/platform/";
         static const std::string devices_prefix = "/devices/";
 
-        if (android::base::StartsWith(device, devices_platform_prefix.c_str())) {
+        if (StartsWith(device, devices_platform_prefix.c_str())) {
             device = device.substr(devices_platform_prefix.length());
-        } else if (android::base::StartsWith(device, devices_prefix.c_str())) {
+        } else if (StartsWith(device, devices_prefix.c_str())) {
             device = device.substr(devices_prefix.length());
         }
 
         type = "platform";
-    } else if (find_pci_device_prefix(uevent->path, &device)) {
+    } else if (FindPciDevicePrefix(uevent.path, &device)) {
         type = "pci";
-    } else if (find_vbd_device_prefix(uevent->path, &device)) {
+    } else if (FindVbdDevicePrefix(uevent.path, &device)) {
         type = "vbd";
     } else {
         return {};
@@ -538,379 +326,113 @@
 
     auto link_path = "/dev/block/" + type + "/" + device;
 
-    if (!uevent->partition_name.empty()) {
-        std::string partition_name_sanitized(uevent->partition_name);
-        sanitize_partition_name(&partition_name_sanitized);
-        if (partition_name_sanitized != uevent->partition_name) {
-            LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '"
+    if (!uevent.partition_name.empty()) {
+        std::string partition_name_sanitized(uevent.partition_name);
+        SanitizePartitionName(&partition_name_sanitized);
+        if (partition_name_sanitized != uevent.partition_name) {
+            LOG(VERBOSE) << "Linking partition '" << uevent.partition_name << "' as '"
                          << partition_name_sanitized << "'";
         }
         links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
     }
 
-    if (uevent->partition_num >= 0) {
-        links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent->partition_num));
+    if (uevent.partition_num >= 0) {
+        links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent.partition_num));
     }
 
-    auto last_slash = uevent->path.rfind('/');
-    links.emplace_back(link_path + "/" + uevent->path.substr(last_slash + 1));
+    auto last_slash = uevent.path.rfind('/');
+    links.emplace_back(link_path + "/" + uevent.path.substr(last_slash + 1));
 
     return links;
 }
 
-static void make_link_init(const std::string& oldpath, const std::string& newpath) {
-    if (mkdir_recursive(dirname(newpath.c_str()), 0755, sehandle)) {
-        PLOG(ERROR) << "Failed to create directory " << dirname(newpath.c_str());
-    }
-
-    if (symlink(oldpath.c_str(), newpath.c_str()) && errno != EEXIST) {
-        PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
-    }
-}
-
-static void remove_link(const std::string& oldpath, const std::string& newpath) {
-    std::string path;
-    if (android::base::Readlink(newpath, &path) && path == oldpath) unlink(newpath.c_str());
-}
-
-static void handle_device(const std::string& action, const std::string& devpath, int block,
-                          int major, int minor, const std::vector<std::string>& links) {
+void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, bool block,
+                                 int major, int minor, const std::vector<std::string>& links) const {
     if (action == "add") {
-        make_device(devpath, block, major, minor, links);
+        MakeDevice(devpath, block, major, minor, links);
         for (const auto& link : links) {
-            make_link_init(devpath, link);
+            if (mkdir_recursive(Dirname(link), 0755, sehandle_)) {
+                PLOG(ERROR) << "Failed to create directory " << Dirname(link);
+            }
+
+            if (symlink(devpath.c_str(), link.c_str()) && errno != EEXIST) {
+                PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
+            }
         }
     }
 
     if (action == "remove") {
         for (const auto& link : links) {
-            remove_link(devpath, link);
+            std::string link_path;
+            if (Readlink(link, &link_path) && link_path == devpath) {
+                unlink(link.c_str());
+            }
         }
         unlink(devpath.c_str());
     }
 }
 
-void handle_platform_device_event(uevent* uevent) {
-    if (uevent->action == "add") {
-        platform_devices.emplace_back(uevent->path);
-    } else if (uevent->action == "remove") {
-        auto it = std::find(platform_devices.begin(), platform_devices.end(), uevent->path);
-        if (it != platform_devices.end()) platform_devices.erase(it);
-    }
-}
-
-static void handle_block_device_event(uevent* uevent) {
-    // if it's not a /dev device, nothing to do
-    if (uevent->major < 0 || uevent->minor < 0) return;
-
-    const char* base = "/dev/block/";
-    make_dir(base, 0755, sehandle);
-
-    std::string name = android::base::Basename(uevent->path);
-    std::string devpath = base + name;
-
-    std::vector<std::string> links;
-    if (android::base::StartsWith(uevent->path, "/devices")) {
-        links = get_block_device_symlinks(uevent);
+void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
+    if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
+        FixupSysPermissions(uevent.path, uevent.subsystem);
     }
 
-    handle_device(uevent->action, devpath, 1, uevent->major, uevent->minor, links);
-}
-
-static void handle_generic_device_event(uevent* uevent) {
     // if it's not a /dev device, nothing to do
-    if (uevent->major < 0 || uevent->minor < 0) return;
+    if (uevent.major < 0 || uevent.minor < 0) return;
 
     std::string devpath;
+    std::vector<std::string> links;
+    bool block = false;
 
-    if (android::base::StartsWith(uevent->subsystem, "usb")) {
-        if (uevent->subsystem == "usb") {
-            if (!uevent->device_name.empty()) {
-                devpath = "/dev/" + uevent->device_name;
+    if (uevent.subsystem == "block") {
+        block = true;
+        devpath = "/dev/block/" + Basename(uevent.path);
+
+        if (StartsWith(uevent.path, "/devices")) {
+            links = GetBlockDeviceSymlinks(uevent);
+        }
+    } else if (StartsWith(uevent.subsystem, "usb")) {
+        if (uevent.subsystem == "usb") {
+            if (!uevent.device_name.empty()) {
+                devpath = "/dev/" + uevent.device_name;
             } else {
                 // This imitates the file system that would be created
                 // if we were using devfs instead.
                 // Minors are broken up into groups of 128, starting at "001"
-                int bus_id = uevent->minor / 128 + 1;
-                int device_id = uevent->minor % 128 + 1;
-                devpath = android::base::StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
+                int bus_id = uevent.minor / 128 + 1;
+                int device_id = uevent.minor % 128 + 1;
+                devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
             }
         } else {
             // ignore other USB events
             return;
         }
-    } else if (auto subsystem = std::find(subsystems.begin(), subsystems.end(), uevent->subsystem);
-               subsystem != subsystems.end()) {
+    } else if (const auto subsystem =
+                   std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
+               subsystem != subsystems_.cend()) {
         devpath = subsystem->ParseDevPath(uevent);
     } else {
-        devpath = "/dev/" + android::base::Basename(uevent->path);
+        devpath = "/dev/" + Basename(uevent.path);
     }
 
-    mkdir_recursive(android::base::Dirname(devpath), 0755, sehandle);
+    mkdir_recursive(Dirname(devpath), 0755, sehandle_);
 
-    auto links = get_character_device_symlinks(uevent);
-
-    handle_device(uevent->action, devpath, 0, uevent->major, uevent->minor, links);
+    HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
 }
 
-static void handle_device_event(struct uevent *uevent)
-{
-    if (uevent->action == "add" || uevent->action == "change" || uevent->action == "online") {
-        fixup_sys_permissions(uevent->path, uevent->subsystem);
-    }
+DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
+                             std::vector<SysfsPermissions> sysfs_permissions,
+                             std::vector<Subsystem> subsystems, bool skip_restorecon)
+    : dev_permissions_(std::move(dev_permissions)),
+      sysfs_permissions_(std::move(sysfs_permissions)),
+      subsystems_(std::move(subsystems)),
+      sehandle_(selinux_android_file_context_handle()),
+      skip_restorecon_(skip_restorecon),
+      sysfs_mount_point_("/sys") {}
 
-    if (uevent->subsystem == "block") {
-        handle_block_device_event(uevent);
-    } else if (uevent->subsystem == "platform") {
-        handle_platform_device_event(uevent);
-    } else {
-        handle_generic_device_event(uevent);
-    }
-}
+DeviceHandler::DeviceHandler()
+    : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
+                    std::vector<Subsystem>{}, false) {}
 
-static void load_firmware(uevent* uevent, const std::string& root,
-                          int fw_fd, size_t fw_size,
-                          int loading_fd, int data_fd) {
-    // Start transfer.
-    android::base::WriteFully(loading_fd, "1", 1);
-
-    // Copy the firmware.
-    int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
-    if (rc == -1) {
-        PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent->firmware << "' }";
-    }
-
-    // Tell the firmware whether to abort or commit.
-    const char* response = (rc != -1) ? "0" : "-1";
-    android::base::WriteFully(loading_fd, response, strlen(response));
-}
-
-static int is_booting() {
-    return access("/dev/.booting", F_OK) == 0;
-}
-
-static void process_firmware_event(uevent* uevent) {
-    int booting = is_booting();
-
-    LOG(INFO) << "firmware: loading '" << uevent->firmware << "' for '" << uevent->path << "'";
-
-    std::string root = "/sys" + uevent->path;
-    std::string loading = root + "/loading";
-    std::string data = root + "/data";
-
-    android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY|O_CLOEXEC));
-    if (loading_fd == -1) {
-        PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent->firmware;
-        return;
-    }
-
-    android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY|O_CLOEXEC));
-    if (data_fd == -1) {
-        PLOG(ERROR) << "couldn't open firmware data fd for " << uevent->firmware;
-        return;
-    }
-
-    static const char* firmware_dirs[] = {"/etc/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;
-        android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY|O_CLOEXEC));
-        struct stat sb;
-        if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
-            load_firmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
-            return;
-        }
-    }
-
-    if (booting) {
-        // If we're not fully booted, we may be missing
-        // filesystems needed for firmware, wait and retry.
-        std::this_thread::sleep_for(100ms);
-        booting = is_booting();
-        goto try_loading_again;
-    }
-
-    LOG(ERROR) << "firmware: could not find firmware for " << uevent->firmware;
-
-    // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
-    write(loading_fd, "-1", 2);
-}
-
-static void handle_firmware_event(uevent* uevent) {
-    if (uevent->subsystem != "firmware" || uevent->action != "add") return;
-
-    // Loading the firmware in a child means we can do that in parallel...
-    // (We ignore SIGCHLD rather than wait for our children.)
-    pid_t pid = fork();
-    if (pid == 0) {
-        Timer t;
-        process_firmware_event(uevent);
-        LOG(INFO) << "loading " << uevent->path << " took " << t;
-        _exit(EXIT_SUCCESS);
-    } else if (pid == -1) {
-        PLOG(ERROR) << "could not fork to process firmware event for " << uevent->firmware;
-    }
-}
-
-static bool inline should_stop_coldboot(coldboot_action_t act)
-{
-    return (act == COLDBOOT_STOP || act == COLDBOOT_FINISH);
-}
-
-#define UEVENT_MSG_LEN  2048
-
-static inline coldboot_action_t handle_device_fd_with(
-        std::function<coldboot_action_t(uevent* uevent)> handle_uevent)
-{
-    char msg[UEVENT_MSG_LEN+2];
-    int n;
-    while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
-        if(n >= UEVENT_MSG_LEN)   /* overflow -- discard */
-            continue;
-
-        msg[n] = '\0';
-        msg[n+1] = '\0';
-
-        uevent uevent;
-        parse_event(msg, &uevent);
-        coldboot_action_t act = handle_uevent(&uevent);
-        if (should_stop_coldboot(act))
-            return act;
-    }
-
-    return COLDBOOT_CONTINUE;
-}
-
-coldboot_action_t handle_device_fd(coldboot_callback fn)
-{
-    coldboot_action_t ret = handle_device_fd_with(
-        [&](uevent* uevent) -> coldboot_action_t {
-            // default is to always create the devices
-            coldboot_action_t act = COLDBOOT_CREATE;
-            if (fn) {
-                act = fn(uevent);
-            }
-
-            if (act == COLDBOOT_CREATE || act == COLDBOOT_STOP) {
-                handle_device_event(uevent);
-                handle_firmware_event(uevent);
-            }
-
-            return act;
-        });
-
-    return ret;
-}
-
-/* Coldboot walks parts of the /sys tree and pokes the uevent files
-** to cause the kernel to regenerate device add events that happened
-** before init's device manager was started
-**
-** We drain any pending events from the netlink socket every time
-** we poke another uevent file to make sure we don't overrun the
-** socket's buffer.
-*/
-
-static coldboot_action_t do_coldboot(DIR *d, coldboot_callback fn)
-{
-    struct dirent *de;
-    int dfd, fd;
-    coldboot_action_t act = COLDBOOT_CONTINUE;
-
-    dfd = dirfd(d);
-
-    fd = openat(dfd, "uevent", O_WRONLY);
-    if (fd >= 0) {
-        write(fd, "add\n", 4);
-        close(fd);
-        act = handle_device_fd(fn);
-        if (should_stop_coldboot(act))
-            return act;
-    }
-
-    while (!should_stop_coldboot(act) && (de = readdir(d))) {
-        DIR *d2;
-
-        if(de->d_type != DT_DIR || de->d_name[0] == '.')
-            continue;
-
-        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
-        if(fd < 0)
-            continue;
-
-        d2 = fdopendir(fd);
-        if(d2 == 0)
-            close(fd);
-        else {
-            act = do_coldboot(d2, fn);
-            closedir(d2);
-        }
-    }
-
-    // default is always to continue looking for uevents
-    return act;
-}
-
-static coldboot_action_t coldboot(const char *path, coldboot_callback fn)
-{
-    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path), closedir);
-    if (d) {
-        return do_coldboot(d.get(), fn);
-    }
-
-    return COLDBOOT_CONTINUE;
-}
-
-void device_init(const char* path, coldboot_callback fn) {
-    if (!sehandle) {
-        sehandle = selinux_android_file_context_handle();
-    }
-    // open uevent socket and selinux status only if it hasn't been
-    // done before
-    if (device_fd == -1) {
-        /* is 256K enough? udev uses 16MB! */
-        device_fd.reset(uevent_open_socket(256 * 1024, true));
-        if (device_fd == -1) {
-            return;
-        }
-        fcntl(device_fd, F_SETFL, O_NONBLOCK);
-    }
-
-    if (access(COLDBOOT_DONE, F_OK) == 0) {
-        LOG(VERBOSE) << "Skipping coldboot, already done!";
-        return;
-    }
-
-    Timer t;
-    coldboot_action_t act;
-    if (!path) {
-        act = coldboot("/sys/class", fn);
-        if (!should_stop_coldboot(act)) {
-            act = coldboot("/sys/block", fn);
-            if (!should_stop_coldboot(act)) {
-                act = coldboot("/sys/devices", fn);
-            }
-        }
-    } else {
-        act = coldboot(path, fn);
-    }
-
-    // If we have a callback, then do as it says. If no, then the default is
-    // to always create COLDBOOT_DONE file.
-    if (!fn || (act == COLDBOOT_FINISH)) {
-        close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
-    }
-
-    LOG(INFO) << "Coldboot took " << t;
-}
-
-void device_close() {
-    platform_devices.clear();
-    device_fd.reset();
-}
-
-int get_device_fd() {
-    return device_fd;
-}
+}  // namespace init
+}  // namespace android
diff --git a/init/devices.h b/init/devices.h
index 647b4c4..c64f5fb 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -20,35 +20,17 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <functional>
+#include <algorithm>
 #include <string>
 #include <vector>
 
-#include "init_parser.h"
+#include <android-base/file.h>
+#include <selinux/label.h>
 
-enum coldboot_action_t {
-    // coldboot continues without creating the device for the uevent
-    COLDBOOT_CONTINUE = 0,
-    // coldboot continues after creating the device for the uevent
-    COLDBOOT_CREATE,
-    // coldboot stops after creating the device for uevent but doesn't
-    // create the COLDBOOT_DONE file
-    COLDBOOT_STOP,
-    // same as COLDBOOT_STOP, but creates the COLDBOOT_DONE file
-    COLDBOOT_FINISH
-};
+#include "uevent.h"
 
-struct uevent {
-    std::string action;
-    std::string path;
-    std::string subsystem;
-    std::string firmware;
-    std::string partition_name;
-    std::string device_name;
-    int partition_num;
-    int major;
-    int minor;
-};
+namespace android {
+namespace init {
 
 class Permissions {
   public:
@@ -93,9 +75,15 @@
 
     // 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(uevent* uevent) const;
+    std::string ParseDevPath(const Uevent& uevent) const {
+        std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
+                                  ? uevent.device_name
+                                  : android::base::Basename(uevent.path);
 
-    bool operator==(const std::string& string_name) { return name_ == string_name; }
+        return dir_name_ + "/" + devname;
+    }
+
+    bool operator==(const std::string& string_name) const { return name_ == string_name; }
 
   private:
     enum class DevnameSource {
@@ -108,34 +96,43 @@
     DevnameSource devname_source_;
 };
 
-class SubsystemParser : public SectionParser {
+class DeviceHandler {
   public:
-    SubsystemParser() {}
-    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
-                      std::string* err) override;
-    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
-    void EndSection() override;
+    friend class DeviceHandlerTester;
+
+    DeviceHandler();
+    DeviceHandler(std::vector<Permissions> dev_permissions,
+                  std::vector<SysfsPermissions> sysfs_permissions,
+                  std::vector<Subsystem> subsystems, bool skip_restorecon);
+    ~DeviceHandler(){};
+
+    void HandleDeviceEvent(const Uevent& uevent);
+
+    std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
+    void set_skip_restorecon(bool value) { skip_restorecon_ = value; }
 
   private:
-    bool ParseDevName(std::vector<std::string>&& args, std::string* err);
-    bool ParseDirName(std::vector<std::string>&& args, std::string* err);
+    bool FindPlatformDevice(std::string path, std::string* platform_device_path) const;
+    std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
+        const std::string& path, const std::vector<std::string>& links) const;
+    void MakeDevice(const std::string& path, bool block, int major, int minor,
+                    const std::vector<std::string>& links) const;
+    void HandleDevice(const std::string& action, const std::string& devpath, bool block, int major,
+                      int minor, const std::vector<std::string>& links) const;
+    void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
 
-    Subsystem subsystem_;
+    std::vector<Permissions> dev_permissions_;
+    std::vector<SysfsPermissions> sysfs_permissions_;
+    std::vector<Subsystem> subsystems_;
+    selabel_handle* sehandle_;
+    bool skip_restorecon_;
+    std::string sysfs_mount_point_;
 };
 
-bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs);
-typedef std::function<coldboot_action_t(struct uevent* uevent)> coldboot_callback;
-extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr);
-extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr);
-extern void device_close();
-int get_device_fd();
-
 // Exposed for testing
-extern std::vector<std::string> platform_devices;
-bool find_platform_device(const std::string& path, std::string* out_path);
-std::vector<std::string> get_character_device_symlinks(uevent* uevent);
-std::vector<std::string> get_block_device_symlinks(uevent* uevent);
-void sanitize_partition_name(std::string* string);
-void handle_platform_device_event(uevent* uevent);
+void SanitizePartitionName(std::string* string);
 
-#endif /* _INIT_DEVICES_H */
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index 66521db..ac4ab9b 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -16,173 +16,68 @@
 
 #include "devices.h"
 
-#include <string>
-#include <vector>
-
 #include <android-base/scopeguard.h>
+#include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
-void add_platform_device(const std::string& path) {
-    uevent uevent = {
-        .action = "add", .subsystem = "platform", .path = path,
-    };
-    handle_platform_device_event(&uevent);
-}
+#include "util.h"
 
-void remove_platform_device(const std::string& path) {
-    uevent uevent = {
-        .action = "remove", .subsystem = "platform", .path = path,
-    };
-    handle_platform_device_event(&uevent);
-}
+using namespace std::string_literals;
 
-template <std::vector<std::string> (*Function)(uevent*)>
-void test_get_symlinks(const std::string& platform_device_name, uevent* uevent,
-                       const std::vector<std::string> expected_links) {
-    add_platform_device(platform_device_name);
-    auto platform_device_remover = android::base::make_scope_guard(
-        [&platform_device_name]() { remove_platform_device(platform_device_name); });
+namespace android {
+namespace init {
 
-    std::vector<std::string> result = Function(uevent);
+class DeviceHandlerTester {
+  public:
+    void TestGetSymlinks(const std::string& platform_device, const Uevent& uevent,
+                         const std::vector<std::string> expected_links) {
+        TemporaryDir fake_sys_root;
+        device_handler_.sysfs_mount_point_ = fake_sys_root.path;
 
-    auto expected_size = expected_links.size();
-    ASSERT_EQ(expected_size, result.size());
-    if (expected_size == 0) return;
+        std::string platform_device_dir = fake_sys_root.path + platform_device;
+        mkdir_recursive(platform_device_dir, 0777, nullptr);
 
-    // Explicitly iterate so the results are visible if a failure occurs
-    for (unsigned int i = 0; i < expected_size; ++i) {
-        EXPECT_EQ(expected_links[i], result[i]);
+        std::string platform_bus = fake_sys_root.path + "/bus/platform"s;
+        mkdir_recursive(platform_bus, 0777, nullptr);
+        symlink(platform_bus.c_str(), (platform_device_dir + "/subsystem").c_str());
+
+        mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777, nullptr);
+
+        std::vector<std::string> result;
+        result = device_handler_.GetBlockDeviceSymlinks(uevent);
+
+        auto expected_size = expected_links.size();
+        ASSERT_EQ(expected_size, result.size());
+        if (expected_size == 0) return;
+
+        // Explicitly iterate so the results are visible if a failure occurs
+        for (unsigned int i = 0; i < expected_size; ++i) {
+            EXPECT_EQ(expected_links[i], result[i]);
+        }
     }
-}
 
-TEST(devices, handle_platform_device_event) {
-    platform_devices.clear();
-    add_platform_device("/devices/platform/some_device_name");
-    ASSERT_EQ(1U, platform_devices.size());
-    remove_platform_device("/devices/platform/some_device_name");
-    ASSERT_EQ(0U, platform_devices.size());
-}
+  private:
+    DeviceHandler device_handler_;
+};
 
-TEST(devices, find_platform_device) {
-    platform_devices.clear();
-    add_platform_device("/devices/platform/some_device_name");
-    add_platform_device("/devices/platform/some_device_name/longer");
-    add_platform_device("/devices/platform/other_device_name");
-    EXPECT_EQ(3U, platform_devices.size());
-
-    std::string out_path;
-    EXPECT_FALSE(find_platform_device("/devices/platform/not_found", &out_path));
-    EXPECT_EQ("", out_path);
-
-    EXPECT_FALSE(
-        find_platform_device("/devices/platform/some_device_name_with_same_prefix", &out_path));
-
-    EXPECT_TRUE(
-        find_platform_device("/devices/platform/some_device_name/longer/longer_child", &out_path));
-    EXPECT_EQ("/devices/platform/some_device_name/longer", out_path);
-
-    EXPECT_TRUE(find_platform_device("/devices/platform/some_device_name/other_child", &out_path));
-    EXPECT_EQ("/devices/platform/some_device_name", out_path);
-}
-
-TEST(devices, get_character_device_symlinks_success) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
-        .path = "/devices/platform/some_device_name/usb/usb_device/name/tty2-1:1.0",
-        .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result{"/dev/usb/ttyname"};
-
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_character_device_symlinks_no_pdev_match) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
-        .path = "/device/name/tty2-1:1.0", .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result;
-
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_character_device_symlinks_nothing_after_platform_device) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
-        .path = "/devices/platform/some_device_name", .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result;
-
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_character_device_symlinks_no_usb_found) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
-        .path = "/devices/platform/some_device_name/bad/bad/", .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result;
-
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_character_device_symlinks_no_roothub) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
-        .path = "/devices/platform/some_device_name/usb/", .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result;
-
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_character_device_symlinks_no_usb_device) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
-        .path = "/devices/platform/some_device_name/usb/usb_device/", .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result;
-
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_character_device_symlinks_no_final_slash) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
-        .path = "/devices/platform/some_device_name/usb/usb_device/name", .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result;
-
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_character_device_symlinks_no_final_name) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
-        .path = "/devices/platform/some_device_name/usb/usb_device//", .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result;
-
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_block_device_symlinks_success_platform) {
+TEST(device_handler, get_block_device_symlinks_success_platform) {
     // These are actual paths from bullhead
     const char* platform_device = "/devices/soc.0/f9824900.sdhci";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0",
         .partition_name = "",
         .partition_num = -1,
     };
     std::vector<std::string> expected_result{"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0"};
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
-TEST(devices, get_block_device_symlinks_success_platform_with_partition) {
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition) {
     // These are actual paths from bullhead
     const char* platform_device = "/devices/soc.0/f9824900.sdhci";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
         .partition_name = "modem",
         .partition_num = 1,
@@ -193,12 +88,13 @@
         "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
     };
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
-TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_num) {
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_num) {
     const char* platform_device = "/devices/soc.0/f9824900.sdhci";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
         .partition_name = "",
         .partition_num = 1,
@@ -208,12 +104,13 @@
         "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
     };
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
-TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_name) {
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_name) {
     const char* platform_device = "/devices/soc.0/f9824900.sdhci";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
         .partition_name = "modem",
         .partition_num = -1,
@@ -223,101 +120,107 @@
         "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
     };
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
-TEST(devices, get_block_device_symlinks_success_pci) {
+TEST(device_handler, get_block_device_symlinks_success_pci) {
     const char* platform_device = "/devices/do/not/match";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/pci0000:00/0000:00:1f.2/mmcblk0", .partition_name = "", .partition_num = -1,
     };
     std::vector<std::string> expected_result{"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0"};
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
-TEST(devices, get_block_device_symlinks_pci_bad_format) {
+TEST(device_handler, get_block_device_symlinks_pci_bad_format) {
     const char* platform_device = "/devices/do/not/match";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/pci//mmcblk0", .partition_name = "", .partition_num = -1,
     };
     std::vector<std::string> expected_result{};
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
-TEST(devices, get_block_device_symlinks_success_vbd) {
+TEST(device_handler, get_block_device_symlinks_success_vbd) {
     const char* platform_device = "/devices/do/not/match";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/vbd-1234/mmcblk0", .partition_name = "", .partition_num = -1,
     };
     std::vector<std::string> expected_result{"/dev/block/vbd/1234/mmcblk0"};
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
-TEST(devices, get_block_device_symlinks_vbd_bad_format) {
+TEST(device_handler, get_block_device_symlinks_vbd_bad_format) {
     const char* platform_device = "/devices/do/not/match";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/vbd-/mmcblk0", .partition_name = "", .partition_num = -1,
     };
     std::vector<std::string> expected_result{};
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
-TEST(devices, get_block_device_symlinks_no_matches) {
+TEST(device_handler, get_block_device_symlinks_no_matches) {
     const char* platform_device = "/devices/soc.0/f9824900.sdhci";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/soc.0/not_the_device/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
         .partition_name = "",
         .partition_num = -1,
     };
     std::vector<std::string> expected_result;
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
-TEST(devices, sanitize_null) {
-    sanitize_partition_name(nullptr);
+TEST(device_handler, sanitize_null) {
+    SanitizePartitionName(nullptr);
 }
 
-TEST(devices, sanitize_empty) {
+TEST(device_handler, sanitize_empty) {
     std::string empty;
-    sanitize_partition_name(&empty);
+    SanitizePartitionName(&empty);
     EXPECT_EQ(0u, empty.size());
 }
 
-TEST(devices, sanitize_allgood) {
+TEST(device_handler, sanitize_allgood) {
     std::string good =
         "abcdefghijklmnopqrstuvwxyz"
         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
         "0123456789"
         "_-.";
     std::string good_copy = good;
-    sanitize_partition_name(&good);
+    SanitizePartitionName(&good);
     EXPECT_EQ(good_copy, good);
 }
 
-TEST(devices, sanitize_somebad) {
+TEST(device_handler, sanitize_somebad) {
     std::string string = "abc!@#$%^&*()";
-    sanitize_partition_name(&string);
+    SanitizePartitionName(&string);
     EXPECT_EQ("abc__________", string);
 }
 
-TEST(devices, sanitize_allbad) {
+TEST(device_handler, sanitize_allbad) {
     std::string string = "!@#$%^&*()";
-    sanitize_partition_name(&string);
+    SanitizePartitionName(&string);
     EXPECT_EQ("__________", string);
 }
 
-TEST(devices, sanitize_onebad) {
+TEST(device_handler, sanitize_onebad) {
     std::string string = ")";
-    sanitize_partition_name(&string);
+    SanitizePartitionName(&string);
     EXPECT_EQ("_", string);
 }
 
-TEST(devices, DevPermissionsMatchNormal) {
+TEST(device_handler, DevPermissionsMatchNormal) {
     // Basic from ueventd.rc
     // /dev/null                 0666   root       root
     Permissions permissions("/dev/null", 0666, 0, 0);
@@ -329,7 +232,7 @@
     EXPECT_EQ(0U, permissions.gid());
 }
 
-TEST(devices, DevPermissionsMatchPrefix) {
+TEST(device_handler, DevPermissionsMatchPrefix) {
     // Prefix from ueventd.rc
     // /dev/dri/*                0666   root       graphics
     Permissions permissions("/dev/dri/*", 0666, 0, 1000);
@@ -342,7 +245,7 @@
     EXPECT_EQ(1000U, permissions.gid());
 }
 
-TEST(devices, DevPermissionsMatchWildcard) {
+TEST(device_handler, DevPermissionsMatchWildcard) {
     // Wildcard example
     // /dev/device*name                0666   root       graphics
     Permissions permissions("/dev/device*name", 0666, 0, 1000);
@@ -356,7 +259,7 @@
     EXPECT_EQ(1000U, permissions.gid());
 }
 
-TEST(devices, DevPermissionsMatchWildcardPrefix) {
+TEST(device_handler, DevPermissionsMatchWildcardPrefix) {
     // Wildcard+Prefix example
     // /dev/device*name*                0666   root       graphics
     Permissions permissions("/dev/device*name*", 0666, 0, 1000);
@@ -372,7 +275,7 @@
     EXPECT_EQ(1000U, permissions.gid());
 }
 
-TEST(devices, SysfsPermissionsMatchWithSubsystemNormal) {
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemNormal) {
     // /sys/devices/virtual/input/input*   enable      0660  root   input
     SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001);
     EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/input0", "input"));
@@ -382,7 +285,7 @@
     EXPECT_EQ(1001U, permissions.gid());
 }
 
-TEST(devices, SysfsPermissionsMatchWithSubsystemClass) {
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemClass) {
     // /sys/class/input/event*   enable      0660  root   input
     SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001);
     EXPECT_TRUE(permissions.MatchWithSubsystem(
@@ -396,7 +299,7 @@
     EXPECT_EQ(1001U, permissions.gid());
 }
 
-TEST(devices, SysfsPermissionsMatchWithSubsystemBus) {
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemBus) {
     // /sys/bus/i2c/devices/i2c-*   enable      0660  root   input
     SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001);
     EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "i2c"));
@@ -407,3 +310,6 @@
     EXPECT_EQ(0U, permissions.uid());
     EXPECT_EQ(1001U, permissions.gid());
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
new file mode 100644
index 0000000..b686885
--- /dev/null
+++ b/init/firmware_handler.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 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 "firmware_handler.h"
+
+#include <fcntl.h>
+#include <sys/sendfile.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+#include <thread>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+using android::base::Timer;
+using android::base::unique_fd;
+using android::base::WriteFully;
+
+namespace android {
+namespace init {
+
+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.
+    WriteFully(loading_fd, "1", 1);
+
+    // Copy the firmware.
+    int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
+    if (rc == -1) {
+        PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent.firmware
+                    << "' }";
+    }
+
+    // Tell the firmware whether to abort or commit.
+    const char* response = (rc != -1) ? "0" : "-1";
+    WriteFully(loading_fd, response, strlen(response));
+}
+
+static bool IsBooting() {
+    return access("/dev/.booting", F_OK) == 0;
+}
+
+static void ProcessFirmwareEvent(const Uevent& uevent) {
+    int booting = IsBooting();
+
+    LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
+
+    std::string root = "/sys" + uevent.path;
+    std::string loading = root + "/loading";
+    std::string data = root + "/data";
+
+    unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));
+    if (loading_fd == -1) {
+        PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent.firmware;
+        return;
+    }
+
+    unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));
+    if (data_fd == -1) {
+        PLOG(ERROR) << "couldn't open firmware data fd for " << uevent.firmware;
+        return;
+    }
+
+    static const char* firmware_dirs[] = {"/etc/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;
+        unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
+        struct stat sb;
+        if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
+            LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
+            return;
+        }
+    }
+
+    if (booting) {
+        // If we're not fully booted, we may be missing
+        // filesystems needed for firmware, wait and retry.
+        std::this_thread::sleep_for(100ms);
+        booting = IsBooting();
+        goto try_loading_again;
+    }
+
+    LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware;
+
+    // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
+    write(loading_fd, "-1", 2);
+}
+
+void HandleFirmwareEvent(const Uevent& uevent) {
+    if (uevent.subsystem != "firmware" || uevent.action != "add") return;
+
+    // Loading the firmware in a child means we can do that in parallel...
+    auto pid = fork();
+    if (pid == -1) {
+        PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware;
+    }
+    if (pid == 0) {
+        Timer t;
+        ProcessFirmwareEvent(uevent);
+        LOG(INFO) << "loading " << uevent.path << " took " << t;
+        _exit(EXIT_SUCCESS);
+    }
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/libunwindstack/Log.h b/init/firmware_handler.h
similarity index 66%
copy from libunwindstack/Log.h
copy to init/firmware_handler.h
index 2d01aa8..e456ac4 100644
--- a/libunwindstack/Log.h
+++ b/init/firmware_handler.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2007 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,12 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_LOG_H
-#define _LIBUNWINDSTACK_LOG_H
+#ifndef _INIT_FIRMWARE_HANDLER_H
+#define _INIT_FIRMWARE_HANDLER_H
 
-#include <stdint.h>
+#include "uevent.h"
 
-void log_to_stdout(bool enable);
-void log(uint8_t indent, const char* format, ...);
+namespace android {
+namespace init {
 
-#endif  // _LIBUNWINDSTACK_LOG_H
+void HandleFirmwareEvent(const Uevent& uevent);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index 99275e5..b9fa2ce 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -20,6 +20,9 @@
 
 #include "util.h"
 
+namespace android {
+namespace init {
+
 bool ImportParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                 int line, std::string* err) {
     if (args.size() != 2) {
@@ -50,3 +53,6 @@
         }
     }
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/import_parser.h b/init/import_parser.h
index 45cbfad..b774c57 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -22,6 +22,9 @@
 #include <string>
 #include <vector>
 
+namespace android {
+namespace init {
+
 class ImportParser : public SectionParser {
   public:
     ImportParser(Parser* parser) : parser_(parser) {}
@@ -37,4 +40,7 @@
     std::vector<std::pair<std::string, int>> imports_;
 };
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/init.cpp b/init/init.cpp
index 8b5d15e..bf251ff 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -42,14 +42,12 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
-#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <keyutils.h>
 #include <libavb/libavb.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
-#include <selinux/label.h>
 #include <selinux/selinux.h>
 
 #include <fstream>
@@ -58,7 +56,6 @@
 
 #include "action.h"
 #include "bootchart.h"
-#include "devices.h"
 #include "import_parser.h"
 #include "init_first_stage.h"
 #include "init_parser.h"
@@ -72,9 +69,14 @@
 #include "util.h"
 #include "watchdogd.h"
 
+using namespace std::string_literals;
+
 using android::base::boot_clock;
 using android::base::GetProperty;
-using android::base::StringPrintf;
+using android::base::Timer;
+
+namespace android {
+namespace init {
 
 struct selabel_handle *sehandle;
 struct selabel_handle *sehandle_prop;
@@ -93,6 +95,7 @@
 static std::unique_ptr<Timer> waiting_for_prop(nullptr);
 static std::string wait_prop_name;
 static std::string wait_prop_value;
+static bool shutting_down;
 
 void DumpState() {
     ServiceManager::GetInstance().DumpState();
@@ -157,21 +160,31 @@
     return true;
 }
 
+void ResetWaitForProp() {
+    wait_prop_name.clear();
+    wait_prop_value.clear();
+    waiting_for_prop.reset();
+}
+
 void property_changed(const std::string& name, const std::string& value) {
     // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
     // This is to ensure that init will always and immediately shutdown/reboot, regardless of
     // if there are other pending events to process or if init is waiting on an exec service or
     // waiting on a property.
-    if (name == "sys.powerctl") HandlePowerctlMessage(value);
+    // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
+    // commands to be executed.
+    if (name == "sys.powerctl") {
+        if (HandlePowerctlMessage(value)) {
+            shutting_down = true;
+        }
+    }
 
     if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
 
     if (waiting_for_prop) {
         if (wait_prop_name == name && wait_prop_value == value) {
-            wait_prop_name.clear();
-            wait_prop_value.clear();
             LOG(INFO) << "Wait for property took " << *waiting_for_prop;
-            waiting_for_prop.reset();
+            ResetWaitForProp();
         }
     }
 }
@@ -220,7 +233,7 @@
         panic();
     }
 
-    property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration_ms()).c_str());
+    property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
     return 0;
 }
 
@@ -450,14 +463,14 @@
 
     if (for_emulator) {
         // In the emulator, export any kernel option with the "ro.kernel." prefix.
-        property_set(StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str());
+        property_set("ro.kernel." + key, value);
         return;
     }
 
     if (key == "qemu") {
         strlcpy(qemu, value.c_str(), sizeof(qemu));
     } else if (android::base::StartsWith(key, "androidboot.")) {
-        property_set(StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(), value.c_str());
+        property_set("ro.boot." + key.substr(12), value);
     }
 }
 
@@ -488,7 +501,7 @@
     };
     for (size_t i = 0; i < arraysize(prop_map); i++) {
         std::string value = GetProperty(prop_map[i].src_prop, "");
-        property_set(prop_map[i].dst_prop, (!value.empty()) ? value.c_str() : prop_map[i].default_value);
+        property_set(prop_map[i].dst_prop, (!value.empty()) ? value : prop_map[i].default_value);
     }
 }
 
@@ -512,8 +525,7 @@
         android::base::ReadFileToString(file_name, &dt_file);
         std::replace(dt_file.begin(), dt_file.end(), ',', '.');
 
-        std::string property_name = StringPrintf("ro.boot.%s", dp->d_name);
-        property_set(property_name.c_str(), dt_file.c_str());
+        property_set("ro.boot."s + dp->d_name, dt_file);
     }
 }
 
@@ -859,7 +871,7 @@
         }
 
         // init's first stage can't set properties, so pass the time to the second stage.
-        setenv("INIT_SELINUX_TOOK", std::to_string(t.duration_ms()).c_str(), 1);
+        setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
     } else {
         selinux_init_all_handles();
     }
@@ -881,7 +893,6 @@
     selinux_android_restorecon("/dev/__properties__", 0);
     selinux_android_restorecon("/plat_property_contexts", 0);
     selinux_android_restorecon("/nonplat_property_contexts", 0);
-    selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
     selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
     selinux_android_restorecon("/dev/device-mapper", 0);
 
@@ -1004,7 +1015,7 @@
 
         static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
         uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
-        setenv("INIT_STARTED_AT", StringPrintf("%" PRIu64, start_ms).c_str(), 1);
+        setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
 
         char* path = argv[0];
         char* args[] = { path, nullptr };
@@ -1136,7 +1147,7 @@
             am.ExecuteOneCommand();
         }
         if (!(waiting_for_prop || sm.IsWaitingForExec())) {
-            restart_processes();
+            if (!shutting_down) restart_processes();
 
             // If there's a process that needs restarting, wake up in time for that.
             if (process_needs_restart_at != 0) {
@@ -1159,3 +1170,10 @@
 
     return 0;
 }
+
+}  // namespace init
+}  // namespace android
+
+int main(int argc, char** argv) {
+    android::init::main(argc, argv);
+}
diff --git a/init/init.h b/init/init.h
index 6725a70..aaab523 100644
--- a/init/init.h
+++ b/init/init.h
@@ -19,6 +19,11 @@
 
 #include <string>
 
+#include <selinux/label.h>
+
+namespace android {
+namespace init {
+
 // Note: These globals are *only* valid in init, so they should not be used in ueventd,
 // watchdogd, or any files that may be included in those, such as devices.cpp and util.cpp.
 // TODO: Have an Init class and remove all globals.
@@ -39,4 +44,9 @@
 
 void DumpState();
 
+void ResetWaitForProp();
+
+}  // namespace init
+}  // namespace android
+
 #endif  /* _INIT_INIT_H */
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 9d2a0d1..0f7e38f 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -19,11 +19,13 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <memory>
 #include <set>
 #include <string>
 #include <vector>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/strings.h>
@@ -31,8 +33,15 @@
 #include "devices.h"
 #include "fs_mgr.h"
 #include "fs_mgr_avb.h"
+#include "uevent.h"
+#include "uevent_listener.h"
 #include "util.h"
 
+using android::base::Timer;
+
+namespace android {
+namespace init {
+
 // Class Declarations
 // ------------------
 class FirstStageMount {
@@ -47,22 +56,23 @@
     bool InitDevices();
 
   protected:
-    void InitRequiredDevices();
-    void InitVerityDevice(const std::string& verity_device);
+    bool InitRequiredDevices();
+    bool InitVerityDevice(const std::string& verity_device);
     bool MountPartitions();
 
-    virtual coldboot_action_t ColdbootCallback(uevent* uevent);
+    virtual ListenerAction UeventCallback(const Uevent& uevent);
 
     // Pure virtual functions.
     virtual bool GetRequiredDevices() = 0;
     virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
 
     bool need_dm_verity_;
-    // Device tree fstab entries.
+
     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
-    // Eligible first stage mount candidates, only allow /system, /vendor and/or /odm.
     std::vector<fstab_rec*> mount_fstab_recs_;
     std::set<std::string> required_devices_partition_names_;
+    DeviceHandler device_handler_;
+    UeventListener uevent_listener_;
 };
 
 class FirstStageMountVBootV1 : public FirstStageMount {
@@ -83,7 +93,7 @@
     ~FirstStageMountVBootV2() override = default;
 
   protected:
-    coldboot_action_t ColdbootCallback(uevent* uevent) override;
+    ListenerAction UeventCallback(const Uevent& uevent) override;
     bool GetRequiredDevices() override;
     bool SetUpDmVerity(fstab_rec* fstab_rec) override;
     bool InitAvbHandle();
@@ -111,12 +121,10 @@
         LOG(ERROR) << "Failed to read fstab from device tree";
         return;
     }
-    for (auto mount_point : {"/system", "/vendor", "/odm"}) {
-        fstab_rec* fstab_rec =
-            fs_mgr_get_entry_for_mount_point(device_tree_fstab_.get(), mount_point);
-        if (fstab_rec != nullptr) {
-            mount_fstab_recs_.push_back(fstab_rec);
-        }
+    // Stores device_tree_fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>)
+    // for easier manipulation later, e.g., range-base for loop.
+    for (int i = 0; i < device_tree_fstab_->num_entries; i++) {
+        mount_fstab_recs_.push_back(&device_tree_fstab_->recs[i]);
     }
 }
 
@@ -140,86 +148,119 @@
 }
 
 bool FirstStageMount::InitDevices() {
-    if (!GetRequiredDevices()) return false;
-
-    InitRequiredDevices();
-
-    // InitRequiredDevices() will remove found partitions from required_devices_partition_names_.
-    // So if it isn't empty here, it means some partitions are not found.
-    if (!required_devices_partition_names_.empty()) {
-        LOG(ERROR) << __FUNCTION__ << "(): partition(s) not found: "
-                   << android::base::Join(required_devices_partition_names_, ", ");
-        return false;
-    } else {
-        return true;
-    }
+    return GetRequiredDevices() && InitRequiredDevices();
 }
 
 // Creates devices with uevent->partition_name matching one in the member variable
 // required_devices_partition_names_. Found partitions will then be removed from it
 // for the subsequent member function to check which devices are NOT created.
-void FirstStageMount::InitRequiredDevices() {
+bool FirstStageMount::InitRequiredDevices() {
     if (required_devices_partition_names_.empty()) {
-        return;
+        return true;
     }
 
     if (need_dm_verity_) {
         const std::string dm_path = "/devices/virtual/misc/device-mapper";
-        device_init(("/sys" + dm_path).c_str(), [&dm_path](uevent* uevent) -> coldboot_action_t {
-            if (uevent->path == dm_path) return COLDBOOT_STOP;
-            return COLDBOOT_CONTINUE;  // dm_path not found, continue to find it.
-        });
+        bool found = false;
+        auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
+            if (uevent.path == dm_path) {
+                device_handler_.HandleDeviceEvent(uevent);
+                found = true;
+                return ListenerAction::kStop;
+            }
+            return ListenerAction::kContinue;
+        };
+        uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
+        if (!found) {
+            LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
+            Timer t;
+            uevent_listener_.Poll(dm_callback, 10s);
+            LOG(INFO) << "Wait for device-mapper returned after " << t;
+        }
+        if (!found) {
+            LOG(ERROR) << "device-mapper device not found after polling timeout";
+            return false;
+        }
     }
 
-    device_init(nullptr,
-                [this](uevent* uevent) -> coldboot_action_t { return ColdbootCallback(uevent); });
+    auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
+    uevent_listener_.RegenerateUevents(uevent_callback);
 
-    device_close();
+    // UeventCallback() will remove found partitions from required_devices_partition_names_.
+    // So if it isn't empty here, it means some partitions are not found.
+    if (!required_devices_partition_names_.empty()) {
+        LOG(INFO) << __PRETTY_FUNCTION__
+                  << ": partition(s) not found in /sys, waiting for their uevent(s): "
+                  << android::base::Join(required_devices_partition_names_, ", ");
+        Timer t;
+        uevent_listener_.Poll(uevent_callback, 10s);
+        LOG(INFO) << "Wait for partitions returned after " << t;
+    }
+
+    if (!required_devices_partition_names_.empty()) {
+        LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
+                   << android::base::Join(required_devices_partition_names_, ", ");
+        return false;
+    }
+
+    return true;
 }
 
-coldboot_action_t FirstStageMount::ColdbootCallback(uevent* uevent) {
-    // We need platform devices to create symlinks.
-    if (uevent->subsystem == "platform") {
-        return COLDBOOT_CREATE;
-    }
-
+ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent) {
     // Ignores everything that is not a block device.
-    if (uevent->subsystem != "block") {
-        return COLDBOOT_CONTINUE;
+    if (uevent.subsystem != "block") {
+        return ListenerAction::kContinue;
     }
 
-    if (!uevent->partition_name.empty()) {
+    if (!uevent.partition_name.empty()) {
         // Matches partition name to create device nodes.
         // Both required_devices_partition_names_ and uevent->partition_name have A/B
         // suffix when A/B is used.
-        auto iter = required_devices_partition_names_.find(uevent->partition_name);
+        auto iter = required_devices_partition_names_.find(uevent.partition_name);
         if (iter != required_devices_partition_names_.end()) {
-            LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter;
+            LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
             required_devices_partition_names_.erase(iter);
+            device_handler_.HandleDeviceEvent(uevent);
             if (required_devices_partition_names_.empty()) {
-                return COLDBOOT_STOP;  // Found all partitions, stop coldboot.
+                return ListenerAction::kStop;
             } else {
-                return COLDBOOT_CREATE;  // Creates this device and continue to find others.
+                return ListenerAction::kContinue;
             }
         }
     }
     // Not found a partition or find an unneeded partition, continue to find others.
-    return COLDBOOT_CONTINUE;
+    return ListenerAction::kContinue;
 }
 
 // Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
-void FirstStageMount::InitVerityDevice(const std::string& verity_device) {
+bool FirstStageMount::InitVerityDevice(const std::string& verity_device) {
     const std::string device_name(basename(verity_device.c_str()));
     const std::string syspath = "/sys/block/" + device_name;
+    bool found = false;
 
-    device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
-        if (uevent->device_name == device_name) {
+    auto verity_callback = [&device_name, &verity_device, this, &found](const Uevent& uevent) {
+        if (uevent.device_name == device_name) {
             LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
-            return COLDBOOT_STOP;
+            device_handler_.HandleDeviceEvent(uevent);
+            found = true;
+            return ListenerAction::kStop;
         }
-        return COLDBOOT_CONTINUE;
-    });
-    device_close();
+        return ListenerAction::kContinue;
+    };
+
+    uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback);
+    if (!found) {
+        LOG(INFO) << "dm-verity device not found in /sys, waiting for its uevent";
+        Timer t;
+        uevent_listener_.Poll(verity_callback, 10s);
+        LOG(INFO) << "wait for dm-verity device returned after " << t;
+    }
+    if (!found) {
+        LOG(ERROR) << "dm-verity device not found after polling timeout";
+        return false;
+    }
+
+    return true;
 }
 
 bool FirstStageMount::MountPartitions() {
@@ -280,14 +321,18 @@
 bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
     if (fs_mgr_is_verified(fstab_rec)) {
         int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */);
-        if (ret == FS_MGR_SETUP_VERITY_DISABLED) {
-            LOG(INFO) << "Verity disabled for '" << fstab_rec->mount_point << "'";
-        } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
-            // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
-            // Needs to create it because ueventd isn't started in init first stage.
-            InitVerityDevice(fstab_rec->blk_device);
-        } else {
-            return false;
+        switch (ret) {
+            case FS_MGR_SETUP_VERITY_SKIPPED:
+            case FS_MGR_SETUP_VERITY_DISABLED:
+                LOG(INFO) << "Verity disabled/skipped for '" << fstab_rec->mount_point << "'";
+                return true;
+            case FS_MGR_SETUP_VERITY_SUCCESS:
+                // The exact block device name (fstab_rec->blk_device) is changed to
+                // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
+                // first stage.
+                return InitVerityDevice(fstab_rec->blk_device);
+            default:
+                return false;
         }
     }
     return true;  // Returns true to mount the partition.
@@ -345,46 +390,49 @@
     return true;
 }
 
-coldboot_action_t FirstStageMountVBootV2::ColdbootCallback(uevent* uevent) {
-    // Invokes the parent function to see if any desired partition has been found.
-    // If yes, record the by-name symlink for creating FsManagerAvbHandle later.
-    coldboot_action_t parent_callback_ret = FirstStageMount::ColdbootCallback(uevent);
-
-    // Skips the uevent if the parent function returns COLDBOOT_CONTINUE (meaning
-    // that the uevent was skipped) or there is no uevent->partition_name to
-    // create the by-name symlink.
-    if (parent_callback_ret != COLDBOOT_CONTINUE && !uevent->partition_name.empty()) {
-        // get_block_device_symlinks() will return three symlinks at most, depending on
+ListenerAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) {
+    // Check if this uevent corresponds to one of the required partitions and store its symlinks if
+    // so, in order to create FsManagerAvbHandle later.
+    // Note that the parent callback removes partitions from the list of required partitions
+    // 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()) {
+        // 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.,
         //   - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem
         //   - /dev/block/platform/soc.0/f9824900.sdhci/by-num/p1
         //   - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
-        std::vector<std::string> links = get_block_device_symlinks(uevent);
+        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
+                LOG(ERROR) << "Partition '" << uevent.partition_name
                            << "' already existed in the by-name symlink map with a value of '"
                            << it->second << "', new value '" << links[0] << "' will be ignored.";
             }
         }
     }
 
-    return parent_callback_ret;
+    return FirstStageMount::UeventCallback(uevent);
 }
 
 bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
     if (fs_mgr_is_avb(fstab_rec)) {
         if (!InitAvbHandle()) return false;
-        if (avb_handle_->hashtree_disabled()) {
-            LOG(INFO) << "avb hashtree disabled for '" << fstab_rec->mount_point << "'";
-        } else if (avb_handle_->SetUpAvb(fstab_rec, false /* wait_for_verity_dev */)) {
-            // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
-            // Needs to create it because ueventd isn't started in init first stage.
-            InitVerityDevice(fstab_rec->blk_device);
-        } else {
-            return false;
+        SetUpAvbHashtreeResult hashtree_result =
+            avb_handle_->SetUpAvbHashtree(fstab_rec, false /* wait_for_verity_dev */);
+        switch (hashtree_result) {
+            case SetUpAvbHashtreeResult::kDisabled:
+                return true;  // Returns true to mount the partition.
+            case SetUpAvbHashtreeResult::kSuccess:
+                // The exact block device name (fstab_rec->blk_device) is changed to
+                // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
+                // first stage.
+                return InitVerityDevice(fstab_rec->blk_device);
+            default:
+                return false;
         }
     }
     return true;  // Returns true to mount the partition.
@@ -412,7 +460,7 @@
 
 // Public functions
 // ----------------
-// Mounts /system, /vendor, and/or /odm if they are present in the fstab provided by device tree.
+// Mounts partitions specified by fstab in device tree.
 bool DoFirstStageMount() {
     // Skips first stage mount if we're in recovery mode.
     if (IsRecoveryMode()) {
@@ -464,3 +512,6 @@
     }
     setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/init_first_stage.h b/init/init_first_stage.h
index 170a24c..c7a3867 100644
--- a/init/init_first_stage.h
+++ b/init/init_first_stage.h
@@ -17,7 +17,13 @@
 #ifndef _INIT_FIRST_STAGE_H
 #define _INIT_FIRST_STAGE_H
 
+namespace android {
+namespace init {
+
 bool DoFirstStageMount();
 void SetInitAvbVersionInRecovery();
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 1b31cf2..9f7089b 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -18,6 +18,7 @@
 
 #include <dirent.h>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -25,6 +26,9 @@
 #include "parser.h"
 #include "util.h"
 
+namespace android {
+namespace init {
+
 Parser::Parser() {
 }
 
@@ -108,7 +112,7 @@
 
 bool Parser::ParseConfigFile(const std::string& path) {
     LOG(INFO) << "Parsing file " << path << "...";
-    Timer t;
+    android::base::Timer t;
     std::string data;
     std::string err;
     if (!ReadFile(path, &data, &err)) {
@@ -159,3 +163,6 @@
     }
     return ParseConfigFile(path);
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/init_parser.h b/init/init_parser.h
index bd8a178..c07a699 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -45,6 +45,9 @@
 //    This function is called at the end of the file.
 //    It indicates that the parsing has completed and any relevant objects should be committed.
 
+namespace android {
+namespace init {
+
 class SectionParser {
   public:
     virtual ~SectionParser() {}
@@ -66,9 +69,9 @@
     //  be written.
     using LineCallback = std::function<bool(std::vector<std::string>&&, std::string*)>;
 
+    // TODO: init is the only user of this as a singleton; remove it.
     static Parser& GetInstance();
 
-    // Exposed for testing
     Parser();
 
     bool ParseConfig(const std::string& path);
@@ -93,4 +96,7 @@
     bool is_odm_etc_init_loaded_ = false;
 };
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
index 86d60d0..95f269a 100644
--- a/init/init_parser_test.cpp
+++ b/init/init_parser_test.cpp
@@ -16,13 +16,17 @@
 
 #include "init_parser.h"
 
-#include "init.h"
-#include "service.h"
+#include <string>
+#include <vector>
 
 #include <gtest/gtest.h>
 
-#include <string>
-#include <vector>
+#include "init.h"
+#include "service.h"
+#include "util.h"
+
+namespace android {
+namespace init {
 
 TEST(init_parser, make_exec_oneshot_service_invalid_syntax) {
     ServiceManager& sm = ServiceManager::GetInstance();
@@ -141,3 +145,6 @@
 TEST(init_parser, make_exec_oneshot_service_with_just_command_no_dash) {
     Test_make_exec_oneshot_service(false, false, false, false, false);
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 7093ba9..0a4071b 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -27,6 +27,9 @@
 #include "keyword_map.h"
 #include "util.h"
 
+namespace android {
+namespace init {
+
 class TestFunctionMap : public KeywordMap<BuiltinFunction> {
   public:
     // Helper for argument-less functions
@@ -185,3 +188,6 @@
 
     EXPECT_EQ(6, num_executed);
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/keychords.cpp b/init/keychords.cpp
index c572cee..a0d7cc5 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "keychords.h"
+
 #include <fcntl.h>
 #include <stdlib.h>
 #include <sys/stat.h>
@@ -25,7 +27,9 @@
 #include <android-base/properties.h>
 
 #include "init.h"
-#include "service.h"
+
+namespace android {
+namespace init {
 
 static struct input_keychord *keychords = 0;
 static int keychords_count = 0;
@@ -112,3 +116,6 @@
 
     register_epoll_handler(keychord_fd, handle_keychord);
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/keychords.h b/init/keychords.h
index d2723b7..1c34098 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -17,9 +17,15 @@
 #ifndef _INIT_KEYCHORDS_H_
 #define _INIT_KEYCHORDS_H_
 
-struct service;
+#include "service.h"
 
-void add_service_keycodes(service*);
+namespace android {
+namespace init {
+
+void add_service_keycodes(Service* svc);
 void keychord_init();
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 88bad01..481d637 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -22,6 +22,9 @@
 
 #include <android-base/stringprintf.h>
 
+namespace android {
+namespace init {
+
 template <typename Function>
 class KeywordMap {
   public:
@@ -79,4 +82,7 @@
     virtual const Map& map() const = 0;
 };
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/log.cpp b/init/log.cpp
index 0615730..1830077 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -23,6 +23,9 @@
 #include <android-base/logging.h>
 #include <selinux/selinux.h>
 
+namespace android {
+namespace init {
+
 void InitKernelLogging(char* argv[]) {
     // Make stdin/stdout/stderr all point to /dev/null.
     int fd = open("/sys/fs/selinux/null", O_RDWR);
@@ -55,3 +58,6 @@
     android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
     return 0;
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/log.h b/init/log.h
index 29a27af..5a4eba6 100644
--- a/init/log.h
+++ b/init/log.h
@@ -19,8 +19,14 @@
 
 #include <sys/cdefs.h>
 
+namespace android {
+namespace init {
+
 void InitKernelLogging(char* argv[]);
 
 int selinux_klog_callback(int level, const char* fmt, ...) __printflike(2, 3);
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/parser.cpp b/init/parser.cpp
index 0d13cfe..c0fa6d9 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -1,5 +1,8 @@
 #include "parser.h"
 
+namespace android {
+namespace init {
+
 int next_token(struct parse_state *state)
 {
     char *x = state->ptr;
@@ -116,3 +119,6 @@
     }
     return T_EOF;
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/parser.h b/init/parser.h
index 3dcc566..86e4c57 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -21,6 +21,9 @@
 #define T_TEXT 1
 #define T_NEWLINE 2
 
+namespace android {
+namespace init {
+
 struct parse_state
 {
     char *ptr;
@@ -31,4 +34,7 @@
 
 int next_token(struct parse_state *state);
 
+}  // namespace init
+}  // namespace android
+
 #endif /* PARSER_H_ */
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 18e47e3..925cc9b 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,12 +39,13 @@
 #include <sys/_system_properties.h>
 
 #include <memory>
+#include <queue>
 #include <vector>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
-#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <bootimg.h>
 #include <fs_mgr.h>
@@ -55,11 +56,14 @@
 #include "init.h"
 #include "util.h"
 
-using android::base::StringPrintf;
+using android::base::Timer;
 
 #define PERSISTENT_PROPERTY_DIR  "/data/property"
 #define RECOVERY_MOUNT_POINT "/recovery"
 
+namespace android {
+namespace init {
+
 static int persistent_properties_loaded = 0;
 
 static int property_set_fd = -1;
@@ -144,7 +148,7 @@
     if (name[0] == '.') return false;
     if (name[namelen - 1] == '.') return false;
 
-    /* Only allow alphanumeric, plus '.', '-', '@', or '_' */
+    /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
     /* Don't allow ".." to appear in a property name */
     for (size_t i = 0; i < namelen; i++) {
         if (name[i] == '.') {
@@ -152,7 +156,7 @@
             if (name[i-1] == '.') return false;
             continue;
         }
-        if (name[i] == '_' || name[i] == '-' || name[i] == '@') continue;
+        if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
         if (name[i] >= 'a' && name[i] <= 'z') continue;
         if (name[i] >= 'A' && name[i] <= 'Z') continue;
         if (name[i] >= '0' && name[i] <= '9') continue;
@@ -162,7 +166,7 @@
     return true;
 }
 
-uint32_t property_set(const std::string& name, const std::string& value) {
+static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
     size_t valuelen = value.size();
 
     if (!is_legal_property_name(name)) {
@@ -176,12 +180,6 @@
         return PROP_ERROR_INVALID_VALUE;
     }
 
-    if (name == "selinux.restorecon_recursive" && valuelen > 0) {
-        if (selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
-            LOG(ERROR) << "Failed to restorecon_recursive " << value;
-        }
-    }
-
     prop_info* pi = (prop_info*) __system_property_find(name.c_str());
     if (pi != nullptr) {
         // ro.* properties are actually "write-once".
@@ -210,6 +208,85 @@
     return PROP_SUCCESS;
 }
 
+typedef int (*PropertyAsyncFunc)(const std::string&, const std::string&);
+
+struct PropertyChildInfo {
+    pid_t pid;
+    PropertyAsyncFunc func;
+    std::string name;
+    std::string value;
+};
+
+static std::queue<PropertyChildInfo> property_children;
+
+static void PropertyChildLaunch() {
+    auto& info = property_children.front();
+    pid_t pid = fork();
+    if (pid < 0) {
+        LOG(ERROR) << "Failed to fork for property_set_async";
+        while (!property_children.empty()) {
+            property_children.pop();
+        }
+        return;
+    }
+    if (pid != 0) {
+        info.pid = pid;
+    } else {
+        if (info.func(info.name, info.value) != 0) {
+            LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value
+                       << "\") failed";
+        }
+        exit(0);
+    }
+}
+
+bool PropertyChildReap(pid_t pid) {
+    if (property_children.empty()) {
+        return false;
+    }
+    auto& info = property_children.front();
+    if (info.pid != pid) {
+        return false;
+    }
+    if (PropertySetImpl(info.name, info.value) != PROP_SUCCESS) {
+        LOG(ERROR) << "Failed to set async property " << info.name;
+    }
+    property_children.pop();
+    if (!property_children.empty()) {
+        PropertyChildLaunch();
+    }
+    return true;
+}
+
+static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
+                                 PropertyAsyncFunc func) {
+    if (value.empty()) {
+        return PropertySetImpl(name, value);
+    }
+
+    PropertyChildInfo info;
+    info.func = func;
+    info.name = name;
+    info.value = value;
+    property_children.push(info);
+    if (property_children.size() == 1) {
+        PropertyChildLaunch();
+    }
+    return PROP_SUCCESS;
+}
+
+static int RestoreconRecursiveAsync(const std::string& name, const std::string& value) {
+    return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+}
+
+uint32_t property_set(const std::string& name, const std::string& value) {
+    if (name == "selinux.restorecon_recursive") {
+        return PropertySetAsync(name, value, RestoreconRecursiveAsync);
+    }
+
+    return PropertySetImpl(name, value);
+}
+
 class SocketConnection {
  public:
   SocketConnection(int socket, const struct ucred& cred)
@@ -276,7 +353,7 @@
     while (*timeout_ms > 0) {
       Timer timer;
       int nr = poll(ufds, 1, *timeout_ms);
-      uint64_t millis = timer.duration_ms();
+      uint64_t millis = timer.duration().count();
       *timeout_ms = (millis > *timeout_ms) ? 0 : *timeout_ms - millis;
 
       if (nr > 0) {
@@ -640,7 +717,7 @@
     boot_img_hdr hdr;
     if (android::base::ReadFully(fd, &hdr, sizeof(hdr))) {
         std::string hex = bytes_to_hex(reinterpret_cast<uint8_t*>(hdr.id), sizeof(hdr.id));
-        property_set("ro.recovery_id", hex.c_str());
+        property_set("ro.recovery_id", hex);
     } else {
         PLOG(ERROR) << "error reading /recovery";
     }
@@ -670,3 +747,6 @@
 
     register_epoll_handler(property_set_fd, handle_property_set_fd);
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/property_service.h b/init/property_service.h
index 9a5b6f6..a55e79c 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -21,11 +21,16 @@
 
 #include <string>
 
+namespace android {
+namespace init {
+
 struct property_audit_data {
     ucred *cr;
     const char* name;
 };
 
+extern bool PropertyChildReap(pid_t pid);
+
 void property_init(void);
 void property_load_boot_defaults(void);
 void load_persist_props(void);
@@ -34,5 +39,7 @@
 uint32_t property_set(const std::string& name, const std::string& value);
 bool is_legal_property_name(const std::string& name);
 
+}  // namespace init
+}  // namespace android
 
 #endif  /* _INIT_PROPERTY_H */
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index 4d784aa..3a64e02 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -23,6 +23,9 @@
 
 #include <gtest/gtest.h>
 
+namespace android {
+namespace init {
+
 TEST(property_service, very_long_name_35166374) {
   // Connect to the property service directly...
   int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
@@ -46,3 +49,6 @@
   ASSERT_EQ(static_cast<ssize_t>(sizeof(data)), send(fd, &data, sizeof(data), 0));
   ASSERT_EQ(0, close(fd));
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp
index cdfc698..969caec 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -20,7 +20,7 @@
 #include <fcntl.h>
 #include <linux/fs.h>
 #include <mntent.h>
-#include <selinux/selinux.h>
+#include <sys/capability.h>
 #include <sys/cdefs.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
@@ -35,6 +35,7 @@
 #include <thread>
 #include <vector>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
@@ -48,10 +49,16 @@
 #include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
 
+#include "capabilities.h"
+#include "init.h"
 #include "property_service.h"
 #include "service.h"
 
 using android::base::StringPrintf;
+using android::base::Timer;
+
+namespace android {
+namespace init {
 
 // represents umount status during reboot / shutdown.
 enum UmountStat {
@@ -156,12 +163,42 @@
 }
 
 static void LogShutdownTime(UmountStat stat, Timer* t) {
-    LOG(WARNING) << "powerctl_shutdown_time_ms:" << std::to_string(t->duration_ms()) << ":" << stat;
+    LOG(WARNING) << "powerctl_shutdown_time_ms:" << std::to_string(t->duration().count()) << ":"
+                 << stat;
+}
+
+// Determines whether the system is capable of rebooting. This is conservative,
+// so if any of the attempts to determine this fail, it will still return true.
+static bool IsRebootCapable() {
+    if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
+        PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
+        return true;
+    }
+
+    ScopedCaps caps(cap_get_proc());
+    if (!caps) {
+        PLOG(WARNING) << "cap_get_proc() failed";
+        return true;
+    }
+
+    cap_flag_value_t value = CAP_SET;
+    if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) {
+        PLOG(WARNING) << "cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed";
+        return true;
+    }
+    return value == CAP_SET;
 }
 
 static void __attribute__((noreturn))
 RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
     LOG(INFO) << "Reboot ending, jumping to kernel";
+
+    if (!IsRebootCapable()) {
+        // On systems where init does not have the capability of rebooting the
+        // device, just exit cleanly.
+        exit(0);
+    }
+
     switch (cmd) {
         case ANDROID_RB_POWEROFF:
             reboot(RB_POWER_OFF);
@@ -220,7 +257,7 @@
     }
 }
 
-static UmountStat UmountPartitions(int timeoutMs) {
+static UmountStat UmountPartitions(std::chrono::milliseconds timeout) {
     Timer t;
     UmountStat stat = UMOUNT_STAT_TIMEOUT;
     int retry = 0;
@@ -238,7 +275,7 @@
             stat = UMOUNT_STAT_SUCCESS;
             break;
         }
-        if ((timeoutMs < t.duration_ms()) && retry > 0) {  // try umount at least once
+        if ((timeout < t.duration()) && retry > 0) {  // try umount at least once
             stat = UMOUNT_STAT_TIMEOUT;
             break;
         }
@@ -267,7 +304,7 @@
  *
  * return true when umount was successful. false when timed out.
  */
-static UmountStat TryUmountAndFsck(bool runFsck, int timeoutMs) {
+static UmountStat TryUmountAndFsck(bool runFsck, std::chrono::milliseconds timeout) {
     Timer t;
     std::vector<MountEntry> block_devices;
     std::vector<MountEntry> emulated_devices;
@@ -278,13 +315,13 @@
         return UMOUNT_STAT_ERROR;
     }
 
-    UmountStat stat = UmountPartitions(timeoutMs - t.duration_ms());
+    UmountStat stat = UmountPartitions(timeout - t.duration());
     if (stat != UMOUNT_STAT_SUCCESS) {
         LOG(INFO) << "umount timeout, last resort, kill all and try";
         if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
         KillAllProcesses();
         // even if it succeeds, still it is timeout and do not run fsck with all processes killed
-        UmountPartitions(0);
+        UmountPartitions(0ms);
         if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
     }
 
@@ -318,26 +355,27 @@
         abort();
     }
 
-    constexpr unsigned int shutdownTimeoutDefault = 6;
-    unsigned int shutdownTimeout = shutdownTimeoutDefault;
-    if (SHUTDOWN_ZERO_TIMEOUT) {  // eng build
-        shutdownTimeout = 0;
-    } else {
-        shutdownTimeout =
-            android::base::GetUintProperty("ro.build.shutdown_timeout", shutdownTimeoutDefault);
+    auto shutdown_timeout = 0s;
+    if (!SHUTDOWN_ZERO_TIMEOUT) {
+        constexpr unsigned int shutdown_timeout_default = 6;
+        auto shutdown_timeout_property =
+            android::base::GetUintProperty("ro.build.shutdown_timeout", shutdown_timeout_default);
+        shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
     }
-    LOG(INFO) << "Shutdown timeout: " << shutdownTimeout;
+    LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " seconds";
 
     // keep debugging tools until non critical ones are all gone.
     const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
     // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
-    const std::set<std::string> to_starts{"watchdogd", "vold", "ueventd"};
+    const std::set<std::string> to_starts{"watchdogd"};
     ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
         if (kill_after_apps.count(s->name())) {
             s->SetShutdownCritical();
         } else if (to_starts.count(s->name())) {
             s->Start();
             s->SetShutdownCritical();
+        } else if (s->IsShutdownCritical()) {
+            s->Start();  // start shutdown critical service if not started
         }
     });
 
@@ -351,7 +389,7 @@
 
     // optional shutdown step
     // 1. terminate all services except shutdown critical ones. wait for delay to finish
-    if (shutdownTimeout > 0) {
+    if (shutdown_timeout > 0s) {
         LOG(INFO) << "terminating init services";
 
         // Ask all services to terminate except shutdown critical ones.
@@ -360,9 +398,9 @@
         });
 
         int service_count = 0;
-        // Up to half as long as shutdownTimeout or 3 seconds, whichever is lower.
-        unsigned int terminationWaitTimeout = std::min<unsigned int>((shutdownTimeout + 1) / 2, 3);
-        while (t.duration_s() < terminationWaitTimeout) {
+        // Up to half as long as shutdown_timeout or 3 seconds, whichever is lower.
+        auto termination_wait_timeout = std::min((shutdown_timeout + 1s) / 2, 3s);
+        while (t.duration() < termination_wait_timeout) {
             ServiceManager::GetInstance().ReapAnyOutstandingChildren();
 
             service_count = 0;
@@ -410,7 +448,7 @@
     });
     // 4. sync, try umount, and optionally run fsck for user shutdown
     sync();
-    UmountStat stat = TryUmountAndFsck(runFsck, shutdownTimeout * 1000 - t.duration_ms());
+    UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
     // Follow what linux shutdown is doing: one more sync with little bit delay
     sync();
     std::this_thread::sleep_for(100ms);
@@ -457,6 +495,9 @@
         }
     } else if (command == "thermal-shutdown") {  // no additional parameter allowed
         cmd = ANDROID_RB_THERMOFF;
+        // Do not queue "shutdown" trigger since we want to shutdown immediately
+        DoReboot(cmd, command, reboot_target, run_fsck);
+        return true;
     } else {
         command_invalid = true;
     }
@@ -465,6 +506,28 @@
         return false;
     }
 
-    DoReboot(cmd, command, reboot_target, run_fsck);
+    LOG(INFO) << "Clear action queue and start shutdown trigger";
+    ActionManager::GetInstance().ClearQueue();
+    // Queue shutdown trigger first
+    ActionManager::GetInstance().QueueEventTrigger("shutdown");
+    // Queue built-in shutdown_done
+    auto shutdown_handler = [cmd, command, reboot_target,
+                             run_fsck](const std::vector<std::string>&) {
+        DoReboot(cmd, command, reboot_target, run_fsck);
+        return 0;
+    };
+    ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
+
+    // Skip wait for prop if it is in progress
+    ResetWaitForProp();
+
+    // Skip wait for exec if it is in progress
+    if (ServiceManager::GetInstance().IsWaitingForExec()) {
+        ServiceManager::GetInstance().ClearExecWait();
+    }
+
     return true;
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/reboot.h b/init/reboot.h
index b304b3c..e559540 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -19,6 +19,9 @@
 
 #include <string>
 
+namespace android {
+namespace init {
+
 /* Reboot / shutdown the system.
  * cmd ANDROID_RB_* as defined in android_reboot.h
  * reason Reason string like "reboot", "userrequested"
@@ -32,4 +35,7 @@
 // Parses and handles a setprop sys.powerctl message.
 bool HandlePowerctlMessage(const std::string& command);
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/service.cpp b/init/service.cpp
index 7c931da..7a657c8 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -34,6 +34,7 @@
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
+#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <processgroup/processgroup.h>
@@ -45,10 +46,17 @@
 #include "util.h"
 
 using android::base::boot_clock;
+using android::base::GetProperty;
+using android::base::Join;
+using android::base::make_scope_guard;
 using android::base::ParseInt;
+using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
 
+namespace android {
+namespace init {
+
 static std::string ComputeContextFromExecutable(std::string& service_name,
                                                 const std::string& service_path) {
     std::string computed_context;
@@ -198,28 +206,16 @@
         return;
     }
 
-    std::string prop_name = StringPrintf("init.svc.%s", name_.c_str());
-    property_set(prop_name.c_str(), new_state.c_str());
+    std::string prop_name = "init.svc." + name_;
+    property_set(prop_name, new_state);
 
     if (new_state == "running") {
         uint64_t start_ns = time_started_.time_since_epoch().count();
-        property_set(StringPrintf("ro.boottime.%s", name_.c_str()).c_str(),
-                     StringPrintf("%" PRIu64, start_ns).c_str());
+        property_set("ro.boottime." + name_, std::to_string(start_ns));
     }
 }
 
 void Service::KillProcessGroup(int signal) {
-    // We ignore reporting errors of ESRCH as this commonly happens in the below case,
-    // 1) Terminate() is called, which sends SIGTERM to the process
-    // 2) The process successfully exits
-    // 3) ReapOneProcess() is called, which calls waitpid(-1, ...) which removes the pid entry.
-    // 4) Reap() is called, which sends SIGKILL, but the pid no longer exists.
-    // TODO: sigaction for SIGCHLD reports the pid of the exiting process,
-    // we should do this kill with that pid first before calling waitpid().
-    if (kill(-pid_, signal) == -1 && errno != ESRCH) {
-        PLOG(ERROR) << "kill(" << pid_ << ", " << signal << ") failed";
-    }
-
     // If we've already seen a successful result from killProcessGroup*(), then we have removed
     // the cgroup already and calling these functions a second time will simply result in an error.
     // This is true regardless of which signal was sent.
@@ -241,8 +237,15 @@
 void Service::SetProcessAttributes() {
     // Keep capabilites on uid change.
     if (capabilities_.any() && uid_) {
-        if (prctl(PR_SET_SECUREBITS, SECBIT_KEEP_CAPS | SECBIT_KEEP_CAPS_LOCKED) != 0) {
-            PLOG(FATAL) << "prtcl(PR_SET_KEEPCAPS) failed for " << name_;
+        // If Android is running in a container, some securebits might already
+        // be locked, so don't change those.
+        int64_t securebits = prctl(PR_GET_SECUREBITS);
+        if (securebits == -1) {
+            PLOG(FATAL) << "prctl(PR_GET_SECUREBITS) failed for " << name_;
+        }
+        securebits |= SECBIT_KEEP_CAPS | SECBIT_KEEP_CAPS_LOCKED;
+        if (prctl(PR_SET_SECUREBITS, securebits) != 0) {
+            PLOG(FATAL) << "prctl(PR_SET_SECUREBITS) failed for " << name_;
         }
     }
 
@@ -333,8 +336,8 @@
 
 void Service::DumpState() const {
     LOG(INFO) << "service " << name_;
-    LOG(INFO) << "  class '" << android::base::Join(classnames_, " ") << "'";
-    LOG(INFO) << "  exec "<< android::base::Join(args_, " ");
+    LOG(INFO) << "  class '" << Join(classnames_, " ") << "'";
+    LOG(INFO) << "  exec " << Join(args_, " ");
     std::for_each(descriptors_.begin(), descriptors_.end(),
                   [] (const auto& info) { LOG(INFO) << *info; });
 }
@@ -498,6 +501,14 @@
     return true;
 }
 
+bool Service::ParseShutdown(const std::vector<std::string>& args, std::string* err) {
+    if (args[1] == "critical") {
+        flags_ |= SVC_SHUTDOWN_CRITICAL;
+        return true;
+    }
+    return false;
+}
+
 template <typename T>
 bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
     int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
@@ -537,9 +548,8 @@
 
 // name type perm [ uid gid context ]
 bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) {
-    if (!android::base::StartsWith(args[2], "dgram") &&
-        !android::base::StartsWith(args[2], "stream") &&
-        !android::base::StartsWith(args[2], "seqpacket")) {
+    if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
+        !StartsWith(args[2], "seqpacket")) {
         *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
         return false;
     }
@@ -602,6 +612,7 @@
         {"namespace",   {1,     2,    &Service::ParseNamespace}},
         {"seclabel",    {1,     1,    &Service::ParseSeclabel}},
         {"setenv",      {2,     2,    &Service::ParseSetenv}},
+        {"shutdown",    {1,     1,    &Service::ParseShutdown}},
         {"socket",      {3,     6,    &Service::ParseSocket}},
         {"file",        {2,     2,    &Service::ParseFile}},
         {"user",        {1,     1,    &Service::ParseUser}},
@@ -622,10 +633,10 @@
     return (this->*parser)(args, err);
 }
 
-bool Service::ExecStart(std::unique_ptr<Timer>* exec_waiter) {
+bool Service::ExecStart(std::unique_ptr<android::base::Timer>* exec_waiter) {
     flags_ |= SVC_EXEC | SVC_ONESHOT;
 
-    exec_waiter->reset(new Timer);
+    exec_waiter->reset(new android::base::Timer);
 
     if (!Start()) {
         exec_waiter->reset();
@@ -707,13 +718,13 @@
 
         // See if there were "writepid" instructions to write to files under /dev/cpuset/.
         auto cpuset_predicate = [](const std::string& path) {
-            return android::base::StartsWith(path, "/dev/cpuset/");
+            return StartsWith(path, "/dev/cpuset/");
         };
         auto iter = std::find_if(writepid_files_.begin(), writepid_files_.end(), cpuset_predicate);
         if (iter == writepid_files_.end()) {
             // There were no "writepid" instructions for cpusets, check if the system default
             // cpuset is specified to be used for the process.
-            std::string default_cpuset = android::base::GetProperty("ro.cpuset.default", "");
+            std::string default_cpuset = GetProperty("ro.cpuset.default", "");
             if (!default_cpuset.empty()) {
                 // Make sure the cpuset name starts and ends with '/'.
                 // A single '/' means the 'root' cpuset.
@@ -727,7 +738,7 @@
                     StringPrintf("/dev/cpuset%stasks", default_cpuset.c_str()));
             }
         }
-        std::string pid_str = StringPrintf("%d", getpid());
+        std::string pid_str = std::to_string(getpid());
         for (const auto& file : writepid_files_) {
             if (!WriteStringToFile(pid_str, file)) {
                 PLOG(ERROR) << "couldn't write " << pid_str << " to " << file;
@@ -768,7 +779,7 @@
     }
 
     if (oom_score_adjust_ != -1000) {
-        std::string oom_str = StringPrintf("%d", oom_score_adjust_);
+        std::string oom_str = std::to_string(oom_score_adjust_);
         std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
         if (!WriteStringToFile(oom_str, oom_file)) {
             PLOG(ERROR) << "couldn't write oom_score_adj: " << strerror(errno);
@@ -787,9 +798,9 @@
     }
 
     if ((flags_ & SVC_EXEC) != 0) {
-        LOG(INFO) << android::base::StringPrintf(
-            "SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...", pid_, uid_, gid_,
-            supp_gids_.size(), !seclabel_.empty() ? seclabel_.c_str() : "default");
+        LOG(INFO) << "SVC_EXEC pid " << pid_ << " (uid " << uid_ << " gid " << gid_ << "+"
+                  << supp_gids_.size() << " context "
+                  << (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
     }
 
     NotifyStateChange("running");
@@ -966,8 +977,7 @@
     std::vector<std::string> str_args(args.begin() + command_arg, args.end());
 
     exec_count_++;
-    std::string name =
-        "exec " + std::to_string(exec_count_) + " (" + android::base::Join(str_args, " ") + ")";
+    std::string name = "exec " + std::to_string(exec_count_) + " (" + Join(str_args, " ") + ")";
 
     unsigned flags = SVC_EXEC | SVC_ONESHOT | SVC_TEMPORARY;
     CapSet no_capabilities;
@@ -1089,13 +1099,25 @@
 }
 
 bool ServiceManager::ReapOneProcess() {
-    int status;
-    pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
-    if (pid == 0) {
+    siginfo_t siginfo = {};
+    // This returns a zombie pid or informs us that there are no zombies left to be reaped.
+    // It does NOT reap the pid; that is done below.
+    if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
+        PLOG(ERROR) << "waitid failed";
         return false;
-    } else if (pid == -1) {
-        PLOG(ERROR) << "waitpid failed";
-        return false;
+    }
+
+    auto pid = siginfo.si_pid;
+    if (pid == 0) return false;
+
+    // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
+    // whenever the function returns from this point forward.
+    // We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we
+    // want the pid to remain valid throughout that (and potentially future) usages.
+    auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });
+
+    if (PropertyChildReap(pid)) {
+        return true;
     }
 
     Service* svc = FindServiceByPid(pid);
@@ -1103,24 +1125,20 @@
     std::string name;
     std::string wait_string;
     if (svc) {
-        name = android::base::StringPrintf("Service '%s' (pid %d)",
-                                           svc->name().c_str(), pid);
+        name = StringPrintf("Service '%s' (pid %d)", svc->name().c_str(), pid);
         if (svc->flags() & SVC_EXEC) {
-            wait_string =
-                android::base::StringPrintf(" waiting took %f seconds", exec_waiter_->duration_s());
+            wait_string = StringPrintf(" waiting took %f seconds",
+                                       exec_waiter_->duration().count() / 1000.0f);
         }
     } else {
-        name = android::base::StringPrintf("Untracked pid %d", pid);
+        name = StringPrintf("Untracked pid %d", pid);
     }
 
+    auto status = siginfo.si_status;
     if (WIFEXITED(status)) {
         LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;
     } else if (WIFSIGNALED(status)) {
         LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;
-    } else if (WIFSTOPPED(status)) {
-        LOG(INFO) << name << " stopped by signal " << WSTOPSIG(status) << wait_string;
-    } else {
-        LOG(INFO) << name << " state changed" << wait_string;
     }
 
     if (!svc) {
@@ -1144,6 +1162,15 @@
     }
 }
 
+void ServiceManager::ClearExecWait() {
+    // Clear EXEC flag if there is one pending
+    // And clear the wait flag
+    for (const auto& s : services_) {
+        s->UnSetExec();
+    }
+    exec_waiter_.reset();
+}
+
 bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                  int line, std::string* err) {
     if (args.size() < 3) {
@@ -1185,3 +1212,6 @@
     // the service name to the "ctl.start" and "ctl.stop" properties.)
     return is_legal_property_name("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/service.h b/init/service.h
index b9c270a..f682abd 100644
--- a/init/service.h
+++ b/init/service.h
@@ -32,7 +32,6 @@
 #include "descriptors.h"
 #include "init_parser.h"
 #include "keyword_map.h"
-#include "util.h"
 
 #define SVC_DISABLED 0x001        // do not autostart with class
 #define SVC_ONESHOT 0x002         // do not restart on exit
@@ -55,8 +54,8 @@
 
 #define NR_SVC_SUPP_GIDS 12    // twelve supplementary groups
 
-class Action;
-class ServiceManager;
+namespace android {
+namespace init {
 
 struct ServiceEnvironmentInfo {
     ServiceEnvironmentInfo();
@@ -76,7 +75,7 @@
 
     bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
     bool ParseLine(const std::vector<std::string>& args, std::string* err);
-    bool ExecStart(std::unique_ptr<Timer>* exec_waiter);
+    bool ExecStart(std::unique_ptr<android::base::Timer>* exec_waiter);
     bool Start();
     bool StartIfNotDisabled();
     bool Enable();
@@ -89,6 +88,7 @@
     void DumpState() const;
     void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
     bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
+    void UnSetExec() { flags_ &= ~SVC_EXEC; }
 
     const std::string& name() const { return name_; }
     const std::set<std::string>& classnames() const { return classnames_; }
@@ -137,6 +137,7 @@
     bool ParseNamespace(const std::vector<std::string>& args, std::string* err);
     bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
     bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
+    bool ParseShutdown(const std::vector<std::string>& args, std::string* err);
     bool ParseSocket(const std::vector<std::string>& args, std::string* err);
     bool ParseFile(const std::vector<std::string>& args, std::string* err);
     bool ParseUser(const std::vector<std::string>& args, std::string* err);
@@ -186,7 +187,7 @@
 };
 
 class ServiceManager {
-public:
+  public:
     static ServiceManager& GetInstance();
 
     // Exposed for testing
@@ -208,14 +209,15 @@
     void ReapAnyOutstandingChildren();
     void RemoveService(const Service& svc);
     void DumpState() const;
+    void ClearExecWait();
 
-private:
+  private:
     // Cleans up a child process that exited.
     // Returns true iff a children was cleaned up.
     bool ReapOneProcess();
 
     static int exec_count_; // Every service needs a unique name.
-    std::unique_ptr<Timer> exec_waiter_;
+    std::unique_ptr<android::base::Timer> exec_waiter_;
 
     std::vector<std::unique_ptr<Service>> services_;
 };
@@ -236,4 +238,7 @@
     std::unique_ptr<Service> service_;
 };
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/service_test.cpp b/init/service_test.cpp
index b9c4627..44f28a3 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -23,6 +23,9 @@
 
 #include <gtest/gtest.h>
 
+namespace android {
+namespace init {
+
 TEST(service, pod_initialized) {
     constexpr auto memory_size = sizeof(Service);
     alignas(alignof(Service)) char old_memory[memory_size];
@@ -67,3 +70,6 @@
     EXPECT_EQ(-1000, service_in_old_memory2->oom_score_adjust());
     EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index 4d56d84..db1bfcf 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -21,11 +21,13 @@
 #include <unistd.h>
 
 #include <android-base/logging.h>
-#include <android-base/stringprintf.h>
 
 #include "init.h"
 #include "service.h"
 
+namespace android {
+namespace init {
+
 static int signal_write_fd = -1;
 static int signal_read_fd = -1;
 
@@ -65,3 +67,6 @@
 
     register_epoll_handler(signal_read_fd, handle_signal);
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/signal_handler.h b/init/signal_handler.h
index 449b4af..f7881ab 100644
--- a/init/signal_handler.h
+++ b/init/signal_handler.h
@@ -17,6 +17,12 @@
 #ifndef _INIT_SIGNAL_HANDLER_H_
 #define _INIT_SIGNAL_HANDLER_H_
 
+namespace android {
+namespace init {
+
 void signal_handler_init(void);
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/uevent.h b/init/uevent.h
new file mode 100644
index 0000000..c4fd945
--- /dev/null
+++ b/init/uevent.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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 _INIT_UEVENT_H
+#define _INIT_UEVENT_H
+
+#include <string>
+
+namespace android {
+namespace init {
+
+struct Uevent {
+    std::string action;
+    std::string path;
+    std::string subsystem;
+    std::string firmware;
+    std::string partition_name;
+    std::string device_name;
+    int partition_num;
+    int major;
+    int minor;
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
new file mode 100644
index 0000000..ac1d7c7
--- /dev/null
+++ b/init/uevent_listener.cpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2017 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 "uevent_listener.h"
+
+#include <fcntl.h>
+#include <poll.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+#include <cutils/uevent.h>
+
+namespace android {
+namespace init {
+
+static void ParseEvent(const char* msg, Uevent* uevent) {
+    uevent->partition_num = -1;
+    uevent->major = -1;
+    uevent->minor = -1;
+    uevent->action.clear();
+    uevent->path.clear();
+    uevent->subsystem.clear();
+    uevent->firmware.clear();
+    uevent->partition_name.clear();
+    uevent->device_name.clear();
+    // currently ignoring SEQNUM
+    while (*msg) {
+        if (!strncmp(msg, "ACTION=", 7)) {
+            msg += 7;
+            uevent->action = msg;
+        } else if (!strncmp(msg, "DEVPATH=", 8)) {
+            msg += 8;
+            uevent->path = msg;
+        } else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
+            msg += 10;
+            uevent->subsystem = msg;
+        } else if (!strncmp(msg, "FIRMWARE=", 9)) {
+            msg += 9;
+            uevent->firmware = msg;
+        } else if (!strncmp(msg, "MAJOR=", 6)) {
+            msg += 6;
+            uevent->major = atoi(msg);
+        } else if (!strncmp(msg, "MINOR=", 6)) {
+            msg += 6;
+            uevent->minor = atoi(msg);
+        } else if (!strncmp(msg, "PARTN=", 6)) {
+            msg += 6;
+            uevent->partition_num = atoi(msg);
+        } else if (!strncmp(msg, "PARTNAME=", 9)) {
+            msg += 9;
+            uevent->partition_name = msg;
+        } else if (!strncmp(msg, "DEVNAME=", 8)) {
+            msg += 8;
+            uevent->device_name = msg;
+        }
+
+        // advance to after the next \0
+        while (*msg++)
+            ;
+    }
+
+    if (LOG_UEVENTS) {
+        LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
+                  << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
+                  << ", " << uevent->minor << " }";
+    }
+}
+
+UeventListener::UeventListener() {
+    // is 256K enough? udev uses 16MB!
+    device_fd_.reset(uevent_open_socket(256 * 1024, true));
+    if (device_fd_ == -1) {
+        LOG(FATAL) << "Could not open uevent socket";
+    }
+
+    fcntl(device_fd_, F_SETFL, O_NONBLOCK);
+}
+
+bool UeventListener::ReadUevent(Uevent* uevent) const {
+    char msg[UEVENT_MSG_LEN + 2];
+    int n = uevent_kernel_multicast_recv(device_fd_, msg, UEVENT_MSG_LEN);
+    if (n <= 0) {
+        if (errno != EAGAIN && errno != EWOULDBLOCK) {
+            LOG(ERROR) << "Error reading from Uevent Fd";
+        }
+        return false;
+    }
+    if (n >= UEVENT_MSG_LEN) {
+        LOG(ERROR) << "Uevent overflowed buffer, discarding";
+        // Return true here even if we discard as we may have more uevents pending and we
+        // want to keep processing them.
+        return true;
+    }
+
+    msg[n] = '\0';
+    msg[n + 1] = '\0';
+
+    ParseEvent(msg, uevent);
+
+    return true;
+}
+
+// RegenerateUevents*() walks parts of the /sys tree and pokes the uevent files to cause the kernel
+// to regenerate device add uevents that have already happened.  This is particularly useful when
+// starting ueventd, to regenerate all of the uevents that it had previously missed.
+//
+// We drain any pending events from the netlink socket every time we poke another uevent file to
+// make sure we don't overrun the socket's buffer.
+//
+
+ListenerAction UeventListener::RegenerateUeventsForDir(DIR* d,
+                                                       const ListenerCallback& callback) const {
+    int dfd = dirfd(d);
+
+    int fd = openat(dfd, "uevent", O_WRONLY);
+    if (fd >= 0) {
+        write(fd, "add\n", 4);
+        close(fd);
+
+        Uevent uevent;
+        while (ReadUevent(&uevent)) {
+            if (callback(uevent) == ListenerAction::kStop) return ListenerAction::kStop;
+        }
+    }
+
+    dirent* de;
+    while ((de = readdir(d)) != nullptr) {
+        if (de->d_type != DT_DIR || de->d_name[0] == '.') continue;
+
+        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
+        if (fd < 0) continue;
+
+        std::unique_ptr<DIR, decltype(&closedir)> d2(fdopendir(fd), closedir);
+        if (d2 == 0) {
+            close(fd);
+        } else {
+            if (RegenerateUeventsForDir(d2.get(), callback) == ListenerAction::kStop) {
+                return ListenerAction::kStop;
+            }
+        }
+    }
+
+    // default is always to continue looking for uevents
+    return ListenerAction::kContinue;
+}
+
+ListenerAction UeventListener::RegenerateUeventsForPath(const std::string& path,
+                                                        const ListenerCallback& callback) const {
+    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
+    if (!d) return ListenerAction::kContinue;
+
+    return RegenerateUeventsForDir(d.get(), callback);
+}
+
+static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
+
+void UeventListener::RegenerateUevents(const ListenerCallback& callback) const {
+    for (const auto path : kRegenerationPaths) {
+        if (RegenerateUeventsForPath(path, callback) == ListenerAction::kStop) return;
+    }
+}
+
+void UeventListener::Poll(const ListenerCallback& callback,
+                          const std::optional<std::chrono::milliseconds> relative_timeout) const {
+    using namespace std::chrono;
+
+    pollfd ufd;
+    ufd.events = POLLIN;
+    ufd.fd = device_fd_;
+
+    auto start_time = steady_clock::now();
+
+    while (true) {
+        ufd.revents = 0;
+
+        int timeout_ms = -1;
+        if (relative_timeout) {
+            auto now = steady_clock::now();
+            auto time_elapsed = duration_cast<milliseconds>(now - start_time);
+            if (time_elapsed > *relative_timeout) return;
+
+            auto remaining_timeout = *relative_timeout - time_elapsed;
+            timeout_ms = remaining_timeout.count();
+        }
+
+        int nr = poll(&ufd, 1, timeout_ms);
+        if (nr == 0) return;
+        if (nr < 0) {
+            PLOG(ERROR) << "poll() of uevent socket failed, continuing";
+            continue;
+        }
+        if (ufd.revents & POLLIN) {
+            // We're non-blocking, so if we receive a poll event keep processing until
+            // we have exhausted all uevent messages.
+            Uevent uevent;
+            while (ReadUevent(&uevent)) {
+                if (callback(uevent) == ListenerAction::kStop) return;
+            }
+        }
+    }
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
new file mode 100644
index 0000000..5b453fe
--- /dev/null
+++ b/init/uevent_listener.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 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 _INIT_UEVENT_LISTENER_H
+#define _INIT_UEVENT_LISTENER_H
+
+#include <dirent.h>
+
+#include <chrono>
+#include <functional>
+#include <optional>
+
+#include <android-base/unique_fd.h>
+
+#include "uevent.h"
+
+#define UEVENT_MSG_LEN 2048
+
+namespace android {
+namespace init {
+
+enum class ListenerAction {
+    kStop = 0,  // Stop regenerating uevents as we've handled the one(s) we're interested in.
+    kContinue,  // Continue regenerating uevents as we haven't seen the one(s) we're interested in.
+};
+
+using ListenerCallback = std::function<ListenerAction(const Uevent&)>;
+
+class UeventListener {
+  public:
+    UeventListener();
+
+    void RegenerateUevents(const ListenerCallback& callback) const;
+    ListenerAction RegenerateUeventsForPath(const std::string& path,
+                                            const ListenerCallback& callback) const;
+    void Poll(const ListenerCallback& callback,
+              const std::optional<std::chrono::milliseconds> relative_timeout = {}) const;
+
+  private:
+    bool ReadUevent(Uevent* uevent) const;
+    ListenerAction RegenerateUeventsForDir(DIR* d, const ListenerCallback& callback) const;
+
+    android::base::unique_fd device_fd_;
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 8c0c574..c0eae1e 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -18,50 +18,215 @@
 
 #include <ctype.h>
 #include <fcntl.h>
-#include <poll.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/wait.h>
 
+#include <set>
+#include <thread>
+
+#include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
-#include <android-base/stringprintf.h>
+#include <selinux/android.h>
 #include <selinux/selinux.h>
 
 #include "devices.h"
+#include "firmware_handler.h"
 #include "log.h"
+#include "uevent_listener.h"
+#include "ueventd_parser.h"
 #include "util.h"
 
-int ueventd_main(int argc, char **argv)
-{
-    /*
-     * init sets the umask to 077 for forked processes. We need to
-     * create files with exact permissions, without modification by
-     * the umask.
-     */
-    umask(000);
+// At a high level, ueventd listens for uevent messages generated by the kernel through a netlink
+// socket.  When ueventd receives such a message it handles it by taking appropriate actions,
+// which can typically be creating a device node in /dev, setting file permissions, setting selinux
+// labels, etc.
+// Ueventd also handles loading of firmware that the kernel requests, and creates symlinks for block
+// and character devices.
 
-    /* Prevent fire-and-forget children from becoming zombies.
-     * If we should need to wait() for some children in the future
-     * (as opposed to none right now), double-forking here instead
-     * of ignoring SIGCHLD may be the better solution.
-     */
-    signal(SIGCHLD, SIG_IGN);
+// When ueventd starts, it regenerates uevents for all currently registered devices by traversing
+// /sys and writing 'add' to each 'uevent' file that it finds.  This causes the kernel to generate
+// and resend uevent messages for all of the currently registered devices.  This is done, because
+// ueventd would not have been running when these devices were registered and therefore was unable
+// to receive their uevent messages and handle them appropriately.  This process is known as
+// 'cold boot'.
 
-    InitKernelLogging(argv);
+// 'init' currently waits synchronously on the cold boot process of ueventd before it continues
+// its boot process.  For this reason, cold boot should be as quick as possible.  One way to achieve
+// a speed up here is to parallelize the handling of ueventd messages, which consume the bulk of the
+// time during cold boot.
 
-    LOG(INFO) << "ueventd started!";
+// Handling of uevent messages has two unique properties:
+// 1) It can be done in isolation; it doesn't need to read or write any status once it is started.
+// 2) It uses setegid() and setfscreatecon() so either care (aka locking) must be taken to ensure
+//    that no file system operations are done while the uevent process has an abnormal egid or
+//    fscreatecon or this handling must happen in a separate process.
+// Given the above two properties, it is best to fork() subprocesses to handle the uevents.  This
+// reduces the overhead and complexity that would be required in a solution with threads and locks.
+// In testing, a racy multithreaded solution has the same performance as the fork() solution, so
+// there is no reason to deal with the complexity of the former.
 
-    selinux_callback cb;
-    cb.func_log = selinux_klog_callback;
-    selinux_set_callback(SELINUX_CB_LOG, cb);
+// One other important caveat during the boot process is the handling of SELinux restorecon.
+// Since many devices have child devices, calling selinux_android_restorecon() recursively for each
+// device when its uevent is handled, results in multiple restorecon operations being done on a
+// given file.  It is more efficient to simply do restorecon recursively on /sys during cold boot,
+// than to do restorecon on each device as its uevent is handled.  This only applies to cold boot;
+// once that has completed, restorecon is done for each device as its uevent is handled.
 
-    Parser& parser = Parser::GetInstance();
-    parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>());
+// With all of the above considered, the cold boot process has the below steps:
+// 1) ueventd regenerates uevents by doing the /sys traversal and listens to the netlink socket for
+//    the generated uevents.  It writes these uevents into a queue represented by a vector.
+//
+// 2) ueventd forks 'n' separate uevent handler subprocesses and has each of them to handle the
+//    uevents in the queue based on a starting offset (their process number) and a stride (the total
+//    number of processes).  Note that no IPC happens at this point and only const functions from
+//    DeviceHandler should be called from this context.
+//
+// 3) In parallel to the subprocesses handling the uevents, the main thread of ueventd calls
+//    selinux_android_restorecon() recursively on /sys/class, /sys/block, and /sys/devices.
+//
+// 4) Once the restorecon operation finishes, the main thread calls waitpid() to wait for all
+//    subprocess handlers to complete and exit.  Once this happens, it marks coldboot as having
+//    completed.
+//
+// At this point, ueventd is single threaded, poll()'s and then handles any future uevents.
+
+// Lastly, it should be noted that uevents that occur during the coldboot process are handled
+// without issue after the coldboot process completes.  This is because the uevent listener is
+// paused while the uevent handler and restorecon actions take place.  Once coldboot completes,
+// the uevent listener resumes in polling mode and will handle the uevents that occurred during
+// coldboot.
+
+namespace android {
+namespace init {
+
+class ColdBoot {
+  public:
+    ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler)
+        : uevent_listener_(uevent_listener),
+          device_handler_(device_handler),
+          num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}
+
+    void Run();
+
+  private:
+    void UeventHandlerMain(unsigned int process_num, unsigned int total_processes);
+    void RegenerateUevents();
+    void ForkSubProcesses();
+    void DoRestoreCon();
+    void WaitForSubProcesses();
+
+    UeventListener& uevent_listener_;
+    DeviceHandler& device_handler_;
+
+    unsigned int num_handler_subprocesses_;
+    std::vector<Uevent> uevent_queue_;
+
+    std::set<pid_t> subprocess_pids_;
+};
+
+void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
+    for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) {
+        auto& uevent = uevent_queue_[i];
+        device_handler_.HandleDeviceEvent(uevent);
+    }
+    _exit(EXIT_SUCCESS);
+}
+
+void ColdBoot::RegenerateUevents() {
+    uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
+        HandleFirmwareEvent(uevent);
+
+        uevent_queue_.emplace_back(std::move(uevent));
+        return ListenerAction::kContinue;
+    });
+}
+
+void ColdBoot::ForkSubProcesses() {
+    for (unsigned int i = 0; i < num_handler_subprocesses_; ++i) {
+        auto pid = fork();
+        if (pid < 0) {
+            PLOG(FATAL) << "fork() failed!";
+        }
+
+        if (pid == 0) {
+            UeventHandlerMain(i, num_handler_subprocesses_);
+        }
+
+        subprocess_pids_.emplace(pid);
+    }
+}
+
+void ColdBoot::DoRestoreCon() {
+    selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
+    device_handler_.set_skip_restorecon(false);
+}
+
+void ColdBoot::WaitForSubProcesses() {
+    // Treat subprocesses that crash or get stuck the same as if ueventd itself has crashed or gets
+    // stuck.
+    //
+    // When a subprocess crashes, we fatally abort from ueventd.  init will restart ueventd when
+    // init reaps it, and the cold boot process will start again.  If this continues to fail, then
+    // since ueventd is marked as a critical service, init will reboot to recovery.
+    //
+    // When a subprocess gets stuck, keep ueventd spinning waiting for it.  init has a timeout for
+    // cold boot and will reboot to the bootloader if ueventd does not complete in time.
+    while (!subprocess_pids_.empty()) {
+        int status;
+        pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, 0));
+        if (pid == -1) {
+            PLOG(ERROR) << "waitpid() failed";
+            continue;
+        }
+
+        auto it = std::find(subprocess_pids_.begin(), subprocess_pids_.end(), pid);
+        if (it == subprocess_pids_.end()) continue;
+
+        if (WIFEXITED(status)) {
+            if (WEXITSTATUS(status) == EXIT_SUCCESS) {
+                subprocess_pids_.erase(it);
+            } else {
+                LOG(FATAL) << "subprocess exited with status " << WEXITSTATUS(status);
+            }
+        } else if (WIFSIGNALED(status)) {
+            LOG(FATAL) << "subprocess killed by signal " << WTERMSIG(status);
+        }
+    }
+}
+
+void ColdBoot::Run() {
+    android::base::Timer cold_boot_timer;
+
+    RegenerateUevents();
+
+    ForkSubProcesses();
+
+    DoRestoreCon();
+
+    WaitForSubProcesses();
+
+    close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+    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;
-    parser.AddSingleLineParser("/sys/", std::bind(ParsePermissionsLine, _1, _2, true));
-    parser.AddSingleLineParser("/dev/", std::bind(ParsePermissionsLine, _1, _2, false));
+    std::vector<SysfsPermissions> sysfs_permissions;
+    std::vector<Permissions> dev_permissions;
+    parser.AddSingleLineParser(
+        "/sys/", std::bind(ParsePermissionsLine, _1, _2, &sysfs_permissions, nullptr));
+    parser.AddSingleLineParser("/dev/",
+                               std::bind(ParsePermissionsLine, _1, _2, nullptr, &dev_permissions));
+
     parser.ParseConfig("/ueventd.rc");
     parser.ParseConfig("/vendor/ueventd.rc");
     parser.ParseConfig("/odm/ueventd.rc");
@@ -76,22 +241,49 @@
     std::string hardware = android::base::GetProperty("ro.hardware", "");
     parser.ParseConfig("/ueventd." + hardware + ".rc");
 
-    device_init();
+    return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
+                         std::move(subsystems), true);
+}
 
-    pollfd ufd;
-    ufd.events = POLLIN;
-    ufd.fd = get_device_fd();
+int ueventd_main(int argc, char** argv) {
+    /*
+     * init sets the umask to 077 for forked processes. We need to
+     * create files with exact permissions, without modification by
+     * the umask.
+     */
+    umask(000);
 
-    while (true) {
-        ufd.revents = 0;
-        int nr = poll(&ufd, 1, -1);
-        if (nr <= 0) {
-            continue;
-        }
-        if (ufd.revents & POLLIN) {
-            handle_device_fd();
-        }
+    InitKernelLogging(argv);
+
+    LOG(INFO) << "ueventd started!";
+
+    selinux_callback cb;
+    cb.func_log = selinux_klog_callback;
+    selinux_set_callback(SELINUX_CB_LOG, cb);
+
+    DeviceHandler device_handler = CreateDeviceHandler();
+    UeventListener uevent_listener;
+
+    if (access(COLDBOOT_DONE, F_OK) != 0) {
+        ColdBoot cold_boot(uevent_listener, device_handler);
+        cold_boot.Run();
     }
 
+    // We use waitpid() in ColdBoot, so we can't ignore SIGCHLD until now.
+    signal(SIGCHLD, SIG_IGN);
+    // Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN
+    // for SIGCHLD above.
+    while (waitpid(-1, nullptr, WNOHANG) > 0) {
+    }
+
+    uevent_listener.Poll([&device_handler](const Uevent& uevent) {
+        HandleFirmwareEvent(uevent);
+        device_handler.HandleDeviceEvent(uevent);
+        return ListenerAction::kContinue;
+    });
+
     return 0;
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/ueventd.h b/init/ueventd.h
index 1f424d3..51775ec 100644
--- a/init/ueventd.h
+++ b/init/ueventd.h
@@ -17,6 +17,12 @@
 #ifndef _INIT_UEVENTD_H_
 #define _INIT_UEVENTD_H_
 
+namespace android {
+namespace init {
+
 int ueventd_main(int argc, char** argv);
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
new file mode 100644
index 0000000..02e0d42
--- /dev/null
+++ b/init/ueventd_parser.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 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 <grp.h>
+#include <pwd.h>
+
+#include "keyword_map.h"
+
+namespace android {
+namespace init {
+
+bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
+                          std::vector<SysfsPermissions>* out_sysfs_permissions,
+                          std::vector<Permissions>* out_dev_permissions) {
+    bool is_sysfs = out_sysfs_permissions != nullptr;
+    if (is_sysfs && args.size() != 5) {
+        *err = "/sys/ lines must have 5 entries";
+        return false;
+    }
+
+    if (!is_sysfs && args.size() != 4) {
+        *err = "/dev/ lines must have 4 entries";
+        return false;
+    }
+
+    auto it = args.begin();
+    const std::string& name = *it++;
+
+    std::string sysfs_attribute;
+    if (is_sysfs) sysfs_attribute = *it++;
+
+    // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>
+    std::string& perm_string = *it++;
+    char* end_pointer = 0;
+    mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
+    if (end_pointer == nullptr || *end_pointer != '\0') {
+        *err = "invalid mode '" + perm_string + "'";
+        return false;
+    }
+
+    std::string& uid_string = *it++;
+    passwd* pwd = getpwnam(uid_string.c_str());
+    if (!pwd) {
+        *err = "invalid uid '" + uid_string + "'";
+        return false;
+    }
+    uid_t uid = pwd->pw_uid;
+
+    std::string& gid_string = *it++;
+    struct group* grp = getgrnam(gid_string.c_str());
+    if (!grp) {
+        *err = "invalid gid '" + gid_string + "'";
+        return false;
+    }
+    gid_t gid = grp->gr_gid;
+
+    if (is_sysfs) {
+        out_sysfs_permissions->emplace_back(name, sysfs_attribute, perm, uid, gid);
+    } else {
+        out_dev_permissions->emplace_back(name, perm, uid, gid);
+    }
+    return true;
+}
+
+bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                   int line, std::string* err) {
+    if (args.size() != 2) {
+        *err = "subsystems must have exactly one name";
+        return false;
+    }
+
+    if (std::find(subsystems_->begin(), subsystems_->end(), args[1]) != subsystems_->end()) {
+        *err = "ignoring duplicate subsystem entry";
+        return false;
+    }
+
+    subsystem_.name_ = args[1];
+
+    return true;
+}
+
+bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
+    if (args[1] == "uevent_devname") {
+        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
+        return true;
+    }
+    if (args[1] == "uevent_devpath") {
+        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
+        return true;
+    }
+
+    *err = "invalid devname '" + args[1] + "'";
+    return false;
+}
+
+bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
+    if (args[1].front() != '/') {
+        *err = "dirname '" + args[1] + " ' does not start with '/'";
+        return false;
+    }
+
+    subsystem_.dir_name_ = args[1];
+    return true;
+}
+
+bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+    using OptionParser =
+        bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
+    static class OptionParserMap : public KeywordMap<OptionParser> {
+      private:
+        const Map& map() const override {
+            // clang-format off
+            static const Map option_parsers = {
+                {"devname",     {1,     1,      &SubsystemParser::ParseDevName}},
+                {"dirname",     {1,     1,      &SubsystemParser::ParseDirName}},
+            };
+            // clang-format on
+            return option_parsers;
+        }
+    } parser_map;
+
+    auto parser = parser_map.FindFunction(args, err);
+
+    if (!parser) {
+        return false;
+    }
+
+    return (this->*parser)(std::move(args), err);
+}
+
+void SubsystemParser::EndSection() {
+    subsystems_->emplace_back(std::move(subsystem_));
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
new file mode 100644
index 0000000..592df63
--- /dev/null
+++ b/init/ueventd_parser.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007 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 _INIT_UEVENTD_PARSER_H
+#define _INIT_UEVENTD_PARSER_H
+
+#include <string>
+#include <vector>
+
+#include "devices.h"
+#include "init_parser.h"
+
+namespace android {
+namespace init {
+
+class SubsystemParser : public SectionParser {
+  public:
+    SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
+    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
+                      std::string* err) override;
+    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+    void EndSection() override;
+
+  private:
+    bool ParseDevName(std::vector<std::string>&& args, std::string* err);
+    bool ParseDirName(std::vector<std::string>&& args, std::string* err);
+
+    Subsystem subsystem_;
+    std::vector<Subsystem>* subsystems_;
+};
+
+bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
+                          std::vector<SysfsPermissions>* out_sysfs_permissions,
+                          std::vector<Permissions>* out_dev_permissions);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
new file mode 100644
index 0000000..86d7055
--- /dev/null
+++ b/init/ueventd_test.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 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 <linux/futex.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/scopeguard.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <selinux/selinux.h>
+
+using namespace std::string_literals;
+
+template <typename T, typename F>
+void WriteFromMultipleThreads(std::vector<std::pair<std::string, T>>& files_and_parameters,
+                              F function) {
+    auto num_threads = files_and_parameters.size();
+    pthread_barrier_t barrier;
+    pthread_barrier_init(&barrier, nullptr, num_threads);
+    auto barrier_destroy =
+        android::base::make_scope_guard([&barrier]() { pthread_barrier_destroy(&barrier); });
+
+    auto make_thread_function = [&function, &barrier](const auto& file, const auto& parameter) {
+        return [&]() {
+            function(parameter);
+            pthread_barrier_wait(&barrier);
+            android::base::WriteStringToFile("<empty>", file);
+        };
+    };
+
+    std::vector<std::thread> threads;
+    // TODO(b/63712782): Structured bindings + templated containers are broken in clang :(
+    // for (const auto& [file, parameter] : files_and_parameters) {
+    for (const auto& pair : files_and_parameters) {
+        const auto& file = pair.first;
+        const auto& parameter = pair.second;
+        threads.emplace_back(std::thread(make_thread_function(file, parameter)));
+    }
+
+    for (auto& thread : threads) {
+        thread.join();
+    }
+}
+
+TEST(ueventd, setegid_IsPerThread) {
+    if (getuid() != 0) return;
+
+    TemporaryDir dir;
+
+    gid_t gid = 0;
+    std::vector<std::pair<std::string, gid_t>> files_and_gids;
+    std::generate_n(std::back_inserter(files_and_gids), 100, [&gid, &dir]() {
+        gid++;
+        return std::pair(dir.path + "/gid_"s + std::to_string(gid), gid);
+    });
+
+    WriteFromMultipleThreads(files_and_gids, [](gid_t gid) { EXPECT_EQ(0, setegid(gid)); });
+
+    for (const auto& [file, expected_gid] : files_and_gids) {
+        struct stat info;
+        EXPECT_EQ(0, stat(file.c_str(), &info));
+        EXPECT_EQ(expected_gid, info.st_gid);
+    }
+}
+
+TEST(ueventd, setfscreatecon_IsPerThread) {
+    if (getuid() != 0) return;
+
+    const char* const contexts[] = {
+        "u:object_r:audio_device:s0",
+        "u:object_r:sensors_device:s0",
+        "u:object_r:video_device:s0"
+        "u:object_r:zero_device:s0",
+    };
+
+    TemporaryDir dir;
+    std::vector<std::pair<std::string, std::string>> files_and_contexts;
+    for (const char* context : contexts) {
+        files_and_contexts.emplace_back(dir.path + "/context_"s + context, context);
+    }
+
+    WriteFromMultipleThreads(files_and_contexts, [](const std::string& context) {
+        EXPECT_EQ(0, setfscreatecon(context.c_str()));
+    });
+
+    for (const auto& [file, expected_context] : files_and_contexts) {
+        char* file_context;
+        EXPECT_GT(getfilecon(file.c_str(), &file_context), 0);
+        EXPECT_EQ(expected_context, file_context);
+        freecon(file_context);
+    }
+}
diff --git a/init/util.cpp b/init/util.cpp
index 75f81b9..2792794 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -50,6 +50,9 @@
 using android::base::boot_clock;
 using namespace std::literals::string_literals;
 
+namespace android {
+namespace init {
+
 // DecodeUid() - decodes and returns the given string, which can be either the
 // numeric or name representation, into the integer uid or gid. Returns
 // UINT_MAX on error.
@@ -367,14 +370,10 @@
 
 void panic() {
     LOG(ERROR) << "panic: rebooting to bootloader";
+    // Do not queue "shutdown" trigger since we want to shutdown immediately
     DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
 }
 
-std::ostream& operator<<(std::ostream& os, const Timer& t) {
-    os << t.duration_s() << " seconds";
-    return os;
-}
-
 // Reads the content of device tree file under kAndroidDtDir directory.
 // Returns true if the read is success, false otherwise.
 bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
@@ -397,3 +396,6 @@
     }
     return false;
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/util.h b/init/util.h
index 1ad6b77..452df2d 100644
--- a/init/util.h
+++ b/init/util.h
@@ -35,32 +35,15 @@
 using android::base::boot_clock;
 using namespace std::chrono_literals;
 
+namespace android {
+namespace init {
+
 int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
                  const char* socketcon, selabel_handle* sehandle);
 
 bool ReadFile(const std::string& path, std::string* content, std::string* err);
 bool WriteFile(const std::string& path, const std::string& content, std::string* err);
 
-class Timer {
-  public:
-    Timer() : start_(boot_clock::now()) {}
-
-    double duration_s() const {
-        typedef std::chrono::duration<double> double_duration;
-        return std::chrono::duration_cast<double_duration>(boot_clock::now() - start_).count();
-    }
-
-    int64_t duration_ms() const {
-        return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_)
-            .count();
-    }
-
-  private:
-    android::base::boot_clock::time_point start_;
-};
-
-std::ostream& operator<<(std::ostream& os, const Timer& t);
-
 bool DecodeUid(const std::string& name, uid_t* uid, std::string* err);
 
 int mkdir_recursive(const std::string& pathname, mode_t mode, selabel_handle* sehandle);
@@ -78,4 +61,7 @@
 bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
 bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 4bb8a83..c16ab74 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -26,6 +26,9 @@
 
 using namespace std::literals::string_literals;
 
+namespace android {
+namespace init {
+
 TEST(util, ReadFile_ENOENT) {
     std::string s("hello");
     std::string err;
@@ -187,3 +190,6 @@
     std::string path3 = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
     EXPECT_TRUE(is_dir(path1.c_str()));
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
index 7baa487..e0164b4 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -31,6 +31,9 @@
 
 #define DEV_NAME "/dev/watchdog"
 
+namespace android {
+namespace init {
+
 int watchdogd_main(int argc, char **argv) {
     InitKernelLogging(argv);
 
@@ -73,3 +76,6 @@
         sleep(interval);
     }
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/watchdogd.h b/init/watchdogd.h
index 8b48ab8..73f77d5 100644
--- a/init/watchdogd.h
+++ b/init/watchdogd.h
@@ -17,6 +17,12 @@
 #ifndef _INIT_WATCHDOGD_H_
 #define _INIT_WATCHDOGD_H_
 
+namespace android {
+namespace init {
+
 int watchdogd_main(int argc, char **argv);
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 7dd9227..e02aaf2 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -113,6 +113,7 @@
             static_libs: ["libasync_safe", "libcutils"],
         },
     },
+    whole_static_libs: ["libdemangle"],
 }
 
 cc_library_shared {
@@ -160,6 +161,7 @@
     shared_libs = [
         "libbase",
         "libunwind",
+        "libziparchive",
     ],
 }
 
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 3545661..e46d353 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -27,6 +27,8 @@
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
+#include <demangle.h>
+
 #include "BacktraceLog.h"
 #include "thread_utils.h"
 #include "UnwindCurrent.h"
@@ -62,8 +64,7 @@
   if (map->start == 0 || (map->flags & PROT_DEVICE_MAP)) {
     return "";
   }
-  std::string func_name = GetFunctionNameRaw(pc, offset);
-  return func_name;
+  return demangle(GetFunctionNameRaw(pc, offset).c_str());
 }
 
 bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, word_t* out_value) {
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 919b65b..13c4abf 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -266,14 +266,27 @@
     return false;
 }
 
+static inline bool prefix_cmp(bool partial, const char* prefix, size_t len, const char* path,
+                              size_t plen) {
+    return ((partial && plen >= len) || (plen == len)) && !strncmp(prefix, path, len);
+}
+
 // alias prefixes of "<partition>/<stuff>" to "system/<partition>/<stuff>" or
 // "system/<partition>/<stuff>" to "<partition>/<stuff>"
-static bool prefix_cmp(const char* prefix, const char* path, size_t len) {
-    if (!strncmp(prefix, path, len)) return true;
+static bool fs_config_cmp(bool partial, const char* prefix, size_t len, const char* path,
+                          size_t plen) {
+    // If name ends in * then allow partial matches.
+    if (!partial && prefix[len - 1] == '*') {
+        len--;
+        partial = true;
+    }
+
+    if (prefix_cmp(partial, prefix, len, path, plen)) return true;
 
     static const char system[] = "system/";
     if (!strncmp(path, system, strlen(system))) {
         path += strlen(system);
+        plen -= strlen(system);
     } else if (len <= strlen(system)) {
         return false;
     } else if (strncmp(prefix, system, strlen(system))) {
@@ -282,25 +295,11 @@
         prefix += strlen(system);
         len -= strlen(system);
     }
-    return is_partition(prefix, len) && !strncmp(prefix, path, len);
+    return is_partition(prefix, len) && prefix_cmp(partial, prefix, len, path, plen);
 }
-
-static bool fs_config_cmp(bool dir, const char* prefix, size_t len, const char* path, size_t plen) {
-    if (dir) {
-        if (plen < len) {
-            return false;
-        }
-    } else {
-        // If name ends in * then allow partial matches.
-        if (prefix[len - 1] == '*') {
-            return prefix_cmp(prefix, path, len - 1);
-        }
-        if (plen != len) {
-            return false;
-        }
-    }
-    return prefix_cmp(prefix, path, len);
-}
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__fs_config_cmp = fs_config_cmp;
+#endif
 
 void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
                unsigned* mode, uint64_t* capabilities) {
diff --git a/libcutils/include/cutils/sched_policy.h b/libcutils/include/cutils/sched_policy.h
index 15391d9..cf8f5c3 100644
--- a/libcutils/include/cutils/sched_policy.h
+++ b/libcutils/include/cutils/sched_policy.h
@@ -34,7 +34,7 @@
  * Check if Linux kernel enables SCHEDTUNE feature (only available in Android
  * common kernel or Linaro LSK, not in mainline Linux as of v4.9)
  *
- * Return value: 1 if Linux kernel CONFIG_SCHEDTUNE=y; 0 otherwise.
+ * Return value: 1 if Linux kernel CONFIG_CGROUP_SCHEDTUNE=y; 0 otherwise.
  */
 extern bool schedboost_enabled();
 
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index e29a844..f733e90 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -28,13 +28,6 @@
 
 #define UNUSED __attribute__((__unused__))
 
-#ifndef SLOGE
-#define SLOGE ALOGE
-#endif
-#ifndef SLOGW
-#define SLOGW ALOGW
-#endif
-
 /* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
  * Call this any place a SchedPolicy is used as an input parameter.
  * Returns the possibly re-mapped policy.
@@ -123,11 +116,8 @@
     on where init.rc mounts cpuset. That's why we'd better require this
     configuration be set if CONFIG_CPUSETS is set.
 
-    With runtime check using the following function, build time
-    variables like ENABLE_CPUSETS (used in Android.mk) or cpusets (used
-    in Android.bp) are not needed.
+    In older releases, this was controlled by build-time configuration.
  */
-
 bool cpusets_enabled() {
     static bool enabled = (access("/dev/cpuset/tasks", F_OK) == 0);
 
@@ -136,15 +126,11 @@
 
 /*
     Similar to CONFIG_CPUSETS above, but with a different configuration
-    CONFIG_SCHEDTUNE that's in Android common Linux kernel and Linaro
+    CONFIG_CGROUP_SCHEDTUNE that's in Android common Linux kernel and Linaro
     Stable Kernel (LSK), but not in mainline Linux as of v4.9.
 
-    With runtime check using the following function, build time
-    variables like ENABLE_SCHEDBOOST (used in Android.mk) or schedboost
-    (used in Android.bp) are not needed.
-
+    In older releases, this was controlled by build-time configuration.
  */
-
 bool schedboost_enabled() {
     static bool enabled = (access("/dev/stune/tasks", F_OK) == 0);
 
@@ -340,7 +326,7 @@
     return 0;
 }
 
-static void set_timerslack_ns(int tid, unsigned long long slack) {
+static void set_timerslack_ns(int tid, unsigned long slack) {
     // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
     // TODO: once we've backported this, log if the open(2) fails.
     if (__sys_supports_timerslack) {
@@ -348,7 +334,7 @@
         snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
         int fd = open(buf, O_WRONLY | O_CLOEXEC);
         if (fd != -1) {
-            int len = snprintf(buf, sizeof(buf), "%llu", slack);
+            int len = snprintf(buf, sizeof(buf), "%lu", slack);
             if (write(fd, buf, len) != len) {
                 SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
             }
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp
index a62cd51..391adb6 100644
--- a/libcutils/tests/fs_config.cpp
+++ b/libcutils/tests/fs_config.cpp
@@ -29,12 +29,39 @@
 
 extern const fs_path_config* __for_testing_only__android_dirs;
 extern const fs_path_config* __for_testing_only__android_files;
+extern bool (*__for_testing_only__fs_config_cmp)(bool, const char*, size_t, const char*, size_t);
 
 // Maximum entries in system/core/libcutils/fs_config.cpp:android_* before we
 // hit a nullptr termination, before we declare the list is just too big or
 // could be missing the nullptr.
 static constexpr size_t max_idx = 4096;
 
+static const struct fs_config_cmp_test {
+    bool dir;
+    const char* prefix;
+    const char* path;
+    bool match;
+} fs_config_cmp_tests[] = {
+    // clang-format off
+    { true,  "system/lib",             "system/lib/hw",           true  },
+    { true,  "vendor/lib",             "system/vendor/lib/hw",    true  },
+    { true,  "system/vendor/lib",      "vendor/lib/hw",           true  },
+    { true,  "system/vendor/lib",      "system/vendor/lib/hw",    true  },
+    { false, "vendor/bin/wifi",        "system/vendor/bin/w",     false },
+    { false, "vendor/bin/wifi",        "system/vendor/bin/wifi",  true  },
+    { false, "vendor/bin/wifi",        "system/vendor/bin/wifi2", false },
+    { false, "system/vendor/bin/wifi", "system/vendor/bin/wifi",  true, },
+    { false, "odm/bin/wifi",           "system/odm/bin/wifi",     true  },
+    { false, "oem/bin/wifi",           "system/oem/bin/wifi",     true  },
+    { false, "data/bin/wifi",          "system/data/bin/wifi",    false },
+    { false, "system/bin/*",           "system/bin/wifi",         true  },
+    { false, "vendor/bin/*",           "system/vendor/bin/wifi",  true  },
+    { false, "system/bin/*",           "system/bin",              false },
+    { false, "system/vendor/bin/*",    "vendor/bin/wifi",         true  },
+    { false, NULL,                     NULL,                      false },
+    // clang-format on
+};
+
 static bool check_unique(std::vector<const char*>& paths, const std::string& config_name,
                          const std::string& prefix) {
     bool retval = false;
@@ -106,6 +133,22 @@
     return check_unique(paths_tmp, config, prefix) || retval;
 }
 
+static bool check_fs_config_cmp(const fs_config_cmp_test* tests) {
+    bool match, retval = false;
+    for (size_t idx = 0; tests[idx].prefix; ++idx) {
+        match = __for_testing_only__fs_config_cmp(tests[idx].dir, tests[idx].prefix,
+                                                  strlen(tests[idx].prefix), tests[idx].path,
+                                                  strlen(tests[idx].path));
+        if (match != tests[idx].match) {
+            GTEST_LOG_(ERROR) << tests[idx].path << (match ? " matched " : " didn't match ")
+                              << tests[idx].prefix;
+            retval = true;
+            break;
+        }
+    }
+    return retval;
+}
+
 #define endof(pointer, field) (offsetof(typeof(*(pointer)), field) + sizeof((pointer)->field))
 
 static bool check_unique(const std::string& config, const std::string& prefix) {
@@ -199,3 +242,7 @@
 TEST(fs_config, odm_files_alias) {
     check_two(__for_testing_only__android_files, "files", "odm/");
 }
+
+TEST(fs_config, system_alias) {
+    EXPECT_FALSE(check_fs_config_cmp(fs_config_cmp_tests));
+}
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index 041fd63..088981a 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -1,5 +1,6 @@
 cc_library {
     name: "libdiskconfig",
+    vendor_available: true,
     srcs: [
         "diskconfig.c",
         "diskutils.c",
diff --git a/libion/Android.bp b/libion/Android.bp
index da98111..6f267e4 100644
--- a/libion/Android.bp
+++ b/libion/Android.bp
@@ -1,6 +1,7 @@
 
 cc_library {
     name: "libion",
+		vendor_available: true,
     srcs: ["ion.c"],
     shared_libs: ["liblog"],
     local_include_dirs: [
diff --git a/liblog/Android.bp b/liblog/Android.bp
index e74aa82..b98d18f 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -42,6 +42,24 @@
     "logd_writer.c",
 ]
 
+cc_library_headers {
+    name: "liblog_headers",
+    host_supported: true,
+    vendor_available: true,
+    export_include_dirs: ["include"],
+    target: {
+        windows: {
+            enabled: true,
+        },
+        linux_bionic: {
+            enabled: true,
+        },
+        vendor: {
+            export_include_dirs: ["include_vndk"],
+        },
+    },
+}
+
 // Shared and static library for host and device
 // ========================================================
 cc_library {
@@ -81,7 +99,8 @@
         },
     },
 
-    export_include_dirs: ["include"],
+    header_libs: ["liblog_headers"],
+    export_header_lib_headers: ["liblog_headers"],
 
     cflags: [
         "-Werror",
@@ -100,7 +119,7 @@
 }
 
 ndk_headers {
-    name: "liblog_headers",
+    name: "liblog_ndk_headers",
     from: "include/android",
     to: "android",
     srcs: ["include/android/log.h"],
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 3a215e9..d01708d 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -161,7 +161,7 @@
 #endif
 
 #if __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
-clockid_t android_log_clockid();
+clockid_t android_log_clockid(void);
 #endif
 
 #endif /* __linux__ */
@@ -185,7 +185,7 @@
  * May be used to clean up File descriptors after a Fork, the resources are
  * all O_CLOEXEC so wil self clean on exec().
  */
-void __android_log_close();
+void __android_log_close(void);
 #endif
 
 #ifndef __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index 057be5d..bb1ce34 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -177,6 +177,12 @@
     return *this;
   }
 
+  android_log_event_list& operator<<(bool value) {
+    int retval = android_log_write_int32(ctx, value ? 1 : 0);
+    if (retval < 0) ret = retval;
+    return *this;
+  }
+
   android_log_event_list& operator<<(int64_t value) {
     int retval = android_log_write_int64(ctx, value);
     if (retval < 0) ret = retval;
diff --git a/liblog/include_vndk/log/log.h b/liblog/include_vndk/log/log.h
index 01623df..a79beec 100644
--- a/liblog/include_vndk/log/log.h
+++ b/liblog/include_vndk/log/log.h
@@ -9,6 +9,7 @@
 #include <log/log_radio.h>
 #include <log/log_read.h>
 #include <log/log_safetynet.h>
+#include <log/log_system.h>
 #include <log/log_time.h>
 
 /*
diff --git a/liblog/include_vndk/log/log_system.h b/liblog/include_vndk/log/log_system.h
new file mode 120000
index 0000000..d0d3904
--- /dev/null
+++ b/liblog/include_vndk/log/log_system.h
@@ -0,0 +1 @@
+../../include/log/log_system.h
\ No newline at end of file
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 71f74ab..46ec5ef 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1720,6 +1720,7 @@
 // Kills logd and toss all collected data, equivalent to logcat -b all -c,
 // except we also return errors to the logging callers.
 #ifdef USING_LOGGER_DEFAULT
+#ifdef __ANDROID__
 #ifdef TEST_PREFIX
 // helper to liblog.enoent to count end-to-end matching logging messages.
 static int count_matching_ts(log_time ts) {
@@ -1832,7 +1833,8 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
-#endif  // USING_LOCAL_LOGD
+#endif  // __ANDROID__
+#endif  // USING_LOGGER_DEFAULT
 
 // Below this point we run risks of setuid(AID_SYSTEM) which may affect others.
 
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index a63ab8e..6ed568a 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -23,6 +23,7 @@
 #include <android-base/stringprintf.h>
 #include <android/log.h>  // minimal logging API
 #include <gtest/gtest.h>
+#include <log/log_properties.h>
 // Test the APIs in this standalone include file
 #include <log/log_read.h>
 // Do not use anything in log/log_time.h despite side effects of the above.
@@ -97,9 +98,12 @@
     /* security buffer is allowed to be denied */
     if (strcmp("security", name)) {
       EXPECT_LT(0, get_log_size);
-      /* crash buffer is allowed to be empty, that is actually healthy! */
-      EXPECT_LE((strcmp("crash", name)) != 0,
-                android_logger_get_log_readable_size(logger));
+      // crash buffer is allowed to be empty, that is actually healthy!
+      // kernel buffer is allowed to be empty on "user" builds
+      EXPECT_LE(  // boolean 1 or 0 depending on expected content or empty
+          !!((strcmp("crash", name) != 0) &&
+             ((strcmp("kernel", name) != 0) || __android_log_is_debuggable())),
+          android_logger_get_log_readable_size(logger));
     } else {
       EXPECT_NE(0, get_log_size);
       if (get_log_size < 0) {
diff --git a/libmemunreachable/Allocator.cpp b/libmemunreachable/Allocator.cpp
index 6fe67a4..213be17 100644
--- a/libmemunreachable/Allocator.cpp
+++ b/libmemunreachable/Allocator.cpp
@@ -33,9 +33,11 @@
 
 #include "android-base/macros.h"
 
-#include "anon_vma_naming.h"
 #include "Allocator.h"
 #include "LinkedList.h"
+#include "anon_vma_naming.h"
+
+namespace android {
 
 // runtime interfaces used:
 // abort
@@ -57,10 +59,9 @@
 static constexpr size_t kUsableChunkSize = kChunkSize - kPageSize;
 static constexpr size_t kMaxBucketAllocationSize = kChunkSize / 4;
 static constexpr size_t kMinBucketAllocationSize = 8;
-static constexpr unsigned int kNumBuckets = const_log2(kMaxBucketAllocationSize)
-    - const_log2(kMinBucketAllocationSize) + 1;
-static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize
-    / kPageSize;
+static constexpr unsigned int kNumBuckets =
+    const_log2(kMaxBucketAllocationSize) - const_log2(kMinBucketAllocationSize) + 1;
+static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize / kPageSize;
 
 std::atomic<int> heap_count;
 
@@ -93,7 +94,7 @@
   void FreeLocked(void* ptr);
 
   struct MapAllocation {
-    void *ptr;
+    void* ptr;
     size_t size;
     MapAllocation* next;
   };
@@ -107,8 +108,7 @@
 }
 
 static inline unsigned int size_to_bucket(size_t size) {
-  if (size < kMinBucketAllocationSize)
-    return kMinBucketAllocationSize;
+  if (size < kMinBucketAllocationSize) return kMinBucketAllocationSize;
   return log2(size - 1) + 1 - const_log2(kMinBucketAllocationSize);
 }
 
@@ -140,8 +140,7 @@
 
   // Trim beginning
   if (aligned_ptr != ptr) {
-    ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr)
-        - reinterpret_cast<uintptr_t>(ptr);
+    ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr) - reinterpret_cast<uintptr_t>(ptr);
     munmap(ptr, extra);
     map_size -= extra;
     ptr = aligned_ptr;
@@ -151,14 +150,13 @@
   if (map_size != size) {
     assert(map_size > size);
     assert(ptr != NULL);
-    munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size),
-        map_size - size);
+    munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size), map_size - size);
   }
 
-#define PR_SET_VMA   0x53564d41
-#define PR_SET_VMA_ANON_NAME    0
-  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME,
-      reinterpret_cast<uintptr_t>(ptr), size, "leak_detector_malloc");
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
+  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(ptr), size,
+        "leak_detector_malloc");
 
   return ptr;
 }
@@ -170,36 +168,31 @@
   Chunk(HeapImpl* heap, int bucket);
   ~Chunk() {}
 
-  void *Alloc();
+  void* Alloc();
   void Free(void* ptr);
   void Purge();
   bool Empty();
 
   static Chunk* ptr_to_chunk(void* ptr) {
-    return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr)
-        & ~(kChunkSize - 1));
+    return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr) & ~(kChunkSize - 1));
   }
   static bool is_chunk(void* ptr) {
     return (reinterpret_cast<uintptr_t>(ptr) & (kChunkSize - 1)) != 0;
   }
 
-  unsigned int free_count() {
-    return free_count_;
-  }
-  HeapImpl* heap() {
-    return heap_;
-  }
-  LinkedList<Chunk*> node_; // linked list sorted by minimum free count
+  unsigned int free_count() { return free_count_; }
+  HeapImpl* heap() { return heap_; }
+  LinkedList<Chunk*> node_;  // linked list sorted by minimum free count
 
  private:
   DISALLOW_COPY_AND_ASSIGN(Chunk);
   HeapImpl* heap_;
   unsigned int bucket_;
-  unsigned int allocation_size_; // size of allocations in chunk, min 8 bytes
-  unsigned int max_allocations_; // maximum number of allocations in the chunk
-  unsigned int first_free_bitmap_; // index into bitmap for first non-full entry
-  unsigned int free_count_; // number of available allocations
-  unsigned int frees_since_purge_; // number of calls to Free since last Purge
+  unsigned int allocation_size_;    // size of allocations in chunk, min 8 bytes
+  unsigned int max_allocations_;    // maximum number of allocations in the chunk
+  unsigned int first_free_bitmap_;  // index into bitmap for first non-full entry
+  unsigned int free_count_;         // number of available allocations
+  unsigned int frees_since_purge_;  // number of calls to Free since last Purge
 
   // bitmap of pages that have been dirtied
   uint32_t dirty_pages_[div_round_up(kUsablePagesPerChunk, 32)];
@@ -210,13 +203,10 @@
   char data_[0];
 
   unsigned int ptr_to_n(void* ptr) {
-    ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr)
-        - reinterpret_cast<uintptr_t>(data_);
+    ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(data_);
     return offset / allocation_size_;
   }
-  void* n_to_ptr(unsigned int n) {
-    return data_ + n * allocation_size_;
-  }
+  void* n_to_ptr(unsigned int n) { return data_ + n * allocation_size_; }
 };
 static_assert(sizeof(Chunk) <= kPageSize, "header must fit in page");
 
@@ -225,23 +215,27 @@
   assert(count == sizeof(Chunk));
   void* mem = MapAligned(kChunkSize, kChunkSize);
   if (!mem) {
-    abort(); //throw std::bad_alloc;
+    abort();  // throw std::bad_alloc;
   }
 
   return mem;
 }
 
 // Override new operator on chunk to use mmap to allocate kChunkSize
-void Chunk::operator delete(void *ptr) {
+void Chunk::operator delete(void* ptr) {
   assert(reinterpret_cast<Chunk*>(ptr) == ptr_to_chunk(ptr));
   munmap(ptr, kChunkSize);
 }
 
-Chunk::Chunk(HeapImpl* heap, int bucket) :
-    node_(this), heap_(heap), bucket_(bucket), allocation_size_(
-        bucket_to_size(bucket)), max_allocations_(
-        kUsableChunkSize / allocation_size_), first_free_bitmap_(0), free_count_(
-        max_allocations_), frees_since_purge_(0) {
+Chunk::Chunk(HeapImpl* heap, int bucket)
+    : node_(this),
+      heap_(heap),
+      bucket_(bucket),
+      allocation_size_(bucket_to_size(bucket)),
+      max_allocations_(kUsableChunkSize / allocation_size_),
+      first_free_bitmap_(0),
+      free_count_(max_allocations_),
+      frees_since_purge_(0) {
   memset(dirty_pages_, 0, sizeof(dirty_pages_));
   memset(free_bitmap_, 0xff, sizeof(free_bitmap_));
 }
@@ -254,8 +248,7 @@
   assert(free_count_ > 0);
 
   unsigned int i = first_free_bitmap_;
-  while (free_bitmap_[i] == 0)
-    i++;
+  while (free_bitmap_[i] == 0) i++;
   assert(i < arraysize(free_bitmap_));
   unsigned int bit = __builtin_ffs(free_bitmap_[i]) - 1;
   assert(free_bitmap_[i] & (1U << bit));
@@ -306,38 +299,35 @@
 void Chunk::Purge() {
   frees_since_purge_ = 0;
 
-  //unsigned int allocsPerPage = kPageSize / allocation_size_;
+  // unsigned int allocsPerPage = kPageSize / allocation_size_;
 }
 
 // Override new operator on HeapImpl to use mmap to allocate a page
-void* HeapImpl::operator new(std::size_t count __attribute__((unused)))
-    noexcept {
+void* HeapImpl::operator new(std::size_t count __attribute__((unused))) noexcept {
   assert(count == sizeof(HeapImpl));
   void* mem = MapAligned(kPageSize, kPageSize);
   if (!mem) {
-    abort(); //throw std::bad_alloc;
+    abort();  // throw std::bad_alloc;
   }
 
   heap_count++;
   return mem;
 }
 
-void HeapImpl::operator delete(void *ptr) {
+void HeapImpl::operator delete(void* ptr) {
   munmap(ptr, kPageSize);
 }
 
-HeapImpl::HeapImpl() :
-    free_chunks_(), full_chunks_(), map_allocation_list_(NULL) {
-}
+HeapImpl::HeapImpl() : free_chunks_(), full_chunks_(), map_allocation_list_(NULL) {}
 
 bool HeapImpl::Empty() {
   for (unsigned int i = 0; i < kNumBuckets; i++) {
-    for (LinkedList<Chunk*> *it = free_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+    for (LinkedList<Chunk*>* it = free_chunks_[i].next(); it->data() != NULL; it = it->next()) {
       if (!it->data()->Empty()) {
         return false;
       }
     }
-    for (LinkedList<Chunk*> *it = full_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+    for (LinkedList<Chunk*>* it = full_chunks_[i].next(); it->data() != NULL; it = it->next()) {
       if (!it->data()->Empty()) {
         return false;
       }
@@ -350,12 +340,12 @@
 HeapImpl::~HeapImpl() {
   for (unsigned int i = 0; i < kNumBuckets; i++) {
     while (!free_chunks_[i].empty()) {
-      Chunk *chunk = free_chunks_[i].next()->data();
+      Chunk* chunk = free_chunks_[i].next()->data();
       chunk->node_.remove();
       delete chunk;
     }
     while (!full_chunks_[i].empty()) {
-      Chunk *chunk = full_chunks_[i].next()->data();
+      Chunk* chunk = full_chunks_[i].next()->data();
       chunk->node_.remove();
       delete chunk;
     }
@@ -373,18 +363,18 @@
   }
   int bucket = size_to_bucket(size);
   if (free_chunks_[bucket].empty()) {
-    Chunk *chunk = new Chunk(this, bucket);
+    Chunk* chunk = new Chunk(this, bucket);
     free_chunks_[bucket].insert(chunk->node_);
   }
   return free_chunks_[bucket].next()->data()->Alloc();
 }
 
-void HeapImpl::Free(void *ptr) {
+void HeapImpl::Free(void* ptr) {
   std::lock_guard<std::mutex> lk(m_);
   FreeLocked(ptr);
 }
 
-void HeapImpl::FreeLocked(void *ptr) {
+void HeapImpl::FreeLocked(void* ptr) {
   if (!Chunk::is_chunk(ptr)) {
     HeapImpl::MapFree(ptr);
   } else {
@@ -397,12 +387,11 @@
 void* HeapImpl::MapAlloc(size_t size) {
   size = (size + kPageSize - 1) & ~(kPageSize - 1);
 
-  MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked(
-      sizeof(MapAllocation)));
+  MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked(sizeof(MapAllocation)));
   void* ptr = MapAligned(size, kChunkSize);
   if (!ptr) {
     FreeLocked(allocation);
-    abort(); //throw std::bad_alloc;
+    abort();  // throw std::bad_alloc;
   }
   allocation->ptr = ptr;
   allocation->size = size;
@@ -412,10 +401,9 @@
   return ptr;
 }
 
-void HeapImpl::MapFree(void *ptr) {
-  MapAllocation **allocation = &map_allocation_list_;
-  while (*allocation && (*allocation)->ptr != ptr)
-    allocation = &(*allocation)->next;
+void HeapImpl::MapFree(void* ptr) {
+  MapAllocation** allocation = &map_allocation_list_;
+  while (*allocation && (*allocation)->ptr != ptr) allocation = &(*allocation)->next;
 
   assert(*allocation != nullptr);
 
@@ -425,22 +413,22 @@
   *allocation = (*allocation)->next;
 }
 
-void HeapImpl::MoveToFreeList(Chunk *chunk, int bucket) {
+void HeapImpl::MoveToFreeList(Chunk* chunk, int bucket) {
   MoveToList(chunk, &free_chunks_[bucket]);
 }
 
-void HeapImpl::MoveToFullList(Chunk *chunk, int bucket) {
+void HeapImpl::MoveToFullList(Chunk* chunk, int bucket) {
   MoveToList(chunk, &full_chunks_[bucket]);
 }
 
-void HeapImpl::MoveToList(Chunk *chunk, LinkedList<Chunk*>* head) {
+void HeapImpl::MoveToList(Chunk* chunk, LinkedList<Chunk*>* head) {
   // Remove from old list
   chunk->node_.remove();
 
-  LinkedList<Chunk*> *node = head;
+  LinkedList<Chunk*>* node = head;
   // Insert into new list, sorted by lowest free count
-  while (node->next() != head && node->data() != nullptr
-      && node->data()->free_count() < chunk->free_count())
+  while (node->next() != head && node->data() != nullptr &&
+         node->data()->free_count() < chunk->free_count())
     node = node->next();
 
   node->insert(chunk->node_);
@@ -469,10 +457,12 @@
   impl_->Free(ptr);
 }
 
-void Heap::deallocate(HeapImpl*impl, void* ptr) {
+void Heap::deallocate(HeapImpl* impl, void* ptr) {
   impl->Free(ptr);
 }
 
 bool Heap::empty() {
   return impl_->Empty();
 }
+
+}  // namespace android
diff --git a/libmemunreachable/Allocator.h b/libmemunreachable/Allocator.h
index 5390739..837a12b 100644
--- a/libmemunreachable/Allocator.h
+++ b/libmemunreachable/Allocator.h
@@ -27,18 +27,20 @@
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
+
+namespace android {
+
 extern std::atomic<int> heap_count;
 
 class HeapImpl;
 
-template<typename T>
+template <typename T>
 class Allocator;
 
-
 // Non-templated class that implements wraps HeapImpl to keep
 // implementation out of the header file
 class Heap {
-public:
+ public:
   Heap();
   ~Heap();
 
@@ -59,110 +61,99 @@
   static void deallocate(HeapImpl* impl, void* ptr);
 
   // Allocate a class of type T
-  template<class T>
+  template <class T>
   T* allocate() {
     return reinterpret_cast<T*>(allocate(sizeof(T)));
   }
 
   // Comparators, copied objects will be equal
-  bool operator ==(const Heap& other) const {
-    return impl_ == other.impl_;
-  }
-  bool operator !=(const Heap& other) const {
-    return !(*this == other);
-  }
+  bool operator==(const Heap& other) const { return impl_ == other.impl_; }
+  bool operator!=(const Heap& other) const { return !(*this == other); }
 
   // std::unique_ptr wrapper that allocates using allocate and deletes using
   // deallocate
-  template<class T>
+  template <class T>
   using unique_ptr = std::unique_ptr<T, std::function<void(void*)>>;
 
-  template<class T, class... Args>
+  template <class T, class... Args>
   unique_ptr<T> make_unique(Args&&... args) {
     HeapImpl* impl = impl_;
-    return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...),
-        [impl](void* ptr) {
-          reinterpret_cast<T*>(ptr)->~T();
-          deallocate(impl, ptr);
-        });
+    return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...), [impl](void* ptr) {
+      reinterpret_cast<T*>(ptr)->~T();
+      deallocate(impl, ptr);
+    });
   }
 
   // std::unique_ptr wrapper that allocates using allocate and deletes using
   // deallocate
-  template<class T>
+  template <class T>
   using shared_ptr = std::shared_ptr<T>;
 
-  template<class T, class... Args>
+  template <class T, class... Args>
   shared_ptr<T> make_shared(Args&&... args);
 
-protected:
+ protected:
   HeapImpl* impl_;
   bool owns_impl_;
 };
 
 // STLAllocator implements the std allocator interface on top of a Heap
-template<typename T>
+template <typename T>
 class STLAllocator {
-public:
+ public:
   using value_type = T;
-  ~STLAllocator() {
-  }
+  ~STLAllocator() {}
 
   // Construct an STLAllocator on top of a Heap
-  STLAllocator(const Heap& heap) :  // NOLINT, implicit
-      heap_(heap) {
-  }
+  STLAllocator(const Heap& heap)
+      :  // NOLINT, implicit
+        heap_(heap) {}
 
   // Rebind an STLAllocator from an another STLAllocator
-  template<typename U>
-  STLAllocator(const STLAllocator<U>& other) :  // NOLINT, implicit
-      heap_(other.heap_) {
-  }
+  template <typename U>
+  STLAllocator(const STLAllocator<U>& other)
+      :  // NOLINT, implicit
+        heap_(other.heap_) {}
 
   STLAllocator(const STLAllocator&) = default;
   STLAllocator<T>& operator=(const STLAllocator<T>&) = default;
 
-  T* allocate(std::size_t n) {
-    return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T)));
-  }
+  T* allocate(std::size_t n) { return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T))); }
 
-  void deallocate(T* ptr, std::size_t) {
-    heap_.deallocate(ptr);
-  }
+  void deallocate(T* ptr, std::size_t) { heap_.deallocate(ptr); }
 
-  template<typename U>
-  bool operator ==(const STLAllocator<U>& other) const {
+  template <typename U>
+  bool operator==(const STLAllocator<U>& other) const {
     return heap_ == other.heap_;
   }
-  template<typename U>
-  inline bool operator !=(const STLAllocator<U>& other) const {
+  template <typename U>
+  inline bool operator!=(const STLAllocator<U>& other) const {
     return !(this == other);
   }
 
-  template<typename U>
+  template <typename U>
   friend class STLAllocator;
 
-protected:
+ protected:
   Heap heap_;
 };
 
-
 // Allocator extends STLAllocator with some convenience methods for allocating
 // a single object and for constructing unique_ptr and shared_ptr objects with
 // appropriate deleters.
-template<class T>
+template <class T>
 class Allocator : public STLAllocator<T> {
  public:
   ~Allocator() {}
 
-  Allocator(const Heap& other) : // NOLINT, implicit
-      STLAllocator<T>(other) {
-  }
+  Allocator(const Heap& other)
+      :  // NOLINT, implicit
+        STLAllocator<T>(other) {}
 
-  template<typename U>
-  Allocator(const STLAllocator<U>& other) :  // NOLINT, implicit
-      STLAllocator<T>(other) {
-  }
+  template <typename U>
+  Allocator(const STLAllocator<U>& other)
+      :  // NOLINT, implicit
+        STLAllocator<T>(other) {}
 
   Allocator(const Allocator&) = default;
   Allocator<T>& operator=(const Allocator<T>&) = default;
@@ -171,24 +162,20 @@
   using STLAllocator<T>::deallocate;
   using STLAllocator<T>::heap_;
 
-  T* allocate() {
-    return STLAllocator<T>::allocate(1);
-  }
-  void deallocate(void* ptr) {
-    heap_.deallocate(ptr);
-  }
+  T* allocate() { return STLAllocator<T>::allocate(1); }
+  void deallocate(void* ptr) { heap_.deallocate(ptr); }
 
   using shared_ptr = Heap::shared_ptr<T>;
 
-  template<class... Args>
-  shared_ptr make_shared(Args&& ...args) {
+  template <class... Args>
+  shared_ptr make_shared(Args&&... args) {
     return heap_.template make_shared<T>(std::forward<Args>(args)...);
   }
 
   using unique_ptr = Heap::unique_ptr<T>;
 
-  template<class... Args>
-  unique_ptr make_unique(Args&& ...args) {
+  template <class... Args>
+  unique_ptr make_unique(Args&&... args) {
     return heap_.template make_unique<T>(std::forward<Args>(args)...);
   }
 };
@@ -196,33 +183,36 @@
 // std::unique_ptr wrapper that allocates using allocate and deletes using
 // deallocate.  Implemented outside class definition in order to pass
 // Allocator<T> to shared_ptr.
-template<class T, class... Args>
+template <class T, class... Args>
 inline Heap::shared_ptr<T> Heap::make_shared(Args&&... args) {
   return std::allocate_shared<T, Allocator<T>, Args...>(Allocator<T>(*this),
-      std::forward<Args>(args)...);
+                                                        std::forward<Args>(args)...);
 }
 
 namespace allocator {
 
-template<class T>
+template <class T>
 using vector = std::vector<T, Allocator<T>>;
 
-template<class T>
+template <class T>
 using list = std::list<T, Allocator<T>>;
 
-template<class Key, class T, class Compare = std::less<Key>>
+template <class Key, class T, class Compare = std::less<Key>>
 using map = std::map<Key, T, Compare, Allocator<std::pair<const Key, T>>>;
 
-template<class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
-using unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>;
+template <class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+using unordered_map =
+    std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>;
 
-template<class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+template <class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
 using unordered_set = std::unordered_set<Key, Hash, KeyEqual, Allocator<Key>>;
 
-template<class Key, class Compare = std::less<Key>>
+template <class Key, class Compare = std::less<Key>>
 using set = std::set<Key, Compare, Allocator<Key>>;
 
 using string = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
 }
 
+}  // namespace android
+
 #endif
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index cdac76b..826a576 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -26,6 +26,7 @@
     defaults: ["libmemunreachable_defaults"],
     srcs: [
         "Allocator.cpp",
+        "Binder.cpp",
         "HeapWalker.cpp",
         "LeakFolding.cpp",
         "LeakPipe.cpp",
@@ -84,3 +85,18 @@
         },
     },
 }
+
+cc_test {
+    name: "memunreachable_binder_test",
+    defaults: ["libmemunreachable_defaults"],
+    srcs: [
+        "tests/Binder_test.cpp",
+        "tests/MemUnreachable_test.cpp",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libhwbinder",
+        "libmemunreachable",
+        "libutils",
+    ],
+}
diff --git a/libmemunreachable/Binder.cpp b/libmemunreachable/Binder.cpp
new file mode 100644
index 0000000..60512a3
--- /dev/null
+++ b/libmemunreachable/Binder.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 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 <sys/cdefs.h>
+#include <unistd.h>
+
+#include <functional>
+
+#include "Binder.h"
+#include "log.h"
+
+__BEGIN_DECLS
+
+// Weak undefined references to the symbols in libbinder and libhwbinder
+// so that libmemunreachable can call them in processes that have them
+// loaded without requiring libmemunreachable to have dependencies on them.
+ssize_t __attribute__((weak)) getBinderKernelReferences(size_t, uintptr_t*);
+ssize_t __attribute__((weak)) getHWBinderKernelReferences(size_t, uintptr_t*);
+
+__END_DECLS
+
+namespace android {
+
+static bool BinderReferencesToVector(allocator::vector<uintptr_t>& refs,
+                                     std::function<ssize_t(size_t, uintptr_t*)> fn) {
+  if (fn == nullptr) {
+    return true;
+  }
+
+  size_t size = refs.size();
+
+  do {
+    refs.resize(size);
+
+    ssize_t ret = fn(refs.size(), refs.data());
+    if (ret < 0) {
+      return false;
+    }
+
+    size = ret;
+  } while (size > refs.size());
+
+  refs.resize(size);
+  return true;
+}
+
+bool BinderReferences(allocator::vector<uintptr_t>& refs) {
+  refs.clear();
+
+  allocator::vector<uintptr_t> binder_refs{refs.get_allocator()};
+  if (BinderReferencesToVector(refs, getBinderKernelReferences)) {
+    refs.insert(refs.end(), binder_refs.begin(), binder_refs.end());
+  } else {
+    MEM_ALOGE("getBinderKernelReferences failed");
+  }
+
+  allocator::vector<uintptr_t> hwbinder_refs{refs.get_allocator()};
+  if (BinderReferencesToVector(hwbinder_refs, getHWBinderKernelReferences)) {
+    refs.insert(refs.end(), hwbinder_refs.begin(), hwbinder_refs.end());
+  } else {
+    MEM_ALOGE("getHWBinderKernelReferences failed");
+  }
+
+  return true;
+}
+
+}  // namespace android
diff --git a/libunwindstack/Log.h b/libmemunreachable/Binder.h
similarity index 65%
copy from libunwindstack/Log.h
copy to libmemunreachable/Binder.h
index 2d01aa8..bf4fd3e 100644
--- a/libunwindstack/Log.h
+++ b/libmemunreachable/Binder.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_LOG_H
-#define _LIBUNWINDSTACK_LOG_H
+#ifndef LIBMEMUNREACHABLE_BINDER_H_
+#define LIBMEMUNREACHABLE_BINDER_H_
 
-#include <stdint.h>
+#include "Allocator.h"
 
-void log_to_stdout(bool enable);
-void log(uint8_t indent, const char* format, ...);
+namespace android {
 
-#endif  // _LIBUNWINDSTACK_LOG_H
+bool BinderReferences(allocator::vector<uintptr_t>& refs);
+
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_BINDER_H_
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index c365ae5..2403ad0 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -28,6 +28,8 @@
 #include "ScopedSignalHandler.h"
 #include "log.h"
 
+namespace android {
+
 bool HeapWalker::Allocation(uintptr_t begin, uintptr_t end) {
   if (end == begin) {
     end = begin + 1;
@@ -114,8 +116,8 @@
   return true;
 }
 
-bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit,
-    size_t* num_leaks_out, size_t* leak_bytes_out) {
+bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit, size_t* num_leaks_out,
+                        size_t* leak_bytes_out) {
   leaked.clear();
 
   size_t num_leaks = 0;
@@ -148,9 +150,9 @@
 
 static bool MapOverPage(void* addr) {
   const size_t page_size = sysconf(_SC_PAGE_SIZE);
-  void *page = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & ~(page_size-1));
+  void* page = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & ~(page_size - 1));
 
-  void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
+  void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
   if (ret == MAP_FAILED) {
     MEM_ALOGE("failed to map page at %p: %s", page, strerror(errno));
     return false;
@@ -159,7 +161,8 @@
   return true;
 }
 
-void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si, void* /*uctx*/) {
+void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si,
+                                void* /*uctx*/) {
   uintptr_t addr = reinterpret_cast<uintptr_t>(si->si_addr);
   if (addr != walking_ptr_) {
     handler.reset();
@@ -172,3 +175,5 @@
 }
 
 ScopedSignalHandler::SignalFn ScopedSignalHandler::handler_;
+
+}  // namespace android
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index b25696f..5c7ec13 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -25,6 +25,8 @@
 #include "ScopedSignalHandler.h"
 #include "Tarjan.h"
 
+namespace android {
+
 // A range [begin, end)
 struct Range {
   uintptr_t begin;
@@ -34,31 +36,31 @@
   bool operator==(const Range& other) const {
     return this->begin == other.begin && this->end == other.end;
   }
-  bool operator!=(const Range& other) const {
-    return !(*this == other);
-  }
+  bool operator!=(const Range& other) const { return !(*this == other); }
 };
 
 // Comparator for Ranges that returns equivalence for overlapping ranges
 struct compare_range {
-  bool operator()(const Range& a, const Range& b) const {
-    return a.end <= b.begin;
-  }
+  bool operator()(const Range& a, const Range& b) const { return a.end <= b.begin; }
 };
 
 class HeapWalker {
  public:
-  explicit HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator),
-    allocations_(allocator), allocation_bytes_(0),
-	roots_(allocator), root_vals_(allocator),
-	segv_handler_(allocator), walking_ptr_(0) {
+  explicit HeapWalker(Allocator<HeapWalker> allocator)
+      : allocator_(allocator),
+        allocations_(allocator),
+        allocation_bytes_(0),
+        roots_(allocator),
+        root_vals_(allocator),
+        segv_handler_(allocator),
+        walking_ptr_(0) {
     valid_allocations_range_.end = 0;
     valid_allocations_range_.begin = ~valid_allocations_range_.end;
 
-    segv_handler_.install(SIGSEGV,
-        [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
+    segv_handler_.install(
+        SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
           this->HandleSegFault(handler, signal, siginfo, uctx);
-      });
+        });
   }
 
   ~HeapWalker() {}
@@ -68,15 +70,14 @@
 
   bool DetectLeaks();
 
-  bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks,
-      size_t* leak_bytes);
+  bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks, size_t* leak_bytes);
   size_t Allocations();
   size_t AllocationBytes();
 
-  template<class F>
+  template <class F>
   void ForEachPtrInRange(const Range& range, F&& f);
 
-  template<class F>
+  template <class F>
   void ForEachAllocation(F&& f);
 
   struct AllocationInfo {
@@ -84,7 +85,6 @@
   };
 
  private:
-
   void RecurseRoot(const Range& root);
   bool WordContainsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info);
   void HandleSegFault(ScopedSignalHandler&, int, siginfo_t*, void*);
@@ -103,7 +103,7 @@
   uintptr_t walking_ptr_;
 };
 
-template<class F>
+template <class F>
 inline void HeapWalker::ForEachPtrInRange(const Range& range, F&& f) {
   uintptr_t begin = (range.begin + (sizeof(uintptr_t) - 1)) & ~(sizeof(uintptr_t) - 1);
   // TODO(ccross): we might need to consider a pointer to the end of a buffer
@@ -118,7 +118,7 @@
   }
 }
 
-template<class F>
+template <class F>
 inline void HeapWalker::ForEachAllocation(F&& f) {
   for (auto& it : allocations_) {
     const Range& range = it.first;
@@ -127,4 +127,6 @@
   }
 }
 
+}  // namespace android
+
 #endif
diff --git a/libmemunreachable/Leak.h b/libmemunreachable/Leak.h
index eaeeea7..de64b64 100644
--- a/libmemunreachable/Leak.h
+++ b/libmemunreachable/Leak.h
@@ -26,9 +26,9 @@
 // as a key in std::unordered_map.
 namespace std {
 
-template<>
-struct hash<Leak::Backtrace> {
-  std::size_t operator()(const Leak::Backtrace& key) const {
+template <>
+struct hash<android::Leak::Backtrace> {
+  std::size_t operator()(const android::Leak::Backtrace& key) const {
     std::size_t seed = 0;
 
     hash_combine(seed, key.num_frames);
@@ -40,7 +40,7 @@
   }
 
  private:
-  template<typename T>
+  template <typename T>
   inline void hash_combine(std::size_t& seed, const T& v) const {
     std::hash<T> hasher;
     seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
@@ -49,9 +49,12 @@
 
 }  // namespace std
 
+namespace android {
+
 static bool operator==(const Leak::Backtrace& lhs, const Leak::Backtrace& rhs) {
   return (lhs.num_frames == rhs.num_frames) &&
-      memcmp(lhs.frames, rhs.frames, lhs.num_frames * sizeof(lhs.frames[0])) == 0;
+         memcmp(lhs.frames, rhs.frames, lhs.num_frames * sizeof(lhs.frames[0])) == 0;
+}
 }
 
 #endif
diff --git a/libmemunreachable/LeakFolding.cpp b/libmemunreachable/LeakFolding.cpp
index be4d20c..69f320c 100644
--- a/libmemunreachable/LeakFolding.cpp
+++ b/libmemunreachable/LeakFolding.cpp
@@ -22,6 +22,8 @@
 #include "Tarjan.h"
 #include "log.h"
 
+namespace android {
+
 // Converts possibly cyclic graph of leaks to a DAG by combining
 // strongly-connected components into a object, stored in the scc pointer
 // of each node in the component.
@@ -31,11 +33,11 @@
 
   Allocator<SCCInfo> scc_allocator = allocator_;
 
-  for (auto& scc_nodes: scc_list) {
+  for (auto& scc_nodes : scc_list) {
     Allocator<SCCInfo>::unique_ptr leak_scc;
     leak_scc = scc_allocator.make_unique(scc_allocator);
 
-    for (auto& node: scc_nodes) {
+    for (auto& node : scc_nodes) {
       node->ptr->scc = leak_scc.get();
       leak_scc->count++;
       leak_scc->size += node->ptr->range.size();
@@ -46,7 +48,7 @@
 
   for (auto& it : leak_map_) {
     LeakInfo& leak = it.second;
-    for (auto& ref: leak.node.references_out) {
+    for (auto& ref : leak.node.references_out) {
       if (leak.scc != ref->ptr->scc) {
         leak.scc->node.Edge(&ref->ptr->scc->node);
       }
@@ -55,17 +57,14 @@
 }
 
 void LeakFolding::AccumulateLeaks(SCCInfo* dominator) {
-  std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_,
-      [&](SCCInfo* scc) {
-        if (scc->accumulator != dominator) {
-          scc->accumulator = dominator;
-          dominator->cuumulative_size += scc->size;
-          dominator->cuumulative_count += scc->count;
-          scc->node.Foreach([&](SCCInfo* ref) {
-            walk(ref);
-          });
-        }
-      });
+  std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_, [&](SCCInfo* scc) {
+    if (scc->accumulator != dominator) {
+      scc->accumulator = dominator;
+      dominator->cuumulative_size += scc->size;
+      dominator->cuumulative_count += scc->count;
+      scc->node.Foreach([&](SCCInfo* ref) { walk(ref); });
+    }
+  });
   walk(dominator);
 }
 
@@ -73,27 +72,25 @@
   Allocator<LeakInfo> leak_allocator = allocator_;
 
   // Find all leaked allocations insert them into leak_map_ and leak_graph_
-  heap_walker_.ForEachAllocation(
-      [&](const Range& range, HeapWalker::AllocationInfo& allocation) {
-        if (!allocation.referenced_from_root) {
-          auto it = leak_map_.emplace(std::piecewise_construct,
-              std::forward_as_tuple(range),
-              std::forward_as_tuple(range, allocator_));
-          LeakInfo& leak = it.first->second;
-          leak_graph_.push_back(&leak.node);
-        }
-      });
+  heap_walker_.ForEachAllocation([&](const Range& range, HeapWalker::AllocationInfo& allocation) {
+    if (!allocation.referenced_from_root) {
+      auto it = leak_map_.emplace(std::piecewise_construct, std::forward_as_tuple(range),
+                                  std::forward_as_tuple(range, allocator_));
+      LeakInfo& leak = it.first->second;
+      leak_graph_.push_back(&leak.node);
+    }
+  });
 
   // Find references between leaked allocations and connect them in leak_graph_
   for (auto& it : leak_map_) {
     LeakInfo& leak = it.second;
     heap_walker_.ForEachPtrInRange(leak.range,
-        [&](Range& ptr_range, HeapWalker::AllocationInfo* ptr_info) {
-          if (!ptr_info->referenced_from_root) {
-            LeakInfo* ptr_leak = &leak_map_.at(ptr_range);
-            leak.node.Edge(&ptr_leak->node);
-          }
-        });
+                                   [&](Range& ptr_range, HeapWalker::AllocationInfo* ptr_info) {
+                                     if (!ptr_info->referenced_from_root) {
+                                       LeakInfo* ptr_leak = &leak_map_.at(ptr_range);
+                                       leak.node.Edge(&ptr_leak->node);
+                                     }
+                                   });
   }
 
   // Convert the cyclic graph to a DAG by grouping strongly connected components
@@ -110,8 +107,8 @@
   return true;
 }
 
-bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked,
-    size_t* num_leaks_out, size_t* leak_bytes_out) {
+bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked, size_t* num_leaks_out,
+                         size_t* leak_bytes_out) {
   size_t num_leaks = 0;
   size_t leak_bytes = 0;
   for (auto& it : leak_map_) {
@@ -123,9 +120,8 @@
   for (auto& it : leak_map_) {
     const LeakInfo& leak = it.second;
     if (leak.scc->dominator) {
-      leaked.emplace_back(Leak{leak.range,
-        leak.scc->cuumulative_count - 1,
-        leak.scc->cuumulative_size - leak.range.size()});
+      leaked.emplace_back(Leak{leak.range, leak.scc->cuumulative_count - 1,
+                               leak.scc->cuumulative_size - leak.range.size()});
     }
   }
 
@@ -138,3 +134,5 @@
 
   return true;
 }
+
+}  // namespace android
diff --git a/libmemunreachable/LeakFolding.h b/libmemunreachable/LeakFolding.h
index 9c6a525..09affac 100644
--- a/libmemunreachable/LeakFolding.h
+++ b/libmemunreachable/LeakFolding.h
@@ -19,11 +19,16 @@
 
 #include "HeapWalker.h"
 
+namespace android {
+
 class LeakFolding {
  public:
   LeakFolding(Allocator<void> allocator, HeapWalker& heap_walker)
-   : allocator_(allocator), heap_walker_(heap_walker),
-     leak_map_(allocator), leak_graph_(allocator), leak_scc_(allocator) {}
+      : allocator_(allocator),
+        heap_walker_(heap_walker),
+        leak_map_(allocator),
+        leak_graph_(allocator),
+        leak_scc_(allocator) {}
 
   bool FoldLeaks();
 
@@ -33,8 +38,7 @@
     size_t referenced_size;
   };
 
-  bool Leaked(allocator::vector<Leak>& leaked,
-      size_t* num_leaks_out, size_t* leak_bytes_out);
+  bool Leaked(allocator::vector<Leak>& leaked, size_t* num_leaks_out, size_t* leak_bytes_out);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(LeakFolding);
@@ -54,9 +58,15 @@
     bool dominator;
     SCCInfo* accumulator;
 
-    explicit SCCInfo(Allocator<SCCInfo> allocator) : node(this, allocator),
-        count(0), size(0), cuumulative_count(0), cuumulative_size(0),
-        dominator(false), accumulator(nullptr) {}
+    explicit SCCInfo(Allocator<SCCInfo> allocator)
+        : node(this, allocator),
+          count(0),
+          size(0),
+          cuumulative_count(0),
+          cuumulative_size(0),
+          dominator(false),
+          accumulator(nullptr) {}
+
    private:
     SCCInfo(SCCInfo&&) = delete;
     DISALLOW_COPY_AND_ASSIGN(SCCInfo);
@@ -71,8 +81,7 @@
     SCCInfo* scc;
 
     LeakInfo(const Range& range, Allocator<LeakInfo> allocator)
-        : node(this, allocator), range(range),
-          scc(nullptr) {}
+        : node(this, allocator), range(range), scc(nullptr) {}
 
    private:
     DISALLOW_COPY_AND_ASSIGN(LeakInfo);
@@ -86,4 +95,6 @@
   allocator::vector<Allocator<SCCInfo>::unique_ptr> leak_scc_;
 };
 
-#endif // LIBMEMUNREACHABLE_LEAK_FOLDING_H_
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_LEAK_FOLDING_H_
diff --git a/libmemunreachable/LeakPipe.cpp b/libmemunreachable/LeakPipe.cpp
index 78117e2..8ea9ad6 100644
--- a/libmemunreachable/LeakPipe.cpp
+++ b/libmemunreachable/LeakPipe.cpp
@@ -21,9 +21,11 @@
 
 #include "log.h"
 
+namespace android {
+
 bool LeakPipe::SendFd(int sock, int fd) {
-  struct msghdr hdr{};
-  struct iovec iov{};
+  struct msghdr hdr {};
+  struct iovec iov {};
   unsigned int data = 0xfdfdfdfd;
   alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
 
@@ -56,8 +58,8 @@
 }
 
 int LeakPipe::ReceiveFd(int sock) {
-  struct msghdr hdr{};
-  struct iovec iov{};
+  struct msghdr hdr {};
+  struct iovec iov {};
   unsigned int data;
   alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
 
@@ -87,3 +89,5 @@
 
   return *(int*)CMSG_DATA(cmsg);
 }
+
+}  // namespace android
diff --git a/libmemunreachable/LeakPipe.h b/libmemunreachable/LeakPipe.h
index 3ea2d8f..94d4aa4 100644
--- a/libmemunreachable/LeakPipe.h
+++ b/libmemunreachable/LeakPipe.h
@@ -26,6 +26,8 @@
 #include "ScopedPipe.h"
 #include "log.h"
 
+namespace android {
+
 // LeakPipe implements a pipe that can transfer vectors of simple objects
 // between processes.  The pipe is created in the sending process and
 // transferred over a socketpair that was created before forking.  This ensures
@@ -34,15 +36,13 @@
 class LeakPipe {
  public:
   LeakPipe() {
-    int ret = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sv_);
+    int ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv_);
     if (ret < 0) {
       MEM_LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
     }
   }
 
-  ~LeakPipe() {
-    Close();
-  }
+  ~LeakPipe() { Close(); }
 
   void Close() {
     close(sv_[0]);
@@ -77,13 +77,9 @@
    public:
     LeakPipeBase() : fd_(-1) {}
 
-    ~LeakPipeBase() {
-      Close();
-    }
+    ~LeakPipeBase() { Close(); }
 
-    void SetFd(int fd) {
-      fd_ = fd;
-    }
+    void SetFd(int fd) { fd_ = fd; }
 
     void Close() {
       close(fd_);
@@ -101,7 +97,7 @@
    public:
     using LeakPipeBase::LeakPipeBase;
 
-    template<typename T>
+    template <typename T>
     bool Send(const T& value) {
       ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, &value, sizeof(T)));
       if (ret < 0) {
@@ -115,7 +111,7 @@
       return true;
     }
 
-    template<class T, class Alloc = std::allocator<T>>
+    template <class T, class Alloc = std::allocator<T>>
     bool SendVector(const std::vector<T, Alloc>& vector) {
       size_t size = vector.size() * sizeof(T);
       if (!Send(size)) {
@@ -139,7 +135,7 @@
    public:
     using LeakPipeBase::LeakPipeBase;
 
-    template<typename T>
+    template <typename T>
     bool Receive(T* value) {
       ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, reinterpret_cast<void*>(value), sizeof(T)));
       if (ret < 0) {
@@ -153,7 +149,7 @@
       return true;
     }
 
-    template<class T, class Alloc = std::allocator<T>>
+    template <class T, class Alloc = std::allocator<T>>
     bool ReceiveVector(std::vector<T, Alloc>& vector) {
       size_t size = 0;
       if (!Receive(&size)) {
@@ -178,16 +174,11 @@
 
       return true;
     }
-
   };
 
-  LeakPipeReceiver& Receiver() {
-    return receiver_;
-  }
+  LeakPipeReceiver& Receiver() { return receiver_; }
 
-  LeakPipeSender& Sender() {
-    return sender_;
-  }
+  LeakPipeSender& Sender() { return sender_; }
 
  private:
   LeakPipeReceiver receiver_;
@@ -198,4 +189,6 @@
   int sv_[2];
 };
 
-#endif // LIBMEMUNREACHABLE_LEAK_PIPE_H_
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_LEAK_PIPE_H_
diff --git a/libmemunreachable/LineBuffer.cpp b/libmemunreachable/LineBuffer.cpp
index d3580c0..4ea0542 100644
--- a/libmemunreachable/LineBuffer.cpp
+++ b/libmemunreachable/LineBuffer.cpp
@@ -23,8 +23,10 @@
 
 #include "LineBuffer.h"
 
-LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len) : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {
-}
+namespace android {
+
+LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len)
+    : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {}
 
 bool LineBuffer::GetLine(char** line, size_t* line_len) {
   while (true) {
@@ -60,3 +62,5 @@
     bytes_ += bytes;
   }
 }
+
+}  // namespace android
diff --git a/libmemunreachable/LineBuffer.h b/libmemunreachable/LineBuffer.h
index a015c46..cc6cd0c 100644
--- a/libmemunreachable/LineBuffer.h
+++ b/libmemunreachable/LineBuffer.h
@@ -19,6 +19,8 @@
 
 #include <stdint.h>
 
+namespace android {
+
 class LineBuffer {
  public:
   LineBuffer(int fd, char* buffer, size_t buffer_len);
@@ -33,4 +35,6 @@
   size_t bytes_ = 0;
 };
 
-#endif // _LIBMEMUNREACHABLE_LINE_BUFFER_H
+}  // namespace android
+
+#endif  // _LIBMEMUNREACHABLE_LINE_BUFFER_H
diff --git a/libmemunreachable/LinkedList.h b/libmemunreachable/LinkedList.h
index 132842d..36fe9fd 100644
--- a/libmemunreachable/LinkedList.h
+++ b/libmemunreachable/LinkedList.h
@@ -17,44 +17,47 @@
 #ifndef LIBMEMUNREACHABLE_LINKED_LIST_H_
 #define LIBMEMUNREACHABLE_LINKED_LIST_H_
 
-template<class T>
+namespace android {
+
+template <class T>
 class LinkedList {
-public:
-    LinkedList() : next_(this), prev_(this), data_() {}
-    explicit LinkedList(T data) : LinkedList() {
-        data_ = data;
-    }
-    ~LinkedList() {}
-    void insert(LinkedList<T>& node) {
-        assert(node.empty());
-        node.next_ = this->next_;
-        node.next_->prev_ = &node;
-        this->next_ = &node;
-        node.prev_ = this;
-    }
-    void remove() {
-        this->next_->prev_ = this->prev_;
-        this->prev_->next_ = this->next_;
-        this->next_ = this;
-        this->prev_ = this;
-    }
-    T data() { return data_; }
-    bool empty() { return next_ == this && prev_ == this; }
-    LinkedList<T> *next() { return next_; }
-private:
-    LinkedList<T> *next_;
-    LinkedList<T> *prev_;
-    T data_;
+ public:
+  LinkedList() : next_(this), prev_(this), data_() {}
+  explicit LinkedList(T data) : LinkedList() { data_ = data; }
+  ~LinkedList() {}
+  void insert(LinkedList<T>& node) {
+    assert(node.empty());
+    node.next_ = this->next_;
+    node.next_->prev_ = &node;
+    this->next_ = &node;
+    node.prev_ = this;
+  }
+  void remove() {
+    this->next_->prev_ = this->prev_;
+    this->prev_->next_ = this->next_;
+    this->next_ = this;
+    this->prev_ = this;
+  }
+  T data() { return data_; }
+  bool empty() { return next_ == this && prev_ == this; }
+  LinkedList<T>* next() { return next_; }
+
+ private:
+  LinkedList<T>* next_;
+  LinkedList<T>* prev_;
+  T data_;
 };
 
-template<class T>
+template <class T>
 class LinkedListHead {
-public:
-    LinkedListHead() : node_() {}
-    ~LinkedListHead() {}
+ public:
+  LinkedListHead() : node_() {}
+  ~LinkedListHead() {}
 
-private:
-    LinkedList<T> node_;
+ private:
+  LinkedList<T> node_;
 };
 
+}  // namespace android
+
 #endif
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index e7c0beb..5e062fd 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -15,18 +15,20 @@
  */
 
 #include <inttypes.h>
+#include <string.h>
 
 #include <functional>
 #include <iomanip>
 #include <mutex>
-#include <string>
 #include <sstream>
+#include <string>
 #include <unordered_map>
 
-#include <backtrace.h>
 #include <android-base/macros.h>
+#include <backtrace.h>
 
 #include "Allocator.h"
+#include "Binder.h"
 #include "HeapWalker.h"
 #include "Leak.h"
 #include "LeakFolding.h"
@@ -37,30 +39,34 @@
 #include "Semaphore.h"
 #include "ThreadCapture.h"
 
-#include "memunreachable/memunreachable.h"
 #include "bionic.h"
 #include "log.h"
-
-const size_t Leak::contents_length;
+#include "memunreachable/memunreachable.h"
 
 using namespace std::chrono_literals;
 
+namespace android {
+
+const size_t Leak::contents_length;
+
 class MemUnreachable {
  public:
-  MemUnreachable(pid_t pid, Allocator<void> allocator) : pid_(pid), allocator_(allocator),
-      heap_walker_(allocator_) {}
+  MemUnreachable(pid_t pid, Allocator<void> allocator)
+      : pid_(pid), allocator_(allocator), heap_walker_(allocator_) {}
   bool CollectAllocations(const allocator::vector<ThreadInfo>& threads,
-      const allocator::vector<Mapping>& mappings);
-  bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
-      size_t* num_leaks, size_t* leak_bytes);
+                          const allocator::vector<Mapping>& mappings,
+                          const allocator::vector<uintptr_t>& refs);
+  bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit, size_t* num_leaks,
+                            size_t* leak_bytes);
   size_t Allocations() { return heap_walker_.Allocations(); }
   size_t AllocationBytes() { return heap_walker_.AllocationBytes(); }
+
  private:
   bool ClassifyMappings(const allocator::vector<Mapping>& mappings,
-      allocator::vector<Mapping>& heap_mappings,
-      allocator::vector<Mapping>& anon_mappings,
-      allocator::vector<Mapping>& globals_mappings,
-      allocator::vector<Mapping>& stack_mappings);
+                        allocator::vector<Mapping>& heap_mappings,
+                        allocator::vector<Mapping>& anon_mappings,
+                        allocator::vector<Mapping>& globals_mappings,
+                        allocator::vector<Mapping>& stack_mappings);
   DISALLOW_COPY_AND_ASSIGN(MemUnreachable);
   pid_t pid_;
   Allocator<void> allocator_;
@@ -68,16 +74,18 @@
 };
 
 static void HeapIterate(const Mapping& heap_mapping,
-    const std::function<void(uintptr_t, size_t)>& func) {
+                        const std::function<void(uintptr_t, size_t)>& func) {
   malloc_iterate(heap_mapping.begin, heap_mapping.end - heap_mapping.begin,
-      [](uintptr_t base, size_t size, void* arg) {
-    auto f = reinterpret_cast<const std::function<void(uintptr_t, size_t)>*>(arg);
-    (*f)(base, size);
-  }, const_cast<void*>(reinterpret_cast<const void*>(&func)));
+                 [](uintptr_t base, size_t size, void* arg) {
+                   auto f = reinterpret_cast<const std::function<void(uintptr_t, size_t)>*>(arg);
+                   (*f)(base, size);
+                 },
+                 const_cast<void*>(reinterpret_cast<const void*>(&func)));
 }
 
 bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads,
-    const allocator::vector<Mapping>& mappings) {
+                                        const allocator::vector<Mapping>& mappings,
+                                        const allocator::vector<uintptr_t>& refs) {
   MEM_ALOGI("searching process %d for allocations", pid_);
   allocator::vector<Mapping> heap_mappings{mappings};
   allocator::vector<Mapping> anon_mappings{mappings};
@@ -113,13 +121,15 @@
     heap_walker_.Root(thread_it->regs);
   }
 
+  heap_walker_.Root(refs);
+
   MEM_ALOGI("searching done");
 
   return true;
 }
 
-bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks,
-    size_t limit, size_t* num_leaks, size_t* leak_bytes) {
+bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
+                                          size_t* num_leaks, size_t* leak_bytes) {
   MEM_ALOGI("sweeping process %d for unreachable memory", pid_);
   leaks.clear();
 
@@ -127,7 +137,6 @@
     return false;
   }
 
-
   allocator::vector<Range> leaked1{allocator_};
   heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes);
 
@@ -152,12 +161,12 @@
   // in backtrace_map.
   leaks.reserve(leaked.size());
 
-  for (auto& it: leaked) {
+  for (auto& it : leaked) {
     leaks.emplace_back();
     Leak* leak = &leaks.back();
 
-    ssize_t num_backtrace_frames = malloc_backtrace(reinterpret_cast<void*>(it.range.begin),
-        leak->backtrace.frames, leak->backtrace.max_frames);
+    ssize_t num_backtrace_frames = malloc_backtrace(
+        reinterpret_cast<void*>(it.range.begin), leak->backtrace.frames, leak->backtrace.max_frames);
     if (num_backtrace_frames > 0) {
       leak->backtrace.num_frames = num_backtrace_frames;
 
@@ -183,14 +192,13 @@
     leak->referenced_size = it.referenced_size;
     leak->total_size = leak->size + leak->referenced_size;
     memcpy(leak->contents, reinterpret_cast<void*>(it.range.begin),
-        std::min(leak->size, Leak::contents_length));
+           std::min(leak->size, Leak::contents_length));
   }
 
   MEM_ALOGI("folding done");
 
-  std::sort(leaks.begin(), leaks.end(), [](const Leak& a, const Leak& b) {
-    return a.total_size > b.total_size;
-  });
+  std::sort(leaks.begin(), leaks.end(),
+            [](const Leak& a, const Leak& b) { return a.total_size > b.total_size; });
 
   if (leaks.size() > limit) {
     leaks.resize(limit);
@@ -205,11 +213,10 @@
 }
 
 bool MemUnreachable::ClassifyMappings(const allocator::vector<Mapping>& mappings,
-    allocator::vector<Mapping>& heap_mappings,
-    allocator::vector<Mapping>& anon_mappings,
-    allocator::vector<Mapping>& globals_mappings,
-    allocator::vector<Mapping>& stack_mappings)
-{
+                                      allocator::vector<Mapping>& heap_mappings,
+                                      allocator::vector<Mapping>& anon_mappings,
+                                      allocator::vector<Mapping>& globals_mappings,
+                                      allocator::vector<Mapping>& stack_mappings) {
   heap_mappings.clear();
   anon_mappings.clear();
   globals_mappings.clear();
@@ -245,7 +252,8 @@
       stack_mappings.emplace_back(*it);
     } else if (mapping_name.size() == 0) {
       globals_mappings.emplace_back(*it);
-    } else if (has_prefix(mapping_name, "[anon:") && mapping_name != "[anon:leak_detector_malloc]") {
+    } else if (has_prefix(mapping_name, "[anon:") &&
+               mapping_name != "[anon:leak_detector_malloc]") {
       // TODO(ccross): it would be nice to treat named anonymous mappings as
       // possible leaks, but naming something in a .bss or .data section makes
       // it impossible to distinguish them from mmaped and then named mappings.
@@ -256,7 +264,7 @@
   return true;
 }
 
-template<typename T>
+template <typename T>
 static inline const char* plural(T val) {
   return (val == 1) ? "" : "s";
 }
@@ -279,6 +287,7 @@
     ThreadCapture thread_capture(parent_pid, heap);
     allocator::vector<ThreadInfo> thread_info(heap);
     allocator::vector<Mapping> mappings(heap);
+    allocator::vector<uintptr_t> refs(heap);
 
     // ptrace all the threads
     if (!thread_capture.CaptureThreads()) {
@@ -298,6 +307,11 @@
       return 1;
     }
 
+    if (!BinderReferences(refs)) {
+      continue_parent_sem.Post();
+      return 1;
+    }
+
     // malloc must be enabled to call fork, at_fork handlers take the same
     // locks as ScopedDisableMalloc.  All threads are paused in ptrace, so
     // memory state is still consistent.  Unfreeze the original thread so it
@@ -323,7 +337,7 @@
 
       MemUnreachable unreachable{parent_pid, heap};
 
-      if (!unreachable.CollectAllocations(thread_info, mappings)) {
+      if (!unreachable.CollectAllocations(thread_info, mappings, refs)) {
         _exit(2);
       }
       size_t num_allocations = unreachable.Allocations();
@@ -403,7 +417,6 @@
 }
 
 std::string Leak::ToString(bool log_contents) const {
-
   std::ostringstream oss;
 
   oss << "  " << std::dec << size;
@@ -492,8 +505,8 @@
   oss << std::endl;
 
   for (auto it = leaks.begin(); it != leaks.end(); it++) {
-      oss << it->ToString(log_contents);
-      oss << std::endl;
+    oss << it->ToString(log_contents);
+    oss << std::endl;
   }
 
   return oss.str();
@@ -502,15 +515,20 @@
 std::string GetUnreachableMemoryString(bool log_contents, size_t limit) {
   UnreachableMemoryInfo info;
   if (!GetUnreachableMemory(info, limit)) {
-    return "Failed to get unreachable memory\n";
+    return "Failed to get unreachable memory\n"
+           "If you are trying to get unreachable memory from a system app\n"
+           "(like com.android.systemui), disable selinux first using\n"
+           "setenforce 0\n";
   }
 
   return info.ToString(log_contents);
 }
 
+}  // namespace android
+
 bool LogUnreachableMemory(bool log_contents, size_t limit) {
-  UnreachableMemoryInfo info;
-  if (!GetUnreachableMemory(info, limit)) {
+  android::UnreachableMemoryInfo info;
+  if (!android::GetUnreachableMemory(info, limit)) {
     return false;
   }
 
@@ -520,10 +538,9 @@
   return true;
 }
 
-
 bool NoLeaks() {
-  UnreachableMemoryInfo info;
-  if (!GetUnreachableMemory(info, 0)) {
+  android::UnreachableMemoryInfo info;
+  if (!android::GetUnreachableMemory(info, 0)) {
     return false;
   }
 
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
index 57b2321..9a06870 100644
--- a/libmemunreachable/ProcessMappings.cpp
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include <inttypes.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <string.h>
 #include <unistd.h>
 
@@ -25,6 +25,8 @@
 #include "ProcessMappings.h"
 #include "log.h"
 
+namespace android {
+
 // This function is not re-entrant since it uses a static buffer for
 // the line data.
 bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings) {
@@ -42,8 +44,8 @@
     int name_pos;
     char perms[5];
     Mapping mapping{};
-    if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n",
-        &mapping.begin, &mapping.end, perms, &name_pos) == 3) {
+    if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n", &mapping.begin,
+               &mapping.end, perms, &name_pos) == 3) {
       if (perms[0] == 'r') {
         mapping.read = true;
       }
@@ -64,3 +66,5 @@
   }
   return true;
 }
+
+}  // namespace android
diff --git a/libmemunreachable/ProcessMappings.h b/libmemunreachable/ProcessMappings.h
index d3b7496..a0e97e9 100644
--- a/libmemunreachable/ProcessMappings.h
+++ b/libmemunreachable/ProcessMappings.h
@@ -19,6 +19,8 @@
 
 #include "Allocator.h"
 
+namespace android {
+
 struct Mapping {
   uintptr_t begin;
   uintptr_t end;
@@ -33,4 +35,6 @@
 // the line data.
 bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings);
 
-#endif // LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
index 73b0493..aca2a82 100644
--- a/libmemunreachable/PtracerThread.cpp
+++ b/libmemunreachable/PtracerThread.cpp
@@ -23,17 +23,19 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 #include <sys/mman.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 #include "android-base/macros.h"
 
+#include "PtracerThread.h"
 #include "anon_vma_naming.h"
 #include "log.h"
-#include "PtracerThread.h"
+
+namespace android {
 
 class Stack {
  public:
@@ -41,7 +43,7 @@
     int prot = PROT_READ | PROT_WRITE;
     int flags = MAP_PRIVATE | MAP_ANONYMOUS;
     page_size_ = sysconf(_SC_PAGE_SIZE);
-    size_ += page_size_*2; // guard pages
+    size_ += page_size_ * 2;  // guard pages
     base_ = mmap(NULL, size_, prot, flags, -1, 0);
     if (base_ == MAP_FAILED) {
       base_ = NULL;
@@ -52,22 +54,20 @@
     mprotect(base_, page_size_, PROT_NONE);
     mprotect(top(), page_size_, PROT_NONE);
   };
-  ~Stack() {
-    munmap(base_, size_);
-  };
+  ~Stack() { munmap(base_, size_); };
   void* top() {
     return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_) + size_ - page_size_);
   };
+
  private:
   DISALLOW_COPY_AND_ASSIGN(Stack);
 
-  void *base_;
+  void* base_;
   size_t size_;
   size_t page_size_;
 };
 
-PtracerThread::PtracerThread(const std::function<int()>& func) :
-    child_pid_(0) {
+PtracerThread::PtracerThread(const std::function<int()>& func) : child_pid_(0) {
   stack_ = std::make_unique<Stack>(PTHREAD_STACK_MIN);
   if (stack_->top() == nullptr) {
     MEM_LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
@@ -93,14 +93,13 @@
   std::unique_lock<std::mutex> lk(m_);
 
   // Convert from void(*)(void*) to lambda with captures
-  auto proxy = [](void *arg) -> int {
+  auto proxy = [](void* arg) -> int {
     prctl(PR_SET_NAME, "libmemunreachable ptrace thread");
     return (*reinterpret_cast<std::function<int()>*>(arg))();
   };
 
-  child_pid_ = clone(proxy, stack_->top(),
-       CLONE_VM|CLONE_FS|CLONE_FILES/*|CLONE_UNTRACED*/,
-       reinterpret_cast<void*>(&func_));
+  child_pid_ = clone(proxy, stack_->top(), CLONE_VM | CLONE_FS | CLONE_FILES /*|CLONE_UNTRACED*/,
+                     reinterpret_cast<void*>(&func_));
   if (child_pid_ < 0) {
     MEM_ALOGE("failed to clone child: %s", strerror(errno));
     return false;
@@ -151,3 +150,5 @@
 void PtracerThread::ClearTracer() {
   prctl(PR_SET_PTRACER, 0);
 }
+
+}  // namespace android
diff --git a/libmemunreachable/PtracerThread.h b/libmemunreachable/PtracerThread.h
index f88b599..4f9c420 100644
--- a/libmemunreachable/PtracerThread.h
+++ b/libmemunreachable/PtracerThread.h
@@ -24,6 +24,8 @@
 
 #include "Allocator.h"
 
+namespace android {
+
 class Stack;
 
 // PtracerThread is similar to std::thread, except that it creates a "thread"
@@ -36,6 +38,7 @@
   ~PtracerThread();
   bool Start();
   int Join();
+
  private:
   void SetTracer(pid_t);
   void ClearTracer();
@@ -47,4 +50,6 @@
   pid_t child_pid_;
 };
 
-#endif // LIBMEMUNREACHABLE_PTRACER_THREAD_H_
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_PTRACER_THREAD_H_
diff --git a/libmemunreachable/ScopedAlarm.h b/libmemunreachable/ScopedAlarm.h
index 287f479..bb50b9e 100644
--- a/libmemunreachable/ScopedAlarm.h
+++ b/libmemunreachable/ScopedAlarm.h
@@ -23,15 +23,15 @@
 #include <chrono>
 #include <functional>
 
+namespace android {
+
 class ScopedAlarm {
  public:
   ScopedAlarm(std::chrono::microseconds us, std::function<void()> func) {
     func_ = func;
-    struct sigaction oldact{};
-    struct sigaction act{};
-    act.sa_handler = [](int) {
-      ScopedAlarm::func_();
-    };
+    struct sigaction oldact {};
+    struct sigaction act {};
+    act.sa_handler = [](int) { ScopedAlarm::func_(); };
     sigaction(SIGALRM, &act, &oldact);
 
     std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(us);
@@ -43,11 +43,15 @@
   ~ScopedAlarm() {
     itimerval t = itimerval{};
     setitimer(ITIMER_REAL, &t, NULL);
-    struct sigaction act{};
+    struct sigaction act {};
     act.sa_handler = SIG_DFL;
     sigaction(SIGALRM, &act, NULL);
   }
+
  private:
   static std::function<void()> func_;
 };
+
+}  // namespace android
+
 #endif
diff --git a/libmemunreachable/ScopedDisableMalloc.h b/libmemunreachable/ScopedDisableMalloc.h
index 758d317..655e826 100644
--- a/libmemunreachable/ScopedDisableMalloc.h
+++ b/libmemunreachable/ScopedDisableMalloc.h
@@ -21,16 +21,16 @@
 
 #include "android-base/macros.h"
 
+#include "ScopedAlarm.h"
 #include "bionic.h"
 #include "log.h"
-#include "ScopedAlarm.h"
 
-class DisableMallocGuard{
+namespace android {
+
+class DisableMallocGuard {
  public:
-  DisableMallocGuard() : disabled_(false){}
-  ~DisableMallocGuard() {
-    Enable();
-  }
+  DisableMallocGuard() : disabled_(false) {}
+  ~DisableMallocGuard() { Enable(); }
 
   void Disable() {
     if (!disabled_) {
@@ -45,6 +45,7 @@
       disabled_ = false;
     }
   }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(DisableMallocGuard);
   bool disabled_;
@@ -59,13 +60,9 @@
 // here.
 class ScopedDisableMalloc {
  public:
-  ScopedDisableMalloc() {
-    disable_malloc_.Disable();
-  }
+  ScopedDisableMalloc() { disable_malloc_.Disable(); }
 
-  ~ScopedDisableMalloc() {
-    disable_malloc_.Enable();
-  }
+  ~ScopedDisableMalloc() { disable_malloc_.Enable(); }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ScopedDisableMalloc);
@@ -74,18 +71,15 @@
 
 class ScopedDisableMallocTimeout {
  public:
-  explicit ScopedDisableMallocTimeout(std::chrono::milliseconds timeout = std::chrono::milliseconds(2000)) :
-    timeout_(timeout), timed_out_(false), disable_malloc_() {
+  explicit ScopedDisableMallocTimeout(
+      std::chrono::milliseconds timeout = std::chrono::milliseconds(2000))
+      : timeout_(timeout), timed_out_(false), disable_malloc_() {
     Disable();
   }
 
-  ~ScopedDisableMallocTimeout() {
-    Enable();
-  }
+  ~ScopedDisableMallocTimeout() { Enable(); }
 
-  bool timed_out() {
-    return timed_out_;
-  }
+  bool timed_out() { return timed_out_; }
 
   void Enable() {
     disable_malloc_.Enable();
@@ -110,4 +104,6 @@
   DisableMallocGuard disable_malloc_;
 };
 
-#endif // LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
index 7f44953..adabfd8 100644
--- a/libmemunreachable/ScopedPipe.h
+++ b/libmemunreachable/ScopedPipe.h
@@ -21,6 +21,8 @@
 
 #include "log.h"
 
+namespace android {
+
 class ScopedPipe {
  public:
   ScopedPipe() : pipefd_{-1, -1} {
@@ -29,28 +31,22 @@
       MEM_LOG_ALWAYS_FATAL("failed to open pipe");
     }
   }
-  ~ScopedPipe() {
-    Close();
-  }
+  ~ScopedPipe() { Close(); }
 
   ScopedPipe(ScopedPipe&& other) {
     SetReceiver(other.ReleaseReceiver());
     SetSender(other.ReleaseSender());
   }
 
-  ScopedPipe& operator = (ScopedPipe&& other) {
+  ScopedPipe& operator=(ScopedPipe&& other) {
     SetReceiver(other.ReleaseReceiver());
     SetSender(other.ReleaseSender());
     return *this;
   }
 
-  void CloseReceiver() {
-    close(ReleaseReceiver());
-  }
+  void CloseReceiver() { close(ReleaseReceiver()); }
 
-  void CloseSender() {
-    close(ReleaseSender());
-  }
+  void CloseSender() { close(ReleaseSender()); }
 
   void Close() {
     CloseReceiver();
@@ -78,4 +74,7 @@
 
   int pipefd_[2];
 };
+
+}  // namespace android
+
 #endif
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index ada2ae4..ff53fad 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -26,18 +26,18 @@
 
 #include "log.h"
 
+namespace android {
+
 class ScopedSignalHandler {
  public:
   using Fn = std::function<void(ScopedSignalHandler&, int, siginfo_t*, void*)>;
 
   explicit ScopedSignalHandler(Allocator<Fn> allocator) : allocator_(allocator), signal_(-1) {}
-  ~ScopedSignalHandler() {
-    reset();
-  }
+  ~ScopedSignalHandler() { reset(); }
 
   template <class F>
   void install(int signal, F&& f) {
-    MEM_LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");
+    if (signal_ != -1) MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed");
 
     handler_ = SignalFn(std::allocator_arg, allocator_,
                         [=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
@@ -65,7 +65,6 @@
     }
   }
 
-
  private:
   using SignalFn = std::function<void(int, siginfo_t*, void*)>;
   DISALLOW_COPY_AND_ASSIGN(ScopedSignalHandler);
@@ -77,4 +76,6 @@
   static SignalFn handler_;
 };
 
-#endif // LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_
diff --git a/libmemunreachable/Semaphore.h b/libmemunreachable/Semaphore.h
index 6bcf4ea..cd73972 100644
--- a/libmemunreachable/Semaphore.h
+++ b/libmemunreachable/Semaphore.h
@@ -22,6 +22,8 @@
 
 #include "android-base/macros.h"
 
+namespace android {
+
 class Semaphore {
  public:
   explicit Semaphore(int count = 0) : count_(count) {}
@@ -29,7 +31,7 @@
 
   void Wait(std::chrono::milliseconds ms) {
     std::unique_lock<std::mutex> lk(m_);
-    cv_.wait_for(lk, ms, [&]{
+    cv_.wait_for(lk, ms, [&] {
       if (count_ > 0) {
         count_--;
         return true;
@@ -44,6 +46,7 @@
     }
     cv_.notify_one();
   }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(Semaphore);
 
@@ -52,5 +55,6 @@
   std::condition_variable cv_;
 };
 
+}  // namespace android
 
-#endif // LIBMEMUNREACHABLE_SEMAPHORE_H_
+#endif  // LIBMEMUNREACHABLE_SEMAPHORE_H_
diff --git a/libmemunreachable/Tarjan.h b/libmemunreachable/Tarjan.h
index 2546341..355679f 100644
--- a/libmemunreachable/Tarjan.h
+++ b/libmemunreachable/Tarjan.h
@@ -24,7 +24,9 @@
 
 #include "Allocator.h"
 
-template<class T>
+namespace android {
+
+template <class T>
 class Node {
  public:
   allocator::set<Node<T>*> references_in;
@@ -34,39 +36,41 @@
 
   T* ptr;
 
-  Node(T* ptr, Allocator<Node> allocator) : references_in(allocator), references_out(allocator),
-      ptr(ptr) {};
+  Node(T* ptr, Allocator<Node> allocator)
+      : references_in(allocator), references_out(allocator), ptr(ptr){};
   Node(Node&& rhs) = default;
   void Edge(Node<T>* ref) {
     references_out.emplace(ref);
     ref->references_in.emplace(this);
   }
-  template<class F>
+  template <class F>
   void Foreach(F&& f) {
-    for (auto& node: references_out) {
+    for (auto& node : references_out) {
       f(node->ptr);
     }
   }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(Node<T>);
 };
 
-template<class T>
+template <class T>
 using Graph = allocator::vector<Node<T>*>;
 
-template<class T>
+template <class T>
 using SCC = allocator::vector<Node<T>*>;
 
-template<class T>
+template <class T>
 using SCCList = allocator::vector<SCC<T>>;
 
-template<class T>
+template <class T>
 class TarjanAlgorithm {
  public:
-  explicit TarjanAlgorithm(Allocator<void> allocator) : index_(0),
-    stack_(allocator), components_(allocator) {}
+  explicit TarjanAlgorithm(Allocator<void> allocator)
+      : index_(0), stack_(allocator), components_(allocator) {}
 
   void Execute(Graph<T>& graph, SCCList<T>& out);
+
  private:
   static constexpr size_t UNDEFINED_INDEX = static_cast<size_t>(-1);
   void Tarjan(Node<T>* vertex, Graph<T>& graph);
@@ -76,17 +80,17 @@
   SCCList<T> components_;
 };
 
-template<class T>
+template <class T>
 void TarjanAlgorithm<T>::Execute(Graph<T>& graph, SCCList<T>& out) {
   stack_.clear();
   components_.clear();
   index_ = 0;
-  for (auto& it: graph) {
+  for (auto& it : graph) {
     it->index = UNDEFINED_INDEX;
     it->lowlink = UNDEFINED_INDEX;
   }
 
-  for (auto& it: graph) {
+  for (auto& it : graph) {
     if (it->index == UNDEFINED_INDEX) {
       Tarjan(it, graph);
     }
@@ -94,14 +98,14 @@
   out.swap(components_);
 }
 
-template<class T>
+template <class T>
 void TarjanAlgorithm<T>::Tarjan(Node<T>* vertex, Graph<T>& graph) {
   assert(vertex->index == UNDEFINED_INDEX);
   vertex->index = index_;
   vertex->lowlink = index_;
   index_++;
   stack_.push_back(vertex);
-  for (auto& it: vertex->references_out) {
+  for (auto& it : vertex->references_out) {
     Node<T>* vertex_next = it;
     if (vertex_next->index == UNDEFINED_INDEX) {
       Tarjan(vertex_next, graph);
@@ -123,10 +127,12 @@
   }
 }
 
-template<class T>
+template <class T>
 void Tarjan(Graph<T>& graph, SCCList<T>& out) {
   TarjanAlgorithm<T> tarjan{graph.get_allocator()};
   tarjan.Execute(graph, out);
 }
 
-#endif // LIBMEMUNREACHABLE_TARJAN_H_
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_TARJAN_H_
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
index 3891f2d..45eb55d 100644
--- a/libmemunreachable/ThreadCapture.cpp
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -21,13 +21,13 @@
 #include <fcntl.h>
 #include <limits.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <sys/ptrace.h>
 #include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/uio.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 #include <map>
 #include <memory>
@@ -39,6 +39,8 @@
 #include "Allocator.h"
 #include "log.h"
 
+namespace android {
+
 // bionic interfaces used:
 // atoi
 // strlcat
@@ -50,12 +52,12 @@
 // Convert a pid > 0 to a string.  sprintf might allocate, so we can't use it.
 // Returns a pointer somewhere in buf to a null terminated string, or NULL
 // on error.
-static char *pid_to_str(char *buf, size_t len, pid_t pid) {
+static char* pid_to_str(char* buf, size_t len, pid_t pid) {
   if (pid <= 0) {
     return nullptr;
   }
 
-  char *ptr = buf + len - 1;
+  char* ptr = buf + len - 1;
   *ptr = 0;
   while (pid > 0) {
     ptr--;
@@ -79,6 +81,7 @@
   bool ReleaseThread(pid_t tid);
   bool CapturedThreadInfo(ThreadInfoList& threads);
   void InjectTestFunc(std::function<void(pid_t)>&& f) { inject_test_func_ = f; }
+
  private:
   int CaptureThread(pid_t tid);
   bool ReleaseThread(pid_t tid, unsigned int signal);
@@ -92,9 +95,8 @@
   std::function<void(pid_t)> inject_test_func_;
 };
 
-ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator) :
-    captured_threads_(allocator), allocator_(allocator), pid_(pid) {
-}
+ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator)
+    : captured_threads_(allocator), allocator_(allocator), pid_(pid) {}
 
 bool ThreadCaptureImpl::ListThreads(TidList& tids) {
   tids.clear();
@@ -115,11 +117,11 @@
   }
 
   struct linux_dirent64 {
-    uint64_t  d_ino;
-    int64_t   d_off;
-    uint16_t  d_reclen;
-    char      d_type;
-    char      d_name[];
+    uint64_t d_ino;
+    int64_t d_off;
+    uint16_t d_reclen;
+    char d_type;
+    char d_name[];
   } __attribute((packed));
   char dirent_buf[4096];
   ssize_t nread;
@@ -209,7 +211,7 @@
 bool ThreadCaptureImpl::PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info) {
   thread_info.tid = tid;
 
-  const unsigned int max_num_regs = 128; // larger than number of registers on any device
+  const unsigned int max_num_regs = 128;  // larger than number of registers on any device
   uintptr_t regs[max_num_regs];
   struct iovec iovec;
   iovec.iov_base = &regs;
@@ -243,7 +245,7 @@
 
   thread_info.stack = std::pair<uintptr_t, uintptr_t>(regs[sp], 0);
 
-   return true;
+  return true;
 }
 
 int ThreadCaptureImpl::CaptureThread(pid_t tid) {
@@ -266,7 +268,7 @@
 
   unsigned int resume_signal = 0;
 
-  unsigned int signal =  WSTOPSIG(status);
+  unsigned int signal = WSTOPSIG(status);
   if ((status >> 16) == PTRACE_EVENT_STOP) {
     switch (signal) {
       case SIGSTOP:
@@ -307,7 +309,7 @@
 
 bool ThreadCaptureImpl::ReleaseThreads() {
   bool ret = true;
-  for (auto it = captured_threads_.begin(); it != captured_threads_.end(); ) {
+  for (auto it = captured_threads_.begin(); it != captured_threads_.end();) {
     if (ReleaseThread(it->first, it->second)) {
       it = captured_threads_.erase(it);
     } else {
@@ -361,3 +363,5 @@
 void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) {
   impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f));
 }
+
+}  // namespace android
diff --git a/libmemunreachable/ThreadCapture.h b/libmemunreachable/ThreadCapture.h
index 1022cad..961cb60 100644
--- a/libmemunreachable/ThreadCapture.h
+++ b/libmemunreachable/ThreadCapture.h
@@ -21,6 +21,8 @@
 
 #include "Allocator.h"
 
+namespace android {
+
 struct ThreadInfo {
   pid_t tid;
   allocator::vector<uintptr_t> regs;
@@ -33,7 +35,7 @@
 class ThreadCaptureImpl;
 
 class ThreadCapture {
-public:
+ public:
   ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator);
   ~ThreadCapture();
 
@@ -44,11 +46,13 @@
   bool CapturedThreadInfo(ThreadInfoList& threads);
   void InjectTestFunc(std::function<void(pid_t)>&& f);
 
-private:
+ private:
   ThreadCapture(const ThreadCapture&) = delete;
   void operator=(const ThreadCapture&) = delete;
 
   Allocator<ThreadCaptureImpl>::unique_ptr impl_;
 };
 
+}  // namespace android
+
 #endif
diff --git a/libmemunreachable/anon_vma_naming.h b/libmemunreachable/anon_vma_naming.h
index 1e4ade1..fb31e41 100644
--- a/libmemunreachable/anon_vma_naming.h
+++ b/libmemunreachable/anon_vma_naming.h
@@ -19,7 +19,7 @@
 
 #include <sys/prctl.h>
 
-#define PR_SET_VMA            0x53564d41
-#define  PR_SET_VMA_ANON_NAME 0
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
 
-#endif // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
+#endif  // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
diff --git a/libmemunreachable/bionic.h b/libmemunreachable/bionic.h
index 83d07a8..dd1ec79 100644
--- a/libmemunreachable/bionic.h
+++ b/libmemunreachable/bionic.h
@@ -17,9 +17,9 @@
 #ifndef LIBMEMUNREACHABLE_BIONIC_H_
 #define LIBMEMUNREACHABLE_BIONIC_H_
 
-#include <sys/cdefs.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <sys/cdefs.h>
 
 __BEGIN_DECLS
 
@@ -27,9 +27,9 @@
 extern void malloc_disable();
 extern void malloc_enable();
 extern int malloc_iterate(uintptr_t base, size_t size,
-    void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
+                          void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
 extern ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count);
 
 __END_DECLS
 
-#endif // LIBMEMUNREACHABLE_BIONIC_H_
+#endif  // LIBMEMUNREACHABLE_BIONIC_H_
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
index 9b227fd..438fcaf 100644
--- a/libmemunreachable/include/memunreachable/memunreachable.h
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -17,12 +17,15 @@
 #ifndef LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
 #define LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
 
+#include <string.h>
 #include <sys/cdefs.h>
 
 #ifdef __cplusplus
 
-#include <vector>
 #include <string>
+#include <vector>
+
+namespace android {
 
 struct Leak {
   uintptr_t begin;
@@ -73,6 +76,8 @@
 
 std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100);
 
+}  // namespace android
+
 #endif
 
 __BEGIN_DECLS
@@ -83,4 +88,4 @@
 
 __END_DECLS
 
-#endif // LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
+#endif  // LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h
index 1725c53..44c5f85 100644
--- a/libmemunreachable/log.h
+++ b/libmemunreachable/log.h
@@ -26,13 +26,21 @@
 #define MEM_ALOGE(...) async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, ##__VA_ARGS__)
 #define MEM_ALOGW(...) async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, ##__VA_ARGS__)
 #define MEM_ALOGI(...) async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG, ##__VA_ARGS__)
-#define MEM_ALOGV(...) async_safe_format_log(ANDROID_LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGV_IMPL(...) async_safe_format_log(ANDROID_LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__)
+
+#ifdef NDEBUG
+#define MEM_ALOGV(...)             \
+  do {                             \
+    if (0) {                       \
+      MEM_ALOGV_IMPL(__VA_ARGS__); \
+    }                              \
+  } while (0)
+#else
+#define MEM_ALOGV(...) MEM_ALOGV_IMPL(__VA_ARGS__)
+#endif
 
 #define MEM_LOG_ALWAYS_FATAL(...) async_safe_fatal(__VA_ARGS__)
 
-#define MEM_LOG_ALWAYS_FATAL_IF(cond, ...) \
-  ((__predict_false(cond)) ? async_safe_fatal(__VA_ARGS__) : (void)0)
-
 #else
 
 #include <log/log.h>
@@ -43,8 +51,7 @@
 #define MEM_ALOGI ALOGI
 
 #define MEM_LOG_ALWAYS_FATAL LOG_ALWAYS_FATAL
-#define MEM_LOG_ALWAYS_FATAL_IF LOG_ALWAYS_FATAL_IF
 
 #endif
 
-#endif // LIBMEMUNREACHABLE_LOG_H_
+#endif  // LIBMEMUNREACHABLE_LOG_H_
diff --git a/libmemunreachable/tests/Allocator_test.cpp b/libmemunreachable/tests/Allocator_test.cpp
index 21c8218..8991a7b 100644
--- a/libmemunreachable/tests/Allocator_test.cpp
+++ b/libmemunreachable/tests/Allocator_test.cpp
@@ -16,44 +16,44 @@
 
 #include <Allocator.h>
 
-#include <gtest/gtest.h>
 #include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
 
+namespace android {
 
 std::function<void()> ScopedAlarm::func_;
 
 class AllocatorTest : public testing::Test {
  protected:
   AllocatorTest() : heap(), disable_malloc_() {}
-  virtual void SetUp() {
-    heap_count = 0;
-  }
+  virtual void SetUp() { heap_count = 0; }
   virtual void TearDown() {
     ASSERT_EQ(heap_count, 0);
     ASSERT_TRUE(heap.empty());
     ASSERT_FALSE(disable_malloc_.timed_out());
   }
   Heap heap;
+
  private:
   ScopedDisableMallocTimeout disable_malloc_;
 };
 
 TEST_F(AllocatorTest, simple) {
   Allocator<char[100]> allocator(heap);
-  void *ptr = allocator.allocate();
+  void* ptr = allocator.allocate();
   ASSERT_TRUE(ptr != NULL);
   allocator.deallocate(ptr);
 }
 
 TEST_F(AllocatorTest, multiple) {
   Allocator<char[100]> allocator(heap);
-  void *ptr1 = allocator.allocate();
+  void* ptr1 = allocator.allocate();
   ASSERT_TRUE(ptr1 != NULL);
-  void *ptr2 = allocator.allocate();
+  void* ptr2 = allocator.allocate();
   ASSERT_TRUE(ptr2 != NULL);
   ASSERT_NE(ptr1, ptr2);
   allocator.deallocate(ptr1);
-  void *ptr3 = allocator.allocate();
+  void* ptr3 = allocator.allocate();
   ASSERT_EQ(ptr1, ptr3);
   allocator.deallocate(ptr3);
   allocator.deallocate(ptr2);
@@ -63,7 +63,7 @@
   const int num = 4096;
   const int size = 128;
   Allocator<char[size]> allocator(heap);
-  void *ptr[num];
+  void* ptr[num];
   for (int i = 0; i < num; i++) {
     ptr[i] = allocator.allocate();
     memset(ptr[i], 0xaa, size);
@@ -87,7 +87,7 @@
 TEST_F(AllocatorTest, large) {
   const size_t size = 1024 * 1024;
   Allocator<char[size]> allocator(heap);
-  void *ptr = allocator.allocate();
+  void* ptr = allocator.allocate();
   memset(ptr, 0xaa, size);
   allocator.deallocate(ptr);
 }
@@ -96,7 +96,7 @@
   const int num = 128;
   const int size = 1024 * 1024;
   Allocator<char[size]> allocator(heap);
-  void *ptr[num];
+  void* ptr[num];
   for (int i = 0; i < num; i++) {
     ptr[i] = allocator.allocate();
     memset(ptr[i], 0xaa, size);
@@ -172,3 +172,5 @@
 
   ASSERT_NE(ptr, nullptr);
 }
+
+}  // namespace android
diff --git a/libmemunreachable/tests/AndroidTest.xml b/libmemunreachable/tests/AndroidTest.xml
new file mode 100644
index 0000000..604c0ec
--- /dev/null
+++ b/libmemunreachable/tests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<configuration description="Config for memunreachable_test">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="memunreachable_test->/data/local/tmp/memunreachable_test" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="memunreachable_test" />
+    </test>
+</configuration>
diff --git a/libmemunreachable/tests/Binder_test.cpp b/libmemunreachable/tests/Binder_test.cpp
new file mode 100644
index 0000000..6e85d5a
--- /dev/null
+++ b/libmemunreachable/tests/Binder_test.cpp
@@ -0,0 +1,156 @@
+/*
+ * 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 <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+
+#include <gtest/gtest.h>
+
+#include "Allocator.h"
+#include "Binder.h"
+
+namespace android {
+
+static const String16 service_name("test.libmemunreachable_binder");
+
+class BinderService : public BBinder {
+ public:
+  BinderService() = default;
+  virtual ~BinderService() = default;
+
+  virtual status_t onTransact(uint32_t /*code*/, const Parcel& data, Parcel* reply,
+                              uint32_t /*flags*/ = 0) {
+    reply->writeStrongBinder(ref);
+    ref = data.readStrongBinder();
+    return 0;
+  }
+
+ private:
+  sp<IBinder> ref;
+};
+
+class BinderObject : public BBinder {
+ public:
+  BinderObject() = default;
+  ~BinderObject() = default;
+};
+
+class ServiceProcess {
+ public:
+  ServiceProcess() : child_(0) {}
+  ~ServiceProcess() { Stop(); }
+
+  bool Run() {
+    pid_t ret = fork();
+    if (ret < 0) {
+      return false;
+    } else if (ret == 0) {
+      // child
+      _exit(Service());
+    } else {
+      // parent
+      child_ = ret;
+      return true;
+    }
+  }
+
+  bool Stop() {
+    if (child_ > 0) {
+      if (kill(child_, SIGTERM)) {
+        return false;
+      }
+      int status = 0;
+      if (TEMP_FAILURE_RETRY(waitpid(child_, &status, 0)) != child_) {
+        return false;
+      }
+      child_ = 0;
+      return WIFEXITED(status) && WEXITSTATUS(status) == 0;
+    }
+
+    return true;
+  }
+
+  int Service() {
+    sp<ProcessState> proc{ProcessState::self()};
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == nullptr) {
+      fprintf(stderr, "Failed to get service manager\n");
+      return 1;
+    }
+    if (sm->addService(service_name, new BinderService()) != OK) {
+      fprintf(stderr, "Failed to add test service\n");
+      return 1;
+    }
+    proc->startThreadPool();
+    pause();
+    return 0;
+  }
+
+ private:
+  pid_t child_;
+};
+
+class BinderTest : public ::testing::Test {
+ protected:
+  ServiceProcess service_process_;
+};
+
+TEST_F(BinderTest, binder) {
+  ServiceProcess service_process;
+  ASSERT_TRUE(service_process.Run());
+
+  sp<IServiceManager> sm = defaultServiceManager();
+  ASSERT_TRUE(sm != nullptr);
+
+  // A small sleep allows the service to start, which
+  // prevents a longer sleep in getService.
+  usleep(100000);
+
+  sp<IBinder> service = sm->getService(service_name);
+  ASSERT_TRUE(service != nullptr);
+
+  sp<IBinder> binder{new BinderObject()};
+
+  Parcel send;
+  Parcel reply;
+
+  send.writeStrongBinder(binder);
+  status_t rv = service->transact(0, send, &reply);
+  ASSERT_EQ(static_cast<status_t>(OK), rv);
+
+  Heap heap;
+  allocator::vector<uintptr_t> refs{heap};
+
+  ASSERT_TRUE(BinderReferences(refs));
+
+  bool found_ref = false;
+  for (auto ref : refs) {
+    if (ref == reinterpret_cast<uintptr_t>(binder.get())) {
+      found_ref = true;
+    }
+  }
+
+  ASSERT_TRUE(found_ref);
+}
+
+}  // namespace android
diff --git a/libmemunreachable/tests/DisableMalloc_test.cpp b/libmemunreachable/tests/DisableMalloc_test.cpp
index 4e6155b..c630049 100644
--- a/libmemunreachable/tests/DisableMalloc_test.cpp
+++ b/libmemunreachable/tests/DisableMalloc_test.cpp
@@ -19,11 +19,13 @@
 #include <chrono>
 #include <functional>
 
-#include <gtest/gtest.h>
 #include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
 
 using namespace std::chrono_literals;
 
+namespace android {
+
 class DisableMallocTest : public ::testing::Test {
  protected:
   void alarm(std::chrono::microseconds us) {
@@ -36,75 +38,83 @@
 };
 
 TEST_F(DisableMallocTest, reenable) {
-  ASSERT_EXIT({
-    alarm(100ms);
-    void *ptr1 = malloc(128);
-    ASSERT_NE(ptr1, nullptr);
-    free(ptr1);
-    {
-      ScopedDisableMalloc disable_malloc;
-    }
-    void *ptr2 = malloc(128);
-    ASSERT_NE(ptr2, nullptr);
-    free(ptr2);
-    _exit(1);
-  }, ::testing::ExitedWithCode(1), "");
+  ASSERT_EXIT(
+      {
+        alarm(100ms);
+        void* ptr1 = malloc(128);
+        ASSERT_NE(ptr1, nullptr);
+        free(ptr1);
+        { ScopedDisableMalloc disable_malloc; }
+        void* ptr2 = malloc(128);
+        ASSERT_NE(ptr2, nullptr);
+        free(ptr2);
+        _exit(1);
+      },
+      ::testing::ExitedWithCode(1), "");
 }
 
 TEST_F(DisableMallocTest, deadlock_allocate) {
-  ASSERT_DEATH({
-    void *ptr = malloc(128);
-    ASSERT_NE(ptr, nullptr);
-    free(ptr);
-    {
-      alarm(100ms);
-      ScopedDisableMalloc disable_malloc;
-      void* ptr = malloc(128);
-      ASSERT_NE(ptr, nullptr);
-      free(ptr);
-    }
-  }, "");
+  ASSERT_DEATH(
+      {
+        void* ptr = malloc(128);
+        ASSERT_NE(ptr, nullptr);
+        free(ptr);
+        {
+          alarm(100ms);
+          ScopedDisableMalloc disable_malloc;
+          void* ptr = malloc(128);
+          ASSERT_NE(ptr, nullptr);
+          free(ptr);
+        }
+      },
+      "");
 }
 
 TEST_F(DisableMallocTest, deadlock_new) {
-  ASSERT_DEATH({
-    char* ptr = new(char);
-    ASSERT_NE(ptr, nullptr);
-    delete(ptr);
-    {
-      alarm(100ms);
-      ScopedDisableMalloc disable_malloc;
-      char* ptr = new (std::nothrow)(char);
-      ASSERT_NE(ptr, nullptr);
-      delete(ptr);
-    }
-  }, "");
+  ASSERT_DEATH(
+      {
+        char* ptr = new (char);
+        ASSERT_NE(ptr, nullptr);
+        delete (ptr);
+        {
+          alarm(100ms);
+          ScopedDisableMalloc disable_malloc;
+          char* ptr = new (std::nothrow)(char);
+          ASSERT_NE(ptr, nullptr);
+          delete (ptr);
+        }
+      },
+      "");
 }
 
 TEST_F(DisableMallocTest, deadlock_delete) {
-  ASSERT_DEATH({
-    char* ptr = new(char);
-    ASSERT_NE(ptr, nullptr);
-    {
-      alarm(250ms);
-      ScopedDisableMalloc disable_malloc;
-      delete(ptr);
-      // Force ptr usage or this code gets optimized away by the arm64 compiler.
-      ASSERT_NE(ptr, nullptr);
-    }
-  }, "");
+  ASSERT_DEATH(
+      {
+        char* ptr = new (char);
+        ASSERT_NE(ptr, nullptr);
+        {
+          alarm(250ms);
+          ScopedDisableMalloc disable_malloc;
+          delete (ptr);
+          // Force ptr usage or this code gets optimized away by the arm64 compiler.
+          ASSERT_NE(ptr, nullptr);
+        }
+      },
+      "");
 }
 
 TEST_F(DisableMallocTest, deadlock_free) {
-  ASSERT_DEATH({
-    void *ptr = malloc(128);
-    ASSERT_NE(ptr, nullptr);
-    {
-      alarm(100ms);
-      ScopedDisableMalloc disable_malloc;
-      free(ptr);
-    }
-  }, "");
+  ASSERT_DEATH(
+      {
+        void* ptr = malloc(128);
+        ASSERT_NE(ptr, nullptr);
+        {
+          alarm(100ms);
+          ScopedDisableMalloc disable_malloc;
+          free(ptr);
+        }
+      },
+      "");
 }
 
 TEST_F(DisableMallocTest, deadlock_fork) {
@@ -113,6 +123,8 @@
       alarm(100ms);
       ScopedDisableMalloc disable_malloc;
       fork();
-    }
-  }, "");
 }
+}, "");
+}
+
+}  // namespace android
diff --git a/libmemunreachable/tests/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp
index 98e4aa1..84a0ec6 100644
--- a/libmemunreachable/tests/HeapWalker_test.cpp
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -19,10 +19,12 @@
 
 #include "HeapWalker.h"
 
-#include <gtest/gtest.h>
 #include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
 #include "Allocator.h"
 
+namespace android {
+
 class HeapWalkerTest : public ::testing::Test {
  public:
   HeapWalkerTest() : disable_malloc_(), heap_() {}
@@ -172,20 +174,20 @@
   ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
 
   EXPECT_EQ(2U, num_leaks);
-  EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+  EXPECT_EQ(2 * sizeof(uintptr_t), leaked_bytes);
   ASSERT_EQ(2U, leaked.size());
 }
 
 TEST_F(HeapWalkerTest, segv) {
   const size_t page_size = sysconf(_SC_PAGE_SIZE);
-  void* buffer1 = mmap(NULL, page_size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+  void* buffer1 = mmap(NULL, page_size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
   ASSERT_NE(buffer1, nullptr);
   void* buffer2;
 
   buffer2 = &buffer1;
 
   HeapWalker heap_walker(heap_);
-  heap_walker.Allocation(buffer_begin(buffer1), buffer_begin(buffer1)+page_size);
+  heap_walker.Allocation(buffer_begin(buffer1), buffer_begin(buffer1) + page_size);
   heap_walker.Root(buffer_begin(buffer2), buffer_end(buffer2));
 
   ASSERT_EQ(true, heap_walker.DetectLeaks());
@@ -199,3 +201,5 @@
   EXPECT_EQ(0U, leaked_bytes);
   ASSERT_EQ(0U, leaked.size());
 }
+
+}  // namespace android
diff --git a/libmemunreachable/tests/HostMallocStub.cpp b/libmemunreachable/tests/HostMallocStub.cpp
index a7e3f07..0ef0487 100644
--- a/libmemunreachable/tests/HostMallocStub.cpp
+++ b/libmemunreachable/tests/HostMallocStub.cpp
@@ -16,8 +16,6 @@
 
 #include "bionic.h"
 
-void malloc_disable() {
-}
+void malloc_disable() {}
 
-void malloc_enable() {
-}
+void malloc_enable() {}
diff --git a/libmemunreachable/tests/LeakFolding_test.cpp b/libmemunreachable/tests/LeakFolding_test.cpp
index e85df5f..f5b3631 100644
--- a/libmemunreachable/tests/LeakFolding_test.cpp
+++ b/libmemunreachable/tests/LeakFolding_test.cpp
@@ -14,13 +14,15 @@
  * limitations under the License.
  */
 
-#include "HeapWalker.h"
 #include "LeakFolding.h"
+#include "HeapWalker.h"
 
-#include <gtest/gtest.h>
 #include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
 #include "Allocator.h"
 
+namespace android {
+
 class LeakFoldingTest : public ::testing::Test {
  public:
   LeakFoldingTest() : disable_malloc_(), heap_() {}
@@ -84,7 +86,7 @@
   ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
 
   EXPECT_EQ(2U, num_leaks);
-  EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+  EXPECT_EQ(2 * sizeof(uintptr_t), leaked_bytes);
   ASSERT_EQ(2U, leaked.size());
   EXPECT_EQ(0U, leaked[0].referenced_count);
   EXPECT_EQ(0U, leaked[0].referenced_size);
@@ -113,7 +115,7 @@
   ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
 
   EXPECT_EQ(2U, num_leaks);
-  EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+  EXPECT_EQ(2 * sizeof(uintptr_t), leaked_bytes);
   ASSERT_EQ(1U, leaked.size());
   EXPECT_EQ(1U, leaked[0].referenced_count);
   EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
@@ -144,10 +146,10 @@
   ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
 
   EXPECT_EQ(3U, num_leaks);
-  EXPECT_EQ(3*sizeof(uintptr_t), leaked_bytes);
+  EXPECT_EQ(3 * sizeof(uintptr_t), leaked_bytes);
   ASSERT_EQ(1U, leaked.size());
   EXPECT_EQ(2U, leaked[0].referenced_count);
-  EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+  EXPECT_EQ(2 * sizeof(uintptr_t), leaked[0].referenced_size);
 }
 
 TEST_F(LeakFoldingTest, dominator_cycle) {
@@ -175,13 +177,13 @@
   ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
 
   EXPECT_EQ(3U, num_leaks);
-  EXPECT_EQ(5*sizeof(uintptr_t), leaked_bytes);
+  EXPECT_EQ(5 * sizeof(uintptr_t), leaked_bytes);
   ASSERT_EQ(2U, leaked.size());
 
   EXPECT_EQ(2U, leaked[0].referenced_count);
-  EXPECT_EQ(3*sizeof(uintptr_t), leaked[0].referenced_size);
+  EXPECT_EQ(3 * sizeof(uintptr_t), leaked[0].referenced_size);
   EXPECT_EQ(2U, leaked[1].referenced_count);
-  EXPECT_EQ(3*sizeof(uintptr_t), leaked[1].referenced_size);
+  EXPECT_EQ(3 * sizeof(uintptr_t), leaked[1].referenced_size);
 }
 
 TEST_F(LeakFoldingTest, two_cycles) {
@@ -218,12 +220,12 @@
   ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
 
   EXPECT_EQ(6U, num_leaks);
-  EXPECT_EQ(6*sizeof(uintptr_t), leaked_bytes);
+  EXPECT_EQ(6 * sizeof(uintptr_t), leaked_bytes);
   ASSERT_EQ(2U, leaked.size());
   EXPECT_EQ(2U, leaked[0].referenced_count);
-  EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+  EXPECT_EQ(2 * sizeof(uintptr_t), leaked[0].referenced_size);
   EXPECT_EQ(2U, leaked[1].referenced_count);
-  EXPECT_EQ(2*sizeof(uintptr_t), leaked[1].referenced_size);
+  EXPECT_EQ(2 * sizeof(uintptr_t), leaked[1].referenced_size);
 }
 
 TEST_F(LeakFoldingTest, two_dominator_cycles) {
@@ -254,7 +256,7 @@
   ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
 
   EXPECT_EQ(4U, num_leaks);
-  EXPECT_EQ(4*sizeof(uintptr_t), leaked_bytes);
+  EXPECT_EQ(4 * sizeof(uintptr_t), leaked_bytes);
   ASSERT_EQ(4U, leaked.size());
   EXPECT_EQ(1U, leaked[0].referenced_count);
   EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
@@ -272,13 +274,13 @@
 
   HeapWalker heap_walker(heap_);
 
-  for (size_t i = 0; i < n; i ++) {
+  for (size_t i = 0; i < n; i++) {
     ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
-        reinterpret_cast<uintptr_t>(&buffer[i+1])));
+                                       reinterpret_cast<uintptr_t>(&buffer[i + 1])));
   }
 
   for (size_t i = 0; i < n - 1; i++) {
-    buffer[i] = &buffer[i+1];
+    buffer[i] = &buffer[i + 1];
   }
   buffer[n - 1] = &buffer[0];
 
@@ -306,15 +308,15 @@
   HeapWalker heap_walker(heap_);
 
   for (size_t i = 0; i < n - 1; i++) {
-    buffer[i] = &buffer[i+1];
+    buffer[i] = &buffer[i + 1];
   }
   buffer[n - 1] = &buffer[0];
 
   buffer1[0] = &buffer[0];
 
-  for (size_t i = 0; i < n; i ++) {
+  for (size_t i = 0; i < n; i++) {
     ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
-        reinterpret_cast<uintptr_t>(&buffer[i+1])));
+                                       reinterpret_cast<uintptr_t>(&buffer[i + 1])));
   }
 
   ALLOCATION(heap_walker, buffer1);
@@ -425,3 +427,5 @@
   EXPECT_EQ(3U, leaked[3].referenced_count);
   EXPECT_EQ(6 * sizeof(uintptr_t), leaked[3].referenced_size);
 }
+
+}  // namespace android
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
index 71da365..ec89388 100644
--- a/libmemunreachable/tests/MemUnreachable_test.cpp
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -16,36 +16,32 @@
 
 #include <fcntl.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <sys/prctl.h>
+#include <unistd.h>
 
 #include <gtest/gtest.h>
 
 #include <memunreachable/memunreachable.h>
 
+namespace android {
+
 class HiddenPointer {
  public:
-  explicit HiddenPointer(size_t size = 256) {
-    Set(malloc(size));
-  }
-  ~HiddenPointer() {
-    Free();
-  }
-  void* Get() {
-    return reinterpret_cast<void*>(~ptr_);
-  }
+  explicit HiddenPointer(size_t size = 256) { Set(malloc(size)); }
+  ~HiddenPointer() { Free(); }
+  void* Get() { return reinterpret_cast<void*>(~ptr_); }
   void Free() {
     free(Get());
     Set(nullptr);
   }
+
  private:
-  void Set(void* ptr) {
-    ptr_ = ~reinterpret_cast<uintptr_t>(ptr);
-  }
+  void Set(void* ptr) { ptr_ = ~reinterpret_cast<uintptr_t>(ptr); }
   volatile uintptr_t ptr_;
 };
 
-static void Ref(void* ptr) {
+// Trick the compiler into thinking a value on the stack is still referenced.
+static void Ref(void** ptr) {
   write(0, ptr, 0);
 }
 
@@ -63,14 +59,14 @@
 
   {
     void* ptr = hidden_ptr.Get();
-    Ref(ptr);
+    Ref(&ptr);
 
     UnreachableMemoryInfo info;
 
     ASSERT_TRUE(GetUnreachableMemory(info));
     ASSERT_EQ(0U, info.leaks.size());
 
-    Ref(ptr);
+    ptr = nullptr;
   }
 
   {
@@ -216,3 +212,5 @@
 
   ASSERT_TRUE(LogUnreachableMemory(true, 100));
 }
+
+}  // namespace android
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
index 44aabd7..4fbf729 100644
--- a/libmemunreachable/tests/ThreadCapture_test.cpp
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -34,6 +34,8 @@
 
 using namespace std::chrono_literals;
 
+namespace android {
+
 class ThreadListTest : public ::testing::TestWithParam<int> {
  public:
   ThreadListTest() : stop_(false) {}
@@ -45,12 +47,10 @@
     WaitForThreads();
   }
 
-  virtual void TearDown() {
-    ASSERT_TRUE(heap.empty());
-  }
+  virtual void TearDown() { ASSERT_TRUE(heap.empty()); }
 
  protected:
-  template<class Function>
+  template <class Function>
   void StartThreads(unsigned int threads, Function&& func) {
     threads_.reserve(threads);
     tids_.reserve(threads);
@@ -68,14 +68,14 @@
 
         {
           std::unique_lock<std::mutex> lk(m_);
-          cv_stop_.wait(lk, [&] {return stop_;});
+          cv_stop_.wait(lk, [&] { return stop_; });
         }
       });
     }
 
     {
       std::unique_lock<std::mutex> lk(m_);
-      cv_start_.wait(lk, [&]{ return tids_.size() == threads; });
+      cv_start_.wait(lk, [&] { return tids_.size() == threads; });
     }
   }
 
@@ -93,9 +93,7 @@
     tids_.clear();
   }
 
-  std::vector<pid_t>& tids() {
-    return tids_;
-  }
+  std::vector<pid_t>& tids() { return tids_; }
 
   Heap heap;
 
@@ -143,7 +141,7 @@
 TEST_P(ThreadListTest, list_some) {
   const unsigned int threads = GetParam() - 1;
 
-  StartThreads(threads, [](){});
+  StartThreads(threads, []() {});
   std::vector<pid_t> expected_tids = tids();
   expected_tids.push_back(getpid());
 
@@ -176,10 +174,8 @@
  public:
   ThreadCaptureTest() {}
   ~ThreadCaptureTest() {}
-  void Fork(std::function<void()>&& child_init,
-      std::function<void()>&& child_cleanup,
-      std::function<void(pid_t)>&& parent) {
-
+  void Fork(std::function<void()>&& child_init, std::function<void()>&& child_cleanup,
+            std::function<void(pid_t)>&& parent) {
     ScopedPipe start_pipe;
     ScopedPipe stop_pipe;
 
@@ -211,39 +207,40 @@
 TEST_P(ThreadCaptureTest, capture_some) {
   const unsigned int threads = GetParam();
 
-  Fork([&](){
-    // child init
-    StartThreads(threads - 1, [](){});
-  },
-  [&](){
-    // child cleanup
-    StopThreads();
-  },
-  [&](pid_t child){
-    // parent
-    ASSERT_GT(child, 0);
+  Fork(
+      [&]() {
+        // child init
+        StartThreads(threads - 1, []() {});
+      },
+      [&]() {
+        // child cleanup
+        StopThreads();
+      },
+      [&](pid_t child) {
+        // parent
+        ASSERT_GT(child, 0);
 
-    {
-      ScopedDisableMallocTimeout disable_malloc;
+        {
+          ScopedDisableMallocTimeout disable_malloc;
 
-      ThreadCapture thread_capture(child, heap);
-      auto list_tids = allocator::vector<pid_t>(heap);
+          ThreadCapture thread_capture(child, heap);
+          auto list_tids = allocator::vector<pid_t>(heap);
 
-      ASSERT_TRUE(thread_capture.ListThreads(list_tids));
-      ASSERT_EQ(threads, list_tids.size());
+          ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+          ASSERT_EQ(threads, list_tids.size());
 
-      ASSERT_TRUE(thread_capture.CaptureThreads());
+          ASSERT_TRUE(thread_capture.CaptureThreads());
 
-      auto thread_info = allocator::vector<ThreadInfo>(heap);
-      ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
-      ASSERT_EQ(threads, thread_info.size());
-      ASSERT_TRUE(thread_capture.ReleaseThreads());
+          auto thread_info = allocator::vector<ThreadInfo>(heap);
+          ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+          ASSERT_EQ(threads, thread_info.size());
+          ASSERT_TRUE(thread_capture.ReleaseThreads());
 
-      if (!HasFailure()) {
-        ASSERT_FALSE(disable_malloc.timed_out());
-      }
-}
-  });
+          if (!HasFailure()) {
+            ASSERT_FALSE(disable_malloc.timed_out());
+          }
+        }
+      });
 }
 
 INSTANTIATE_TEST_CASE_P(ThreadCaptureTest, ThreadCaptureTest, ::testing::Values(1, 2, 10, 1024));
@@ -262,7 +259,7 @@
       ScopedDisableMallocTimeout disable_malloc;
 
       ThreadCapture thread_capture(ret, heap);
-      thread_capture.InjectTestFunc([&](pid_t tid){
+      thread_capture.InjectTestFunc([&](pid_t tid) {
         syscall(SYS_tgkill, ret, tid, SIGKILL);
         usleep(10000);
       });
@@ -288,62 +285,65 @@
   // For signal handler
   static ScopedPipe* g_pipe;
 
-  Fork([&](){
-    // child init
-    pipe.CloseReceiver();
+  Fork(
+      [&]() {
+        // child init
+        pipe.CloseReceiver();
 
-    g_pipe = &pipe;
+        g_pipe = &pipe;
 
-    struct sigaction act{};
-    act.sa_handler = [](int){
-      char buf = '+';
-      write(g_pipe->Sender(), &buf, 1);
-      g_pipe->CloseSender();
-    };
-    sigaction(sig, &act, NULL);
-    sigset_t set;
-    sigemptyset(&set);
-    sigaddset(&set, sig);
-    pthread_sigmask(SIG_UNBLOCK, &set, NULL);
-  },
-  [&](){
-    // child cleanup
-    g_pipe = nullptr;
-    pipe.Close();
-  },
-  [&](pid_t child){
-    // parent
-    ASSERT_GT(child, 0);
-    pipe.CloseSender();
+        struct sigaction act {};
+        act.sa_handler = [](int) {
+          char buf = '+';
+          write(g_pipe->Sender(), &buf, 1);
+          g_pipe->CloseSender();
+        };
+        sigaction(sig, &act, NULL);
+        sigset_t set;
+        sigemptyset(&set);
+        sigaddset(&set, sig);
+        pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+      },
+      [&]() {
+        // child cleanup
+        g_pipe = nullptr;
+        pipe.Close();
+      },
+      [&](pid_t child) {
+        // parent
+        ASSERT_GT(child, 0);
+        pipe.CloseSender();
 
-    {
-      ScopedDisableMallocTimeout disable_malloc;
+        {
+          ScopedDisableMallocTimeout disable_malloc;
 
-      ThreadCapture thread_capture(child, heap);
-      thread_capture.InjectTestFunc([&](pid_t tid){
-        syscall(SYS_tgkill, child, tid, sig);
-        usleep(10000);
+          ThreadCapture thread_capture(child, heap);
+          thread_capture.InjectTestFunc([&](pid_t tid) {
+            syscall(SYS_tgkill, child, tid, sig);
+            usleep(10000);
+          });
+          auto list_tids = allocator::vector<pid_t>(heap);
+
+          ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+          ASSERT_EQ(1U, list_tids.size());
+
+          ASSERT_TRUE(thread_capture.CaptureThreads());
+
+          auto thread_info = allocator::vector<ThreadInfo>(heap);
+          ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+          ASSERT_EQ(1U, thread_info.size());
+          ASSERT_TRUE(thread_capture.ReleaseThreads());
+
+          usleep(100000);
+          char buf;
+          ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(pipe.Receiver(), &buf, 1)));
+          ASSERT_EQ(buf, '+');
+
+          if (!HasFailure()) {
+            ASSERT_FALSE(disable_malloc.timed_out());
+          }
+        }
       });
-      auto list_tids = allocator::vector<pid_t>(heap);
-
-      ASSERT_TRUE(thread_capture.ListThreads(list_tids));
-      ASSERT_EQ(1U, list_tids.size());
-
-      ASSERT_TRUE(thread_capture.CaptureThreads());
-
-      auto thread_info = allocator::vector<ThreadInfo>(heap);
-      ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
-      ASSERT_EQ(1U, thread_info.size());
-      ASSERT_TRUE(thread_capture.ReleaseThreads());
-
-      usleep(100000);
-      char buf;
-      ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(pipe.Receiver(), &buf, 1)));
-      ASSERT_EQ(buf, '+');
-
-      if (!HasFailure()) {
-        ASSERT_FALSE(disable_malloc.timed_out());
-      }
-    }
-  });
 }
+
+}  // namespace android
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 377b7dd..1cea4cd 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -1,4 +1,11 @@
 
+cc_library_headers {
+    name: "libnativebridge-dummy-headers",
+
+    host_supported: true,
+    export_include_dirs=["include"],
+}
+
 cc_library {
     name: "libnativebridge",
 
@@ -7,6 +14,8 @@
     shared_libs: ["liblog"],
     clang: true,
 
+    export_include_dirs=["include"],
+
     cflags: [
         "-Werror",
         "-Wall",
@@ -23,4 +32,4 @@
     },
 }
 
-subdirs = ["tests"]
\ No newline at end of file
+subdirs = ["tests"]
diff --git a/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h
similarity index 96%
rename from include/nativebridge/native_bridge.h
rename to libnativebridge/include/nativebridge/native_bridge.h
index 929b8ae..9bfc935 100644
--- a/include/nativebridge/native_bridge.h
+++ b/libnativebridge/include/nativebridge/native_bridge.h
@@ -161,6 +161,9 @@
 // Use NativeBridgeLoadLibrary() instead in non-namespace scenario.
 void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns);
 
+// Returns vendor namespace if it is enabled for the device and null otherwise
+native_bridge_namespace_t* NativeBridgeGetVendorNamespace();
+
 // Native bridge interfaces to runtime.
 struct NativeBridgeCallbacks {
   // Version number of the interface.
@@ -348,6 +351,15 @@
   // Starting with v3, NativeBridge has two scenarios: with/without namespace.
   // Use loadLibrary instead in non-namespace scenario.
   void* (*loadLibraryExt)(const char* libpath, int flag, native_bridge_namespace_t* ns);
+
+  // Get native bridge version of vendor namespace.
+  // The vendor namespace is the namespace used to load vendor public libraries.
+  // With O release this namespace can be different from the default namespace.
+  // For the devices without enable vendor namespaces this function should return null
+  //
+  // Returns:
+  //   vendor namespace or null if it was not set up for the device
+  native_bridge_namespace_t* (*getVendorNamespace)();
 };
 
 // Runtime interfaces to native bridge.
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 050373a..02b4fe7 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -85,12 +85,14 @@
 // Nativebridge implementation.
 // Used by isCompatibleWith() which is introduced in v2.
 enum NativeBridgeImplementationVersion {
-    // first version, not used.
-    DEFAULT_VERSION = 1,
-    // The version which signal semantic is introduced.
-    SIGNAL_VERSION = 2,
-    // The version which namespace semantic is introduced.
-    NAMESPACE_VERSION = 3,
+  // first version, not used.
+  DEFAULT_VERSION = 1,
+  // The version which signal semantic is introduced.
+  SIGNAL_VERSION = 2,
+  // The version which namespace semantic is introduced.
+  NAMESPACE_VERSION = 3,
+  // The version with vendor namespaces
+  VENDOR_NAMESPACE_VERSION = 4,
 };
 
 // Whether we had an error at some point.
@@ -621,6 +623,14 @@
   return false;
 }
 
+native_bridge_namespace_t* NativeBridgeGetVendorNamespace() {
+  if (!NativeBridgeInitialized() || !isCompatibleWith(VENDOR_NAMESPACE_VERSION)) {
+    return nullptr;
+  }
+
+  return callbacks->getVendorNamespace();
+}
+
 void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) {
   if (NativeBridgeInitialized()) {
     if (isCompatibleWith(NAMESPACE_VERSION)) {
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index efd3978..e31dae0 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -23,6 +23,7 @@
         "-Wextra",
         "-Werror",
     ],
+    header_libs: ["libnativebridge-dummy-headers"],
     cppflags: ["-fvisibility=protected"],
     target: {
         android: {
diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
index 9121277..43c9329 100644
--- a/libnativeloader/include/nativeloader/dlext_namespaces.h
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -124,6 +124,8 @@
  */
 extern void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size);
 
+extern android_namespace_t* android_get_exported_namespace(const char* name);
+
 __END_DECLS
 
 #endif /* __ANDROID_DLEXT_NAMESPACES_H__ */
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index d9cb90d..36a2e44 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -83,6 +83,12 @@
 static constexpr const char* kPublicNativeLibrariesVendorConfig =
                                   "/vendor/etc/public.libraries.txt";
 
+// The device may be configured to have the vendor libraries loaded to a separate namespace.
+// For historical reasons this namespace was named sphal but effectively it is intended
+// to use to load vendor libraries to separate namespace with controlled interface between
+// vendor and system namespaces.
+static constexpr const char* kVendorNamespaceName = "sphal";
+
 // (http://b/27588281) This is a workaround for apps using custom classloaders and calling
 // System.load() with an absolute path which is outside of the classloader library search path.
 // This list includes all directories app is allowed to access this way.
@@ -170,11 +176,23 @@
         return false;
       }
 
-      if (!android_link_namespaces(ns, nullptr, public_libraries_.c_str())) {
+      // Note that when vendor_ns is not configured this function will return nullptr
+      // and it will result in linking vendor_public_libraries_ to the default namespace
+      // which is expected behavior in this case.
+      android_namespace_t* vendor_ns = android_get_exported_namespace(kVendorNamespaceName);
+
+      if (!android_link_namespaces(ns, nullptr, system_public_libraries_.c_str())) {
         *error_msg = dlerror();
         return false;
       }
 
+      if (!vendor_public_libraries_.empty()) {
+        if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
+          *error_msg = dlerror();
+          return false;
+        }
+      }
+
       native_loader_ns = NativeLoaderNamespace(ns);
     } else {
       native_bridge_namespace_t* ns = NativeBridgeCreateNamespace("classloader-namespace",
@@ -183,16 +201,26 @@
                                                                   namespace_type,
                                                                   permitted_path.c_str(),
                                                                   parent_ns.get_native_bridge_ns());
+
       if (ns == nullptr) {
         *error_msg = NativeBridgeGetError();
         return false;
       }
 
-      if (!NativeBridgeLinkNamespaces(ns, nullptr, public_libraries_.c_str())) {
+      native_bridge_namespace_t* vendor_ns = NativeBridgeGetVendorNamespace();
+
+      if (!NativeBridgeLinkNamespaces(ns, nullptr, system_public_libraries_.c_str())) {
         *error_msg = NativeBridgeGetError();
         return false;
       }
 
+      if (!vendor_public_libraries_.empty()) {
+        if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
+          *error_msg = NativeBridgeGetError();
+          return false;
+        }
+      }
+
       native_loader_ns = NativeLoaderNamespace(ns);
     }
 
@@ -249,9 +277,6 @@
       }
     }
 
-    // This file is optional, quietly ignore if the file does not exist.
-    ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames);
-
     // android_init_namespaces() expects all the public libraries
     // to be loaded so that they can be found by soname alone.
     //
@@ -266,7 +291,13 @@
                           soname.c_str(), dlerror());
     }
 
-    public_libraries_ = base::Join(sonames, ':');
+    system_public_libraries_ = base::Join(sonames, ':');
+
+    sonames.clear();
+    // This file is optional, quietly ignore if the file does not exist.
+    ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames);
+
+    vendor_public_libraries_ = base::Join(sonames, ':');
   }
 
   void Reset() {
@@ -325,7 +356,7 @@
     // code is one example) unknown to linker in which  case linker uses anonymous
     // namespace. The second argument specifies the search path for the anonymous
     // namespace which is the library_path of the classloader.
-    initialized_ = android_init_anonymous_namespace(public_libraries_.c_str(),
+    initialized_ = android_init_anonymous_namespace(system_public_libraries_.c_str(),
                                                     is_native_bridge ? nullptr : library_path);
     if (!initialized_) {
       *error_msg = dlerror();
@@ -334,7 +365,7 @@
 
     // and now initialize native bridge namespaces if necessary.
     if (NativeBridgeInitialized()) {
-      initialized_ = NativeBridgeInitAnonymousNamespace(public_libraries_.c_str(),
+      initialized_ = NativeBridgeInitAnonymousNamespace(system_public_libraries_.c_str(),
                                                         is_native_bridge ? library_path : nullptr);
       if (!initialized_) {
         *error_msg = NativeBridgeGetError();
@@ -371,8 +402,8 @@
 
   bool initialized_;
   std::vector<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
-  std::string public_libraries_;
-
+  std::string system_public_libraries_;
+  std::string vendor_public_libraries_;
 
   DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
 };
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 0caf145..9967ef8 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -1,5 +1,6 @@
 cc_library_shared {
     name: "libnetutils",
+    vendor_available: true,
 
     srcs: [
         "dhcpclient.c",
diff --git a/libpixelflinger/codeflinger/mips64_disassem.c b/libpixelflinger/codeflinger/mips64_disassem.c
index f28d726..1856e5c 100644
--- a/libpixelflinger/codeflinger/mips64_disassem.c
+++ b/libpixelflinger/codeflinger/mips64_disassem.c
@@ -555,6 +555,7 @@
     } else {
         vprintf(fmt, argp);
     }
+    va_end(argp);
 }
 
 /*
diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c
index 3007b15..83a9740 100644
--- a/libpixelflinger/codeflinger/mips_disassem.c
+++ b/libpixelflinger/codeflinger/mips_disassem.c
@@ -562,6 +562,7 @@
     } else {
         vprintf(fmt, argp);
     }
+    va_end(argp);
 }
 
 
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 27b4065..9b8248e 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -27,10 +27,12 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include <chrono>
 #include <memory>
 #include <mutex>
+#include <set>
 #include <thread>
 
 #include <android-base/logging.h>
@@ -41,9 +43,6 @@
 
 using namespace std::chrono_literals;
 
-// Uncomment line below use memory cgroups for keeping track of (forked) PIDs
-// #define USE_MEMCG 1
-
 #define MEM_CGROUP_PATH "/dev/memcg/apps"
 #define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks"
 #define ACCT_CGROUP_PATH "/acct"
@@ -89,7 +88,6 @@
 };
 
 static const char* getCgroupRootPath() {
-#ifdef USE_MEMCG
     static const char* cgroup_root_path = NULL;
     std::call_once(init_path_flag, [&]() {
             // Check if mem cgroup is mounted, only then check for write-access to avoid
@@ -98,9 +96,6 @@
                     ACCT_CGROUP_PATH : MEM_CGROUP_PATH;
             });
     return cgroup_root_path;
-#else
-    return ACCT_CGROUP_PATH;
-#endif
 }
 
 static int convertUidToPath(char *path, size_t size, uid_t uid)
@@ -258,6 +253,12 @@
         return -errno;
     }
 
+    // We separate all of the pids in the cgroup into those pids that are also the leaders of
+    // process groups (stored in the pgids set) and those that are not (stored in the pids set).
+    std::set<pid_t> pgids;
+    pgids.emplace(initialPid);
+    std::set<pid_t> pids;
+
     int ret;
     pid_t pid;
     int processes = 0;
@@ -269,8 +270,40 @@
             LOG(WARNING) << "Yikes, we've been told to kill pid 0!  How about we don't do that?";
             continue;
         }
+        pid_t pgid = getpgid(pid);
+        if (pgid == -1) PLOG(ERROR) << "getpgid(" << pid << ") failed";
+        if (pgid == pid) {
+            pgids.emplace(pid);
+        } else {
+            pids.emplace(pid);
+        }
+    }
+
+    // Erase all pids that will be killed when we kill the process groups.
+    for (auto it = pids.begin(); it != pids.end();) {
+        pid_t pgid = getpgid(pid);
+        if (pgids.count(pgid) == 1) {
+            it = pids.erase(it);
+        } else {
+            ++it;
+        }
+    }
+
+    // Kill all process groups.
+    for (const auto pgid : pgids) {
+        LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
+                     << " as part of process cgroup " << initialPid;
+
+        if (kill(-pgid, signal) == -1) {
+            PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed";
+        }
+    }
+
+    // Kill remaining pids.
+    for (const auto pid : pids) {
         LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
                      << initialPid;
+
         if (kill(pid, signal) == -1) {
             PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
         }
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index c13ffe9..aedaa38 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -22,6 +22,7 @@
 
 cc_library {
     name: "libprocinfo",
+    vendor_available: true,
     host_supported: true,
     srcs: [
         "process.cpp",
diff --git a/libprocinfo/include/procinfo/process.h b/libprocinfo/include/procinfo/process.h
index fb140ff..db56fc1 100644
--- a/libprocinfo/include/procinfo/process.h
+++ b/libprocinfo/include/procinfo/process.h
@@ -35,8 +35,18 @@
 
 #if defined(__linux__)
 
+enum ProcessState {
+  kProcessStateUnknown,
+  kProcessStateRunning,
+  kProcessStateSleeping,
+  kProcessStateUninterruptibleWait,
+  kProcessStateStopped,
+  kProcessStateZombie,
+};
+
 struct ProcessInfo {
   std::string name;
+  ProcessState state;
   pid_t tid;
   pid_t pid;
   pid_t ppid;
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp
index c513e16..6e5be6e 100644
--- a/libprocinfo/process.cpp
+++ b/libprocinfo/process.cpp
@@ -44,6 +44,24 @@
   return GetProcessInfoFromProcPidFd(dirfd.get(), process_info);
 }
 
+static ProcessState parse_state(const char* state) {
+  switch (*state) {
+    case 'R':
+      return kProcessStateRunning;
+    case 'S':
+      return kProcessStateSleeping;
+    case 'D':
+      return kProcessStateUninterruptibleWait;
+    case 'T':
+      return kProcessStateStopped;
+    case 'Z':
+      return kProcessStateZombie;
+    default:
+      LOG(ERROR) << "unknown process state: " << *state;
+      return kProcessStateUnknown;
+  }
+}
+
 bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info) {
   int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC);
 
@@ -60,7 +78,7 @@
   }
 
   int field_bitmap = 0;
-  static constexpr int finished_bitmap = 127;
+  static constexpr int finished_bitmap = 255;
   char* line = nullptr;
   size_t len = 0;
 
@@ -98,6 +116,9 @@
     } else if (header == "Gid:") {
       process_info->gid = atoi(tab + 1);
       field_bitmap |= 64;
+    } else if (header == "State:") {
+      process_info->state = parse_state(tab + 1);
+      field_bitmap |= 128;
     }
   }
 
diff --git a/libprocinfo/process_test.cpp b/libprocinfo/process_test.cpp
index 5ffd236..9da9278 100644
--- a/libprocinfo/process_test.cpp
+++ b/libprocinfo/process_test.cpp
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <set>
 #include <thread>
 #include <vector>
@@ -29,6 +30,8 @@
 
 #include <android-base/stringprintf.h>
 
+using namespace std::chrono_literals;
+
 #if !defined(__BIONIC__)
 #include <syscall.h>
 static pid_t gettid() {
@@ -82,3 +85,34 @@
     }
   }).join();
 }
+
+TEST(process_info, process_state) {
+  int pipefd[2];
+  ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
+  pid_t forkpid = fork();
+
+  ASSERT_NE(-1, forkpid);
+  if (forkpid == 0) {
+    close(pipefd[1]);
+    char buf;
+    TEMP_FAILURE_RETRY(read(pipefd[0], &buf, 1));
+    _exit(0);
+  }
+
+  // Give the child some time to get to the read.
+  std::this_thread::sleep_for(100ms);
+
+  android::procinfo::ProcessInfo procinfo;
+  ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo));
+  ASSERT_EQ(android::procinfo::kProcessStateSleeping, procinfo.state);
+
+  ASSERT_EQ(0, kill(forkpid, SIGKILL));
+
+  // Give the kernel some time to kill the child.
+  std::this_thread::sleep_for(100ms);
+
+  ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo));
+  ASSERT_EQ(android::procinfo::kProcessStateZombie, procinfo.state);
+
+  ASSERT_EQ(forkpid, waitpid(forkpid, nullptr, 0));
+}
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
index d27ceea..7883151 100644
--- a/libsuspend/Android.bp
+++ b/libsuspend/Android.bp
@@ -2,6 +2,8 @@
 
 cc_library {
     name: "libsuspend",
+    vendor_available: true,
+
     srcs: [
         "autosuspend.c",
         "autosuspend_autosleep.c",
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 296bd26..550ef42 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -1,5 +1,7 @@
 cc_library_shared {
     name: "libsysutils",
+    vendor_available: true,
+
     srcs: [
         "src/SocketListener.cpp",
         "src/FrameworkListener.cpp",
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index 1b6076f..87e2684 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -68,7 +68,7 @@
         android_errorWriteLog(0x534e4554, "29831647");
         c->sendMsg(500, "Command too large for buffer", false);
         mSkipToNextNullByte = true;
-        return false;
+        return true;
     }
 
     int offset = 0;
@@ -211,7 +211,6 @@
     return;
 
 overflow:
-    LOG_EVENT_INT(78001, cli->getUid());
     cli->sendMsg(500, "Command too long", false);
     goto out;
 }
diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp
index 896dad3..aad0394 100644
--- a/libsysutils/src/NetlinkListener.cpp
+++ b/libsysutils/src/NetlinkListener.cpp
@@ -57,8 +57,6 @@
     count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,
             mBuffer, sizeof(mBuffer), require_group, &uid));
     if (count < 0) {
-        if (uid > 0)
-            LOG_EVENT_INT(65537, uid);
         SLOGE("recvmsg failed (%s)", strerror(errno));
         return false;
     }
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 32ed6c3..94f0f8e 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -31,63 +31,61 @@
         },
     },
 
-    multilib: {
-        lib32: {
-            suffix: "32",
+    arch: {
+        mips: {
+            enabled: false,
         },
-        lib64: {
-            suffix: "64",
+        mips64: {
+            enabled: false,
         },
     },
 }
 
-cc_defaults {
-    name: "libunwindstack_common",
+cc_library {
+    name: "libunwindstack",
     defaults: ["libunwindstack_flags"],
+    export_include_dirs: ["include"],
 
     srcs: [
         "ArmExidx.cpp",
         "DwarfCfa.cpp",
+        "DwarfDebugFrame.cpp",
+        "DwarfEhFrame.cpp",
         "DwarfMemory.cpp",
         "DwarfOp.cpp",
+        "DwarfSection.cpp",
         "Elf.cpp",
         "ElfInterface.cpp",
         "ElfInterfaceArm.cpp",
         "Log.cpp",
-        "Regs.cpp",
         "MapInfo.cpp",
         "Maps.cpp",
         "Memory.cpp",
+        "Regs.cpp",
         "Symbols.cpp",
     ],
 
+    arch: {
+        x86: {
+            srcs: ["AsmGetRegsX86.S"],
+        },
+        x86_64: {
+            srcs: ["AsmGetRegsX86_64.S"],
+        },
+    },
+
     shared_libs: [
         "libbase",
         "liblog",
-    ],
-}
-
-cc_library {
-    name: "libunwindstack",
-    defaults: ["libunwindstack_common"],
-}
-
-cc_library {
-    name: "libunwindstack_debug",
-    defaults: ["libunwindstack_common"],
-
-    cflags: [
-        "-UNDEBUG",
-        "-O0",
-        "-g",
+        "liblzma",
     ],
 }
 
 //-------------------------------------------------------------------------
 // Unit Tests
 //-------------------------------------------------------------------------
-cc_defaults {
-    name: "libunwindstack_test_common",
+cc_test {
+    name: "libunwindstack_test",
     defaults: ["libunwindstack_flags"],
 
     srcs: [
@@ -95,22 +93,31 @@
         "tests/ArmExidxExtractTest.cpp",
         "tests/DwarfCfaLogTest.cpp",
         "tests/DwarfCfaTest.cpp",
+        "tests/DwarfDebugFrameTest.cpp",
+        "tests/DwarfEhFrameTest.cpp",
         "tests/DwarfMemoryTest.cpp",
         "tests/DwarfOpLogTest.cpp",
         "tests/DwarfOpTest.cpp",
+        "tests/DwarfSectionTest.cpp",
+        "tests/DwarfSectionImplTest.cpp",
         "tests/ElfInterfaceArmTest.cpp",
         "tests/ElfInterfaceTest.cpp",
         "tests/ElfTest.cpp",
+        "tests/ElfTestUtils.cpp",
         "tests/LogFake.cpp",
-        "tests/MapInfoTest.cpp",
+        "tests/MapInfoCreateMemoryTest.cpp",
+        "tests/MapInfoGetElfTest.cpp",
         "tests/MapsTest.cpp",
+        "tests/MemoryBufferTest.cpp",
         "tests/MemoryFake.cpp",
         "tests/MemoryFileTest.cpp",
         "tests/MemoryLocalTest.cpp",
         "tests/MemoryRangeTest.cpp",
         "tests/MemoryRemoteTest.cpp",
+        "tests/MemoryTest.cpp",
         "tests/RegsTest.cpp",
         "tests/SymbolsTest.cpp",
+        "tests/UnwindTest.cpp",
     ],
 
     cflags: [
@@ -121,6 +128,48 @@
     shared_libs: [
         "libbase",
         "liblog",
+        "liblzma",
+        "libunwindstack",
+    ],
+
+    static_libs: [
+        "libgmock",
+    ],
+
+    target: {
+        linux: {
+            host_ldlibs: [
+                "-lrt",
+            ],
+        },
+    },
+
+    data: [
+        "tests/files/elf32.xz",
+        "tests/files/elf64.xz",
+    ],
+}
+
+//-------------------------------------------------------------------------
+// Tools
+//-------------------------------------------------------------------------
+cc_defaults {
+    name: "libunwindstack_tools",
+    defaults: ["libunwindstack_flags"],
+
+    shared_libs: [
+        "libunwindstack",
+        "libbase",
+        "liblzma",
+    ],
+}
+
+cc_binary {
+    name: "unwind",
+    defaults: ["libunwindstack_tools"],
+
+    srcs: [
+        "tools/unwind.cpp",
     ],
 
     target: {
@@ -132,50 +181,31 @@
     },
 }
 
-// These unit tests run against the shared library.
-cc_test {
-    name: "libunwindstack_test",
-    defaults: ["libunwindstack_test_common"],
+cc_binary {
+    name: "unwind_info",
+    defaults: ["libunwindstack_tools"],
 
-    shared_libs: [
-        "libunwindstack",
+    srcs: [
+        "tools/unwind_info.cpp",
     ],
 }
 
-// These unit tests run against the static debug library.
-cc_test {
-    name: "libunwindstack_test_debug",
-    defaults: ["libunwindstack_test_common"],
-
-    static_libs: [
-        "libunwindstack_debug",
-    ],
-}
-
-//-------------------------------------------------------------------------
-// Utility Executables
-//-------------------------------------------------------------------------
-cc_defaults {
-    name: "libunwindstack_executables",
-    defaults: ["libunwindstack_flags"],
-
-    shared_libs: [
-        "libunwindstack",
-        "libbase",
-    ],
-
-    static_libs: [
-        "liblog",
-    ],
-
-    compile_multilib: "both",
-}
-
 cc_binary {
-    name: "unwind_info",
-    defaults: ["libunwindstack_executables"],
+    name: "unwind_symbols",
+    defaults: ["libunwindstack_tools"],
 
     srcs: [
-        "unwind_info.cpp",
+        "tools/unwind_symbols.cpp",
+    ],
+}
+
+// Generates the elf data for use in the tests for .gnu_debugdata frames.
+// Once these files are generated, use the xz command to compress the data.
+cc_binary_host {
+    name: "gen_gnudebugdata",
+    defaults: ["libunwindstack_flags"],
+
+    srcs: [
+        "tests/GenGnuDebugdata.cpp",
     ],
 }
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index 12adf57..fed3e0e 100644
--- a/libunwindstack/ArmExidx.cpp
+++ b/libunwindstack/ArmExidx.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <assert.h>
 #include <stdint.h>
 
 #include <deque>
@@ -22,11 +21,15 @@
 
 #include <android-base/stringprintf.h>
 
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
 #include "ArmExidx.h"
-#include "Log.h"
+#include "Check.h"
 #include "Machine.h"
-#include "Memory.h"
-#include "Regs.h"
+
+namespace unwindstack {
 
 void ArmExidx::LogRawData() {
   std::string log_str("Raw Data:");
@@ -173,7 +176,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_10_00(uint8_t byte) {
-  assert((byte >> 4) == 0x8);
+  CHECK((byte >> 4) == 0x8);
 
   uint16_t registers = (byte & 0xf) << 8;
   if (!GetByte(&byte)) {
@@ -232,7 +235,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_10_01(uint8_t byte) {
-  assert((byte >> 4) == 0x9);
+  CHECK((byte >> 4) == 0x9);
 
   uint8_t bits = byte & 0xf;
   if (bits == 13 || bits == 15) {
@@ -258,7 +261,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_10_10(uint8_t byte) {
-  assert((byte >> 4) == 0xa);
+  CHECK((byte >> 4) == 0xa);
 
   // 10100nnn: Pop r4-r[4+nnn]
   // 10101nnn: Pop r4-r[4+nnn], r14
@@ -419,7 +422,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_10_11_1nnn(uint8_t byte) {
-  assert((byte & ~0x07) == 0xb8);
+  CHECK((byte & ~0x07) == 0xb8);
 
   // 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
   if (log_) {
@@ -439,7 +442,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_10(uint8_t byte) {
-  assert((byte >> 6) == 0x2);
+  CHECK((byte >> 6) == 0x2);
 
   switch ((byte >> 4) & 0x3) {
   case 0:
@@ -469,7 +472,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_11_000(uint8_t byte) {
-  assert((byte & ~0x07) == 0xc0);
+  CHECK((byte & ~0x07) == 0xc0);
 
   uint8_t bits = byte & 0x7;
   if (bits == 6) {
@@ -550,7 +553,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_11_001(uint8_t byte) {
-  assert((byte & ~0x07) == 0xc8);
+  CHECK((byte & ~0x07) == 0xc8);
 
   uint8_t bits = byte & 0x7;
   if (bits == 0) {
@@ -605,7 +608,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_11_010(uint8_t byte) {
-  assert((byte & ~0x07) == 0xd0);
+  CHECK((byte & ~0x07) == 0xd0);
 
   // 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
   if (log_) {
@@ -624,7 +627,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_11(uint8_t byte) {
-  assert((byte >> 6) == 0x3);
+  CHECK((byte >> 6) == 0x3);
 
   switch ((byte >> 3) & 0x7) {
   case 0:
@@ -684,3 +687,5 @@
   while (Decode());
   return status_ == ARM_STATUS_FINISH;
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h
index 8c7f15a..f4361d4 100644
--- a/libunwindstack/ArmExidx.h
+++ b/libunwindstack/ArmExidx.h
@@ -21,6 +21,8 @@
 
 #include <deque>
 
+namespace unwindstack {
+
 // Forward declarations.
 class Memory;
 class RegsArm;
@@ -105,4 +107,6 @@
   bool pc_set_ = false;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_ARM_EXIDX_H
diff --git a/libunwindstack/AsmGetRegsX86.S b/libunwindstack/AsmGetRegsX86.S
new file mode 100644
index 0000000..14927a3
--- /dev/null
+++ b/libunwindstack/AsmGetRegsX86.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+  .text
+  .global AsmGetRegs
+  .balign 16
+  .type AsmGetRegs, @function
+AsmGetRegs:
+  .cfi_startproc
+  mov 4(%esp), %eax
+  movl $0, (%eax)
+  movl %ecx, 4(%eax)
+  movl %edx, 8(%eax)
+  movl %ebx, 12(%eax)
+
+  /* ESP */
+  leal 4(%esp), %ecx
+  movl %ecx, 16(%eax)
+
+  movl %ebp, 20(%eax)
+  movl %esi, 24(%eax)
+  movl %edi, 28(%eax)
+
+  /* EIP */
+  movl (%esp), %ecx
+  movl %ecx, 32(%eax)
+
+  movl %cs, 36(%eax)
+  movl %ss, 40(%eax)
+  movl %ds, 44(%eax)
+  movl %es, 48(%eax)
+  movl %fs, 52(%eax)
+  movl %gs, 56(%eax)
+  ret
+
+  .cfi_endproc
+  .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/AsmGetRegsX86_64.S b/libunwindstack/AsmGetRegsX86_64.S
new file mode 100644
index 0000000..4cd3b6f
--- /dev/null
+++ b/libunwindstack/AsmGetRegsX86_64.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+  .text
+  .global AsmGetRegs
+  .balign 16
+  .type AsmGetRegs, @function
+AsmGetRegs:
+  .cfi_startproc
+  movq %rax, (%rdi)
+  movq %rdx, 8(%rdi)
+  movq %rcx, 16(%rdi)
+  movq %rbx, 24(%rdi)
+  movq %rsi, 32(%rdi)
+  movq %rdi, 40(%rdi)
+  movq %rbp, 48(%rdi)
+
+  /* RSP */
+  lea 8(%rsp), %rax
+  movq %rax, 56(%rdi)
+
+  movq %r8, 64(%rdi)
+  movq %r9, 72(%rdi)
+  movq %r10, 80(%rdi)
+  movq %r11, 88(%rdi)
+  movq %r12, 96(%rdi)
+  movq %r13, 104(%rdi)
+  movq %r14, 112(%rdi)
+  movq %r15, 120(%rdi)
+
+  /* RIP */
+  movq (%rsp), %rax
+  movq %rax, 128(%rdi)
+  ret
+
+  .cfi_endproc
+  .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/Check.h b/libunwindstack/Check.h
new file mode 100644
index 0000000..2d216d7
--- /dev/null
+++ b/libunwindstack/Check.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_ERROR_H
+#define _LIBUNWINDSTACK_ERROR_H
+
+#include <stdlib.h>
+
+#include <unwindstack/Log.h>
+
+namespace unwindstack {
+
+#define CHECK(assertion)                                   \
+  if (__builtin_expect(!(assertion), false)) {             \
+    log(0, "%s:%d: %s\n", __FILE__, __LINE__, #assertion); \
+    abort();                                               \
+  }
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_ERROR_H
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 006f039..b1d55ba 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -23,12 +23,15 @@
 
 #include <android-base/stringprintf.h>
 
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/Log.h>
+
 #include "DwarfCfa.h"
 #include "DwarfEncoding.h"
-#include "DwarfMemory.h"
+#include "DwarfError.h"
 #include "DwarfOp.h"
-#include "DwarfStructs.h"
-#include "Log.h"
+
+namespace unwindstack {
 
 template <typename AddressType>
 constexpr typename DwarfCfa<AddressType>::process_func DwarfCfa<AddressType>::kCallbackTable[64];
@@ -711,3 +714,5 @@
 // Explicitly instantiate DwarfCfa.
 template class DwarfCfa<uint32_t>;
 template class DwarfCfa<uint64_t>;
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index ce7da4a..62b9b7a 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -24,10 +24,13 @@
 #include <type_traits>
 #include <vector>
 
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+
 #include "DwarfError.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
-#include "DwarfStructs.h"
+
+namespace unwindstack {
 
 // DWARF Standard home: http://dwarfstd.org/
 // This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
@@ -63,7 +66,7 @@
   typedef typename std::make_signed<AddressType>::type SignedType;
 
  public:
-  DwarfCfa(DwarfMemory* memory, const DwarfFDE* fde) : memory_(memory), fde_(fde) {}
+  DwarfCfa(DwarfMemory* memory, const DwarfFde* fde) : memory_(memory), fde_(fde) {}
   virtual ~DwarfCfa() = default;
 
   bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
@@ -88,7 +91,7 @@
  private:
   DwarfError last_error_;
   DwarfMemory* memory_;
-  const DwarfFDE* fde_;
+  const DwarfFde* fde_;
 
   AddressType cur_pc_;
   const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
@@ -252,4 +255,6 @@
   };
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_CFA_H
diff --git a/libunwindstack/DwarfDebugFrame.cpp b/libunwindstack/DwarfDebugFrame.cpp
new file mode 100644
index 0000000..5707596
--- /dev/null
+++ b/libunwindstack/DwarfDebugFrame.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+#include <stdlib.h>
+
+#include <algorithm>
+
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Memory.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+namespace unwindstack {
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
+  offset_ = offset;
+  end_offset_ = offset + size;
+
+  memory_.clear_func_offset();
+  memory_.clear_text_offset();
+  memory_.set_data_offset(offset);
+  memory_.set_cur_offset(offset);
+
+  return CreateSortedFdeList();
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) {
+  uint8_t version;
+  if (!memory_.ReadBytes(&version, 1)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    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_ = DWARF_ERROR_MEMORY_INVALID;
+      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_ = DWARF_ERROR_MEMORY_INVALID;
+      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_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+  } while (value & 0x80);
+
+  // Skip data alignment factor
+  do {
+    if (!memory_.ReadBytes(&value, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      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_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+    } while (value & 0x80);
+  }
+
+  // Skip the augmentation length.
+  do {
+    if (!memory_.ReadBytes(&value, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      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_ = DWARF_ERROR_MEMORY_INVALID;
+        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_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+      uint64_t value;
+      if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
+        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+    }
+  }
+
+  // It should be impossible to get here.
+  abort();
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<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;
+  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  uint64_t length;
+  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  if (length != 0) {
+    fdes_.emplace_back(entry_offset, start, length);
+  }
+
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::CreateSortedFdeList() {
+  memory_.set_cur_offset(offset_);
+
+  // 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() < end_offset_) {
+    uint64_t cur_entry_offset = memory_.cur_offset();
+
+    // Figure out the entry length and type.
+    uint32_t value32;
+    if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      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_ = DWARF_ERROR_MEMORY_INVALID;
+        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_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+
+      if (value64 == static_cast<uint64_t>(-1)) {
+        // Cie 64 bit
+        address_encoding = DW_EH_PE_sdata8;
+        if (!GetCieInfo(&segment_size, &address_encoding)) {
+          return false;
+        }
+        cie_offset = cur_entry_offset;
+      } else {
+        if (offset_ + value64 != cie_offset) {
+          // This means that this Fde is not following the Cie.
+          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+          return false;
+        }
+
+        // Fde 64 bit
+        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
+          return false;
+        }
+      }
+    } 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_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+
+      if (value32 == static_cast<uint32_t>(-1)) {
+        // Cie 32 bit
+        address_encoding = DW_EH_PE_sdata4;
+        if (!GetCieInfo(&segment_size, &address_encoding)) {
+          return false;
+        }
+        cie_offset = cur_entry_offset;
+      } else {
+        if (offset_ + value32 != cie_offset) {
+          // This means that this Fde is not following the Cie.
+          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+          return false;
+        }
+
+        // Fde 32 bit
+        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
+          return false;
+        }
+      }
+    }
+
+    if (next_entry_offset < memory_.cur_offset()) {
+      // This indicates some kind of corruption, or malformed section data.
+      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+    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;
+  });
+
+  fde_count_ = fdes_.size();
+
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<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;
+    } else {
+      first = current + 1;
+    }
+  }
+  return false;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfDebugFrame<AddressType>::GetFdeFromIndex(size_t index) {
+  if (index >= fdes_.size()) {
+    return nullptr;
+  }
+  return this->GetFdeFromOffset(fdes_[index].offset);
+}
+
+// Explicitly instantiate DwarfDebugFrame.
+template class DwarfDebugFrame<uint32_t>;
+template class DwarfDebugFrame<uint64_t>;
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
new file mode 100644
index 0000000..6a6178e
--- /dev/null
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
+#define _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
+
+template <typename AddressType>
+class DwarfDebugFrame : 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>::last_error_;
+
+  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;
+  };
+
+  DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  virtual ~DwarfDebugFrame() = default;
+
+  bool Init(uint64_t offset, uint64_t size) override;
+
+  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+
+  const DwarfFde* GetFdeFromIndex(size_t index) override;
+
+  bool IsCie32(uint32_t value32) override { return value32 == static_cast<uint32_t>(-1); }
+
+  bool IsCie64(uint64_t value64) override { return value64 == static_cast<uint64_t>(-1); }
+
+  uint64_t GetCieOffsetFromFde32(uint32_t pointer) override { return offset_ + pointer; }
+
+  uint64_t GetCieOffsetFromFde64(uint64_t pointer) override { return offset_ + pointer; }
+
+  uint64_t AdjustPcFromFde(uint64_t pc) override { return 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();
+
+ protected:
+  uint64_t offset_;
+  uint64_t end_offset_;
+
+  std::vector<FdeInfo> fdes_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
diff --git a/libunwindstack/DwarfEhFrame.cpp b/libunwindstack/DwarfEhFrame.cpp
new file mode 100644
index 0000000..d0b35c3
--- /dev/null
+++ b/libunwindstack/DwarfEhFrame.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+#include "DwarfEhFrame.h"
+#include "DwarfError.h"
+
+namespace unwindstack {
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
+  uint8_t data[4];
+
+  memory_.clear_func_offset();
+  memory_.clear_text_offset();
+  memory_.set_data_offset(offset);
+  memory_.set_cur_offset(offset);
+
+  // Read the first four bytes all at once.
+  if (!memory_.ReadBytes(data, 4)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  version_ = data[0];
+  if (version_ != 1) {
+    // Unknown version.
+    last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
+    return false;
+  }
+
+  ptr_encoding_ = data[1];
+  uint8_t fde_count_encoding = data[2];
+  table_encoding_ = data[3];
+  table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
+
+  memory_.set_pc_offset(memory_.cur_offset());
+  if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  memory_.set_pc_offset(memory_.cur_offset());
+  if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  entries_offset_ = memory_.cur_offset();
+  entries_end_ = offset + size;
+  entries_data_offset_ = offset;
+  cur_entries_offset_ = entries_offset_;
+
+  return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfEhFrame<AddressType>::GetFdeFromIndex(size_t index) {
+  const FdeInfo* info = GetFdeInfoFromIndex(index);
+  if (info == nullptr) {
+    return nullptr;
+  }
+  return this->GetFdeFromOffset(info->offset);
+}
+
+template <typename AddressType>
+const typename DwarfEhFrame<AddressType>::FdeInfo* DwarfEhFrame<AddressType>::GetFdeInfoFromIndex(
+    size_t index) {
+  auto entry = fde_info_.find(index);
+  if (entry != fde_info_.end()) {
+    return &fde_info_[index];
+  }
+  FdeInfo* info = &fde_info_[index];
+
+  memory_.set_data_offset(entries_data_offset_);
+  memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
+  memory_.set_pc_offset(memory_.cur_offset());
+  uint64_t value;
+  if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
+      !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    fde_info_.erase(index);
+    return nullptr;
+  }
+  info->pc = value;
+  return info;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
+                                                   uint64_t total_entries) {
+  CHECK(fde_count_ > 0);
+  CHECK(total_entries <= fde_count_);
+
+  size_t first = 0;
+  size_t last = total_entries;
+  while (first < last) {
+    size_t current = (first + last) / 2;
+    const FdeInfo* info = GetFdeInfoFromIndex(current);
+    if (pc == info->pc) {
+      *fde_offset = info->offset;
+      return true;
+    }
+    if (pc < info->pc) {
+      last = current;
+    } else {
+      first = current + 1;
+    }
+  }
+  if (last != 0) {
+    const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
+    *fde_offset = info->offset;
+    return true;
+  }
+  return false;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
+  CHECK(fde_count_ != 0);
+  last_error_ = DWARF_ERROR_NONE;
+  // We can do a binary search if the pc is in the range of the elements
+  // that have already been cached.
+  if (!fde_info_.empty()) {
+    const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
+    if (pc >= info->pc) {
+      *fde_offset = info->offset;
+      return true;
+    }
+    if (pc < info->pc) {
+      return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
+    }
+  }
+
+  if (cur_entries_offset_ == 0) {
+    // All entries read, or error encountered.
+    return false;
+  }
+
+  memory_.set_data_offset(entries_data_offset_);
+  memory_.set_cur_offset(cur_entries_offset_);
+  cur_entries_offset_ = 0;
+
+  FdeInfo* prev_info = nullptr;
+  for (size_t current = fde_info_.size();
+       current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
+    memory_.set_pc_offset(memory_.cur_offset());
+    uint64_t value;
+    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+
+    FdeInfo* info = &fde_info_[current];
+    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+      fde_info_.erase(current);
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    info->pc = value;
+
+    if (pc < info->pc) {
+      if (prev_info == nullptr) {
+        return false;
+      }
+      cur_entries_offset_ = memory_.cur_offset();
+      *fde_offset = prev_info->offset;
+      return true;
+    }
+    prev_info = info;
+  }
+
+  if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
+    *fde_offset = prev_info->offset;
+    return true;
+  }
+  return false;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+  if (fde_count_ == 0) {
+    return false;
+  }
+
+  if (table_entry_size_ > 0) {
+    // Do a binary search since the size of each table entry is fixed.
+    return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
+  } else {
+    // Do a sequential search since each table entry size is variable.
+    return GetFdeOffsetSequential(pc, fde_offset);
+  }
+}
+
+// Explicitly instantiate DwarfEhFrame.
+template class DwarfEhFrame<uint32_t>;
+template class DwarfEhFrame<uint64_t>;
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
new file mode 100644
index 0000000..4207b42
--- /dev/null
+++ b/libunwindstack/DwarfEhFrame.h
@@ -0,0 +1,93 @@
+/*
+ * 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 _LIBUNWINDSTACK_DWARF_EH_FRAME_H
+#define _LIBUNWINDSTACK_DWARF_EH_FRAME_H
+
+#include <stdint.h>
+
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+template <typename AddressType>
+class DwarfEhFrame : 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>::last_error_;
+
+  struct FdeInfo {
+    AddressType pc;
+    uint64_t offset;
+  };
+
+  DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  virtual ~DwarfEhFrame() = default;
+
+  bool Init(uint64_t offset, uint64_t size) override;
+
+  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+
+  const DwarfFde* GetFdeFromIndex(size_t index) override;
+
+  bool IsCie32(uint32_t value32) override { return value32 == 0; }
+
+  bool IsCie64(uint64_t value64) override { return value64 == 0; }
+
+  uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
+    return memory_.cur_offset() - pointer - 4;
+  }
+
+  uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
+    return memory_.cur_offset() - pointer - 8;
+  }
+
+  uint64_t AdjustPcFromFde(uint64_t pc) override {
+    // The eh_frame uses relative pcs.
+    return pc + memory_.cur_offset();
+  }
+
+  const FdeInfo* GetFdeInfoFromIndex(size_t index);
+
+  bool GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset);
+
+  bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries);
+
+ protected:
+  uint8_t version_;
+  uint8_t ptr_encoding_;
+  uint8_t table_encoding_;
+  size_t table_entry_size_;
+
+  uint64_t ptr_offset_;
+
+  uint64_t entries_offset_;
+  uint64_t entries_end_;
+  uint64_t entries_data_offset_;
+  uint64_t cur_entries_offset_ = 0;
+
+  std::unordered_map<uint64_t, FdeInfo> fde_info_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
diff --git a/libunwindstack/DwarfEncoding.h b/libunwindstack/DwarfEncoding.h
index 0ff3b8c..20db222 100644
--- a/libunwindstack/DwarfEncoding.h
+++ b/libunwindstack/DwarfEncoding.h
@@ -19,6 +19,8 @@
 
 #include <stdint.h>
 
+namespace unwindstack {
+
 enum DwarfEncoding : uint8_t {
   DW_EH_PE_omit = 0xff,
 
@@ -44,4 +46,6 @@
   DW_EH_PE_block = 0x0f,
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_ENCODING_H
diff --git a/libunwindstack/DwarfError.h b/libunwindstack/DwarfError.h
index 824c307..54199b8 100644
--- a/libunwindstack/DwarfError.h
+++ b/libunwindstack/DwarfError.h
@@ -19,6 +19,8 @@
 
 #include <stdint.h>
 
+namespace unwindstack {
+
 enum DwarfError : uint8_t {
   DWARF_ERROR_NONE,
   DWARF_ERROR_MEMORY_INVALID,
@@ -27,6 +29,10 @@
   DWARF_ERROR_STACK_INDEX_NOT_VALID,
   DWARF_ERROR_NOT_IMPLEMENTED,
   DWARF_ERROR_TOO_MANY_ITERATIONS,
+  DWARF_ERROR_CFA_NOT_DEFINED,
+  DWARF_ERROR_UNSUPPORTED_VERSION,
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
index 11806ea..b6e0412 100644
--- a/libunwindstack/DwarfMemory.cpp
+++ b/libunwindstack/DwarfMemory.cpp
@@ -14,14 +14,17 @@
  * limitations under the License.
  */
 
-#include <assert.h>
 #include <stdint.h>
 
 #include <string>
 
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
 #include "DwarfEncoding.h"
-#include "DwarfMemory.h"
-#include "Memory.h"
+
+namespace unwindstack {
 
 bool DwarfMemory::ReadBytes(void* dst, size_t num_bytes) {
   if (!memory_->Read(cur_offset_, dst, num_bytes)) {
@@ -100,8 +103,8 @@
 }
 
 bool DwarfMemory::AdjustEncodedValue(uint8_t encoding, uint64_t* value) {
-  assert((encoding & 0x0f) == 0);
-  assert(encoding != DW_EH_PE_aligned);
+  CHECK((encoding & 0x0f) == 0);
+  CHECK(encoding != DW_EH_PE_aligned);
 
   // Handle the encoding.
   switch (encoding) {
@@ -246,3 +249,5 @@
 
 template bool DwarfMemory::ReadEncodedValue<uint32_t>(uint8_t, uint64_t*);
 template bool DwarfMemory::ReadEncodedValue<uint64_t>(uint8_t, uint64_t*);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
index 507ca08..b3fd0df 100644
--- a/libunwindstack/DwarfOp.cpp
+++ b/libunwindstack/DwarfOp.cpp
@@ -22,12 +22,15 @@
 
 #include <android-base/stringprintf.h>
 
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
 #include "DwarfError.h"
-#include "DwarfMemory.h"
 #include "DwarfOp.h"
-#include "Log.h"
-#include "Memory.h"
-#include "Regs.h"
+
+namespace unwindstack {
 
 template <typename AddressType>
 constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256];
@@ -460,3 +463,5 @@
 // Explicitly instantiate DwarfOp.
 template class DwarfOp<uint32_t>;
 template class DwarfOp<uint64_t>;
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
index ed6537a..c29bf35 100644
--- a/libunwindstack/DwarfOp.h
+++ b/libunwindstack/DwarfOp.h
@@ -25,6 +25,9 @@
 #include <vector>
 
 #include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+namespace unwindstack {
 
 enum DwarfVersion : uint8_t {
   DWARF_VERSION_2 = 2,
@@ -37,7 +40,7 @@
 class DwarfMemory;
 class Memory;
 template <typename AddressType>
-class RegsTmpl;
+class RegsImpl;
 
 template <typename AddressType>
 class DwarfOp {
@@ -67,7 +70,7 @@
   AddressType StackAt(size_t index) { return stack_[index]; }
   size_t StackSize() { return stack_.size(); }
 
-  void set_regs(RegsTmpl<AddressType>* regs) { regs_ = regs; }
+  void set_regs(RegsImpl<AddressType>* regs) { regs_ = regs; }
 
   DwarfError last_error() { return last_error_; }
 
@@ -91,7 +94,7 @@
   DwarfMemory* memory_;
   Memory* regular_memory_;
 
-  RegsTmpl<AddressType>* regs_;
+  RegsImpl<AddressType>* regs_;
   bool is_register_ = false;
   DwarfError last_error_ = DWARF_ERROR_NONE;
   uint8_t cur_op_;
@@ -1633,4 +1636,6 @@
   };
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_OP_H
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
new file mode 100644
index 0000000..1234eb1
--- /dev/null
+++ b/libunwindstack/DwarfSection.cpp
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "DwarfCfa.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+#include "DwarfOp.h"
+
+namespace unwindstack {
+
+DwarfSection::DwarfSection(Memory* memory) : memory_(memory), last_error_(DWARF_ERROR_NONE) {}
+
+const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
+  uint64_t fde_offset;
+  if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
+    return nullptr;
+  }
+  const DwarfFde* fde = GetFdeFromOffset(fde_offset);
+  // Guaranteed pc >= pc_start, need to check pc in the fde range.
+  if (pc < fde->pc_end) {
+    return fde;
+  }
+  last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+  return nullptr;
+}
+
+bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
+  last_error_ = DWARF_ERROR_NONE;
+  const DwarfFde* fde = GetFdeFromPc(pc);
+  if (fde == nullptr || fde->cie == nullptr) {
+    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    return false;
+  }
+
+  // Now get the location information for this pc.
+  dwarf_loc_regs_t loc_regs;
+  if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
+    return false;
+  }
+
+  // Now eval the actual registers.
+  return Eval(fde->cie, process_memory, loc_regs, regs);
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, uint8_t version,
+                                                   Memory* regular_memory, AddressType* value) {
+  DwarfOp<AddressType> op(&memory_, regular_memory);
+
+  // Need to evaluate the op data.
+  uint64_t start = loc.values[1];
+  uint64_t end = start + loc.values[0];
+  if (!op.Eval(start, end, version)) {
+    last_error_ = op.last_error();
+    return false;
+  }
+  if (op.StackSize() == 0) {
+    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    return false;
+  }
+  // We don't support an expression that evaluates to a register number.
+  if (op.is_register()) {
+    last_error_ = DWARF_ERROR_NOT_IMPLEMENTED;
+    return false;
+  }
+  *value = op.StackAt(0);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
+                                         const dwarf_loc_regs_t& loc_regs, Regs* regs) {
+  RegsImpl<AddressType>* cur_regs = reinterpret_cast<RegsImpl<AddressType>*>(regs);
+  if (cie->return_address_register >= cur_regs->total_regs()) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+
+  // Get the cfa value;
+  auto cfa_entry = loc_regs.find(CFA_REG);
+  if (cfa_entry == loc_regs.end()) {
+    last_error_ = DWARF_ERROR_CFA_NOT_DEFINED;
+    return false;
+  }
+
+  AddressType prev_pc = regs->pc();
+  AddressType prev_cfa = regs->sp();
+
+  AddressType cfa;
+  const DwarfLocation* loc = &cfa_entry->second;
+  // Only a few location types are valid for the cfa.
+  switch (loc->type) {
+    case DWARF_LOCATION_REGISTER:
+      if (loc->values[0] >= cur_regs->total_regs()) {
+        last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+        return false;
+      }
+      // If the stack pointer register is the CFA, and the stack
+      // pointer register does not have any associated location
+      // information, use the current cfa value.
+      if (regs->sp_reg() == loc->values[0] && loc_regs.count(regs->sp_reg()) == 0) {
+        cfa = prev_cfa;
+      } else {
+        cfa = (*cur_regs)[loc->values[0]];
+      }
+      cfa += loc->values[1];
+      break;
+    case DWARF_LOCATION_EXPRESSION:
+    case DWARF_LOCATION_VAL_EXPRESSION: {
+      AddressType value;
+      if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
+        return false;
+      }
+      if (loc->type == DWARF_LOCATION_EXPRESSION) {
+        if (!regular_memory->Read(value, &cfa, sizeof(AddressType))) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+      } else {
+        cfa = value;
+      }
+      break;
+    }
+    default:
+      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+  }
+
+  // This code is not guaranteed to work in cases where a register location
+  // is a double indirection to the actual value. For example, if r3 is set
+  // to r5 + 4, and r5 is set to CFA + 4, then this won't necessarily work
+  // because it does not guarantee that r5 is evaluated before r3.
+  // Check that this case does not exist, and error if it does.
+  bool return_address_undefined = false;
+  for (const auto& entry : loc_regs) {
+    uint16_t reg = entry.first;
+    // Already handled the CFA register.
+    if (reg == CFA_REG) continue;
+
+    if (reg >= cur_regs->total_regs()) {
+      // Skip this unknown register.
+      continue;
+    }
+
+    const DwarfLocation* loc = &entry.second;
+    switch (loc->type) {
+      case DWARF_LOCATION_OFFSET:
+        if (!regular_memory->Read(cfa + loc->values[0], &(*cur_regs)[reg], sizeof(AddressType))) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+        break;
+      case DWARF_LOCATION_VAL_OFFSET:
+        (*cur_regs)[reg] = cfa + loc->values[0];
+        break;
+      case DWARF_LOCATION_REGISTER: {
+        uint16_t cur_reg = loc->values[0];
+        if (cur_reg >= cur_regs->total_regs()) {
+          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+          return false;
+        }
+        if (loc_regs.find(cur_reg) != loc_regs.end()) {
+          // This is a double indirection, a register definition references
+          // another register which is also defined as something other
+          // than a register.
+          log(0,
+              "Invalid indirection: register %d references register %d which is "
+              "not a plain register.\n",
+              reg, cur_reg);
+          last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+          return false;
+        }
+        (*cur_regs)[reg] = (*cur_regs)[cur_reg] + loc->values[1];
+        break;
+      }
+      case DWARF_LOCATION_EXPRESSION:
+      case DWARF_LOCATION_VAL_EXPRESSION: {
+        AddressType value;
+        if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
+          return false;
+        }
+        if (loc->type == DWARF_LOCATION_EXPRESSION) {
+          if (!regular_memory->Read(value, &(*cur_regs)[reg], sizeof(AddressType))) {
+            last_error_ = DWARF_ERROR_MEMORY_INVALID;
+            return false;
+          }
+        } else {
+          (*cur_regs)[reg] = value;
+        }
+        break;
+      }
+      case DWARF_LOCATION_UNDEFINED:
+        if (reg == cie->return_address_register) {
+          return_address_undefined = true;
+        }
+      default:
+        break;
+    }
+  }
+
+  // Find the return address location.
+  if (return_address_undefined) {
+    cur_regs->set_pc(0);
+  } else {
+    cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
+  }
+  cur_regs->set_sp(cfa);
+  // Stop if the cfa and pc are the same.
+  return prev_cfa != cfa || prev_pc != cur_regs->pc();
+}
+
+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_ = DWARF_ERROR_MEMORY_INVALID;
+    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_ = DWARF_ERROR_MEMORY_INVALID;
+      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_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    if (!IsCie64(cie_id)) {
+      // This is not a Cie, something has gone horribly wrong.
+      last_error_ = 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_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    if (!IsCie32(cie_id)) {
+      // This is not a Cie, something has gone horribly wrong.
+      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+  }
+
+  if (!memory_.ReadBytes(&cie->version, sizeof(cie->version))) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
+    // Unrecognized version.
+    last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
+    return false;
+  }
+
+  // Read the augmentation string.
+  char aug_value;
+  do {
+    if (!memory_.ReadBytes(&aug_value, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      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_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+  }
+
+  // Code Alignment Factor
+  if (!memory_.ReadULEB128(&cie->code_alignment_factor)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  // Data Alignment Factor
+  if (!memory_.ReadSLEB128(&cie->data_alignment_factor)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    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_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    cie->return_address_register = return_address_register;
+  } else if (!memory_.ReadULEB128(&cie->return_address_register)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    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_ = DWARF_ERROR_MEMORY_INVALID;
+    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_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+        break;
+      case 'P': {
+        uint8_t encoding;
+        if (!memory_.ReadBytes(&encoding, 1)) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+        if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+      } break;
+      case 'R':
+        if (!memory_.ReadBytes(&cie->fde_address_encoding, 1)) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          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_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  if (length32 == static_cast<uint32_t>(-1)) {
+    // 64 bit Fde.
+    uint64_t length64;
+    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    fde->cfa_instructions_end = memory_.cur_offset() + length64;
+
+    uint64_t value64;
+    if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    if (IsCie64(value64)) {
+      // This is a Cie, this means something has gone wrong.
+      last_error_ = 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_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    if (IsCie32(value32)) {
+      // This is a Cie, this means something has gone wrong.
+      last_error_ = 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);
+
+  if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_start)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  fde->pc_start = AdjustPcFromFde(fde->pc_start);
+
+  if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_end)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    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_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    uint64_t cur_offset = memory_.cur_offset();
+
+    if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      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);
+
+  // Look for the cached copy of the cie data.
+  auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
+  if (reg_entry == cie_loc_regs_.end()) {
+    if (!cfa.GetLocationInfo(pc, fde->cie->cfa_instructions_offset, fde->cie->cfa_instructions_end,
+                             loc_regs)) {
+      last_error_ = cfa.last_error();
+      return false;
+    }
+    cie_loc_regs_[fde->cie_offset] = *loc_regs;
+  }
+  cfa.set_cie_loc_regs(&cie_loc_regs_[fde->cie_offset]);
+  if (!cfa.GetLocationInfo(pc, fde->cfa_instructions_offset, fde->cfa_instructions_end, loc_regs)) {
+    last_error_ = cfa.last_error();
+    return false;
+  }
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, uint64_t load_bias,
+                                        const DwarfFde* fde) {
+  DwarfCfa<AddressType> cfa(&memory_, fde);
+
+  // Always print the cie information.
+  const DwarfCie* cie = fde->cie;
+  if (!cfa.Log(indent, pc, load_bias, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
+    last_error_ = cfa.last_error();
+    return false;
+  }
+  if (!cfa.Log(indent, pc, load_bias, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
+    last_error_ = cfa.last_error();
+    return false;
+  }
+  return true;
+}
+
+// Explicitly instantiate DwarfSectionImpl
+template class DwarfSectionImpl<uint32_t>;
+template class DwarfSectionImpl<uint64_t>;
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 272b5f0..a800c31 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -23,12 +23,17 @@
 #define LOG_TAG "unwind"
 #include <log/log.h>
 
-#include "Elf.h"
-#include "ElfInterface.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
 #include "ElfInterfaceArm.h"
 #include "Machine.h"
-#include "Memory.h"
-#include "Regs.h"
+#include "Symbols.h"
+
+namespace unwindstack {
 
 bool Elf::Init() {
   if (!memory_) {
@@ -49,6 +54,58 @@
   return valid_;
 }
 
+// It is expensive to initialize the .gnu_debugdata section. Provide a method
+// to initialize this data separately.
+void Elf::InitGnuDebugdata() {
+  if (!valid_ || interface_->gnu_debugdata_offset() == 0) {
+    return;
+  }
+
+  gnu_debugdata_memory_.reset(interface_->CreateGnuDebugdataMemory());
+  gnu_debugdata_interface_.reset(CreateInterfaceFromMemory(gnu_debugdata_memory_.get()));
+  ElfInterface* gnu = gnu_debugdata_interface_.get();
+  if (gnu == nullptr) {
+    return;
+  }
+  if (gnu->Init()) {
+    gnu->InitHeaders();
+  } else {
+    // Free all of the memory associated with the gnu_debugdata section.
+    gnu_debugdata_memory_.reset(nullptr);
+    gnu_debugdata_interface_.reset(nullptr);
+  }
+}
+
+bool Elf::GetSoname(std::string* name) {
+  return valid_ && interface_->GetSoname(name);
+}
+
+uint64_t Elf::GetRelPc(uint64_t pc, const MapInfo* map_info) {
+  uint64_t load_bias = 0;
+  if (valid()) {
+    load_bias = interface_->load_bias();
+  }
+
+  return pc - map_info->start + load_bias + map_info->elf_offset;
+}
+
+bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
+  return valid_ && (interface_->GetFunctionName(addr, name, func_offset) ||
+                    (gnu_debugdata_interface_ &&
+                     gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
+}
+
+bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
+  return valid_ && (interface_->Step(rel_pc, regs, process_memory) ||
+                    (gnu_debugdata_interface_ &&
+                     gnu_debugdata_interface_->Step(rel_pc, regs, process_memory)));
+}
+
+uint64_t Elf::GetLoadBias() {
+  if (!valid_) return 0;
+  return interface_->load_bias();
+}
+
 bool Elf::IsValidElf(Memory* memory) {
   if (memory == nullptr) {
     return false;
@@ -111,3 +168,5 @@
 
   return interface.release();
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 087457c..75abc85 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -20,9 +20,102 @@
 #include <memory>
 #include <string>
 
-#include "ElfInterface.h"
-#include "Memory.h"
-#include "Regs.h"
+#include <7zCrc.h>
+#include <Xz.h>
+#include <XzCrc64.h>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEhFrame.h"
+#include "Symbols.h"
+
+namespace unwindstack {
+
+ElfInterface::~ElfInterface() {
+  for (auto symbol : symbols_) {
+    delete symbol;
+  }
+}
+
+Memory* ElfInterface::CreateGnuDebugdataMemory() {
+  if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) {
+    return nullptr;
+  }
+
+  // TODO: Only call these initialization functions once.
+  CrcGenerateTable();
+  Crc64GenerateTable();
+
+  std::vector<uint8_t> src(gnu_debugdata_size_);
+  if (!memory_->Read(gnu_debugdata_offset_, src.data(), gnu_debugdata_size_)) {
+    gnu_debugdata_offset_ = 0;
+    gnu_debugdata_size_ = static_cast<uint64_t>(-1);
+    return nullptr;
+  }
+
+  ISzAlloc alloc;
+  CXzUnpacker state;
+  alloc.Alloc = [](void*, size_t size) { return malloc(size); };
+  alloc.Free = [](void*, void* ptr) { return free(ptr); };
+
+  XzUnpacker_Construct(&state, &alloc);
+
+  std::unique_ptr<MemoryBuffer> dst(new MemoryBuffer);
+  int return_val;
+  size_t src_offset = 0;
+  size_t dst_offset = 0;
+  ECoderStatus status;
+  dst->Resize(5 * gnu_debugdata_size_);
+  do {
+    size_t src_remaining = src.size() - src_offset;
+    size_t dst_remaining = dst->Size() - dst_offset;
+    if (dst_remaining < 2 * gnu_debugdata_size_) {
+      dst->Resize(dst->Size() + 2 * gnu_debugdata_size_);
+      dst_remaining += 2 * gnu_debugdata_size_;
+    }
+    return_val = XzUnpacker_Code(&state, dst->GetPtr(dst_offset), &dst_remaining, &src[src_offset],
+                                 &src_remaining, CODER_FINISH_ANY, &status);
+    src_offset += src_remaining;
+    dst_offset += dst_remaining;
+  } while (return_val == SZ_OK && status == CODER_STATUS_NOT_FINISHED);
+  XzUnpacker_Free(&state);
+  if (return_val != SZ_OK || !XzUnpacker_IsStreamWasFinished(&state)) {
+    gnu_debugdata_offset_ = 0;
+    gnu_debugdata_size_ = static_cast<uint64_t>(-1);
+    return nullptr;
+  }
+
+  // Shrink back down to the exact size.
+  dst->Resize(dst_offset);
+
+  return dst.release();
+}
+
+template <typename AddressType>
+void ElfInterface::InitHeadersWithTemplate() {
+  if (eh_frame_offset_ != 0) {
+    eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
+    if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_)) {
+      eh_frame_.reset(nullptr);
+      eh_frame_offset_ = 0;
+      eh_frame_size_ = static_cast<uint64_t>(-1);
+    }
+  }
+
+  if (debug_frame_offset_ != 0) {
+    debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
+    if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_)) {
+      debug_frame_.reset(nullptr);
+      debug_frame_offset_ = 0;
+      debug_frame_size_ = static_cast<uint64_t>(-1);
+    }
+  }
+}
 
 template <typename EhdrType, typename PhdrType, typename ShdrType>
 bool ElfInterface::ReadAllHeaders() {
@@ -34,7 +127,13 @@
   if (!ReadProgramHeaders<EhdrType, PhdrType>(ehdr)) {
     return false;
   }
-  return ReadSectionHeaders<EhdrType, ShdrType>(ehdr);
+
+  // We could still potentially unwind without the section header
+  // information, so ignore any errors.
+  if (!ReadSectionHeaders<EhdrType, ShdrType>(ehdr)) {
+    log(0, "Malformed section header found, ignoring...");
+  }
+  return true;
 }
 
 template <typename EhdrType, typename PhdrType>
@@ -124,12 +223,39 @@
   }
 
   // Skip the first header, it's always going to be NULL.
+  offset += ehdr.e_shentsize;
   for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
     if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
       return false;
     }
 
-    if (shdr.sh_type == SHT_PROGBITS) {
+    if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
+      if (!memory_->Read(offset, &shdr, sizeof(shdr))) {
+        return false;
+      }
+      // Need to go get the information about the section that contains
+      // the string terminated names.
+      ShdrType str_shdr;
+      if (shdr.sh_link >= ehdr.e_shnum) {
+        return false;
+      }
+      uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
+      if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_type, sizeof(str_shdr.sh_type))) {
+        return false;
+      }
+      if (str_shdr.sh_type != SHT_STRTAB) {
+        return false;
+      }
+      if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_offset,
+                              sizeof(str_shdr.sh_offset))) {
+        return false;
+      }
+      if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_size, sizeof(str_shdr.sh_size))) {
+        return false;
+      }
+      symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize,
+                                     str_shdr.sh_offset, str_shdr.sh_size));
+    } else if (shdr.sh_type == SHT_PROGBITS && sec_size != 0) {
       // Look for the .debug_frame and .gnu_debugdata.
       if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
         return false;
@@ -137,18 +263,20 @@
       if (shdr.sh_name < sec_size) {
         std::string name;
         if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
+          uint64_t* offset_ptr = nullptr;
+          uint64_t* size_ptr = nullptr;
           if (name == ".debug_frame") {
-            if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
-                memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
-              debug_frame_offset_ = shdr.sh_offset;
-              debug_frame_size_ = shdr.sh_size;
-            }
+            offset_ptr = &debug_frame_offset_;
+            size_ptr = &debug_frame_size_;
           } else if (name == ".gnu_debugdata") {
-            if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
-                memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
-              gnu_debugdata_offset_ = shdr.sh_offset;
-              gnu_debugdata_size_ = shdr.sh_size;
-            }
+            offset_ptr = &gnu_debugdata_offset_;
+            size_ptr = &gnu_debugdata_size_;
+          }
+          if (offset_ptr != nullptr &&
+              memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+              memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+            *offset_ptr = shdr.sh_offset;
+            *size_ptr = shdr.sh_size;
           }
         }
       }
@@ -205,11 +333,47 @@
   return true;
 }
 
-bool ElfInterface::Step(uint64_t, Regs*, Memory*) {
+template <typename SymType>
+bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name,
+                                               uint64_t* func_offset) {
+  if (symbols_.empty()) {
+    return false;
+  }
+
+  for (const auto symbol : symbols_) {
+    if (symbol->GetName<SymType>(addr, load_bias_, memory_, name, func_offset)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
+  // Need to subtract off the load_bias to get the correct pc.
+  if (pc < load_bias_) {
+    return false;
+  }
+  pc -= load_bias_;
+
+  // Try the eh_frame first.
+  DwarfSection* eh_frame = eh_frame_.get();
+  if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory)) {
+    return true;
+  }
+
+  // Try the debug_frame next.
+  DwarfSection* debug_frame = debug_frame_.get();
+  if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory)) {
+    return true;
+  }
+
   return false;
 }
 
 // Instantiate all of the needed template functions.
+template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
+template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
+
 template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>();
 template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>();
 
@@ -221,3 +385,10 @@
 
 template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
 template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
+
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
+                                                                   uint64_t*);
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
+                                                                   uint64_t*);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index bab84cc..66bc51f 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -17,12 +17,14 @@
 #include <elf.h>
 #include <stdint.h>
 
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
 #include "ArmExidx.h"
-#include "ElfInterface.h"
 #include "ElfInterfaceArm.h"
 #include "Machine.h"
-#include "Memory.h"
-#include "Regs.h"
+
+namespace unwindstack {
 
 bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) {
   if (start_offset_ == 0 || total_entries_ == 0) {
@@ -127,3 +129,5 @@
   }
   return false;
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index ece694f..1f4e8cb 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -23,8 +23,10 @@
 #include <iterator>
 #include <unordered_map>
 
-#include "ElfInterface.h"
-#include "Memory.h"
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 class ElfInterfaceArm : public ElfInterface32 {
  public:
@@ -87,4 +89,6 @@
   std::unordered_map<size_t, uint32_t> addrs_;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
diff --git a/libunwindstack/Log.cpp b/libunwindstack/Log.cpp
index faeb66c..436e23c 100644
--- a/libunwindstack/Log.cpp
+++ b/libunwindstack/Log.cpp
@@ -25,7 +25,9 @@
 
 #include <android-base/stringprintf.h>
 
-#include "Log.h"
+#include <unwindstack/Log.h>
+
+namespace unwindstack {
 
 static bool g_print_to_stdout = false;
 
@@ -51,3 +53,5 @@
   }
   va_end(args);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Machine.h b/libunwindstack/Machine.h
index 323ce80..1fb9309 100644
--- a/libunwindstack/Machine.h
+++ b/libunwindstack/Machine.h
@@ -19,6 +19,8 @@
 
 #include <stdint.h>
 
+namespace unwindstack {
+
 enum ArmReg : uint16_t {
   ARM_REG_R0 = 0,
   ARM_REG_R1,
@@ -83,51 +85,57 @@
   ARM64_REG_LR = ARM64_REG_R30,
 };
 
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
 enum X86Reg : uint16_t {
   X86_REG_EAX = 0,
-  X86_REG_ECX,
-  X86_REG_EDX,
-  X86_REG_EBX,
-  X86_REG_ESP,
-  X86_REG_EBP,
-  X86_REG_ESI,
-  X86_REG_EDI,
-  X86_REG_EIP,
-  X86_REG_EFL,
-  X86_REG_CS,
-  X86_REG_SS,
-  X86_REG_DS,
-  X86_REG_ES,
-  X86_REG_FS,
-  X86_REG_GS,
+  X86_REG_ECX = 1,
+  X86_REG_EDX = 2,
+  X86_REG_EBX = 3,
+  X86_REG_ESP = 4,
+  X86_REG_EBP = 5,
+  X86_REG_ESI = 6,
+  X86_REG_EDI = 7,
+  X86_REG_EIP = 8,
+  X86_REG_EFL = 9,
+  X86_REG_CS = 10,
+  X86_REG_SS = 11,
+  X86_REG_DS = 12,
+  X86_REG_ES = 13,
+  X86_REG_FS = 14,
+  X86_REG_GS = 15,
   X86_REG_LAST,
 
   X86_REG_SP = X86_REG_ESP,
   X86_REG_PC = X86_REG_EIP,
 };
 
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
 enum X86_64Reg : uint16_t {
   X86_64_REG_RAX = 0,
-  X86_64_REG_RDX,
-  X86_64_REG_RCX,
-  X86_64_REG_RBX,
-  X86_64_REG_RSI,
-  X86_64_REG_RDI,
-  X86_64_REG_RBP,
-  X86_64_REG_RSP,
-  X86_64_REG_R8,
-  X86_64_REG_R9,
-  X86_64_REG_R10,
-  X86_64_REG_R11,
-  X86_64_REG_R12,
-  X86_64_REG_R13,
-  X86_64_REG_R14,
-  X86_64_REG_R15,
-  X86_64_REG_RIP,
+  X86_64_REG_RDX = 1,
+  X86_64_REG_RCX = 2,
+  X86_64_REG_RBX = 3,
+  X86_64_REG_RSI = 4,
+  X86_64_REG_RDI = 5,
+  X86_64_REG_RBP = 6,
+  X86_64_REG_RSP = 7,
+  X86_64_REG_R8 = 8,
+  X86_64_REG_R9 = 9,
+  X86_64_REG_R10 = 10,
+  X86_64_REG_R11 = 11,
+  X86_64_REG_R12 = 12,
+  X86_64_REG_R13 = 13,
+  X86_64_REG_R14 = 14,
+  X86_64_REG_R15 = 15,
+  X86_64_REG_RIP = 16,
   X86_64_REG_LAST,
 
   X86_64_REG_SP = X86_64_REG_RSP,
   X86_64_REG_PC = X86_64_REG_RIP,
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_MACHINE_H
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 051f700..d0e1216 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -20,10 +20,12 @@
 #include <memory>
 #include <string>
 
-#include "Elf.h"
-#include "MapInfo.h"
-#include "Maps.h"
-#include "Memory.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 Memory* MapInfo::CreateMemory(pid_t pid) {
   if (end <= start) {
@@ -73,14 +75,18 @@
   return new MemoryRange(memory, start, end);
 }
 
-Elf* MapInfo::GetElf(pid_t pid, bool) {
+Elf* MapInfo::GetElf(pid_t pid, bool init_gnu_debugdata) {
   if (elf) {
     return elf;
   }
 
   elf = new Elf(CreateMemory(pid));
-  elf->Init();
+  if (elf->Init() && init_gnu_debugdata) {
+    elf->InitGnuDebugdata();
+  }
   // If the init fails, keep the elf around as an invalid object so we
   // don't try to reinit the object.
   return elf;
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index b369c43..8a90423 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -29,7 +29,11 @@
 #include <string>
 #include <vector>
 
-#include "Maps.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 MapInfo* Maps::Find(uint64_t pc) {
   if (maps_.empty()) {
@@ -71,15 +75,17 @@
     map_info->flags |= PROT_EXEC;
   }
 
-  map_info->name = &line[name_pos];
-  size_t length = map_info->name.length() - 1;
-  if (map_info->name[length] == '\n') {
-    map_info->name.erase(length);
-  }
-  // Mark a device map in /dev/and not in /dev/ashmem/ specially.
-  if (!map_info->name.empty() && map_info->name.substr(0, 5) == "/dev/" &&
-      map_info->name.substr(5, 7) != "ashmem/") {
-    map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
+  if (line[name_pos] != '\0') {
+    map_info->name = &line[name_pos];
+    size_t length = map_info->name.length() - 1;
+    if (map_info->name[length] == '\n') {
+      map_info->name.erase(length);
+    }
+
+    // Mark a device map in /dev/and not in /dev/ashmem/ specially.
+    if (map_info->name.substr(0, 5) == "/dev/" && map_info->name.substr(5, 7) != "ashmem/") {
+      map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
+    }
   }
 
   return true;
@@ -194,3 +200,5 @@
   }
   return true;
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 9e46509..8c36055 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -28,7 +28,11 @@
 
 #include <android-base/unique_fd.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+
+namespace unwindstack {
 
 bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
   string->clear();
@@ -245,6 +249,11 @@
   return true;
 }
 
+MemoryRange::MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
+    : memory_(memory), begin_(begin), length_(end - begin) {
+  CHECK(end > begin);
+}
+
 bool MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
   uint64_t max_read;
   if (__builtin_add_overflow(addr, size, &max_read) || max_read > length_) {
@@ -253,3 +262,5 @@
   // The check above guarantees that addr + begin_ will not overflow.
   return memory_->Read(addr + begin_, dst, size);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index adb6522..aa59e31 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <assert.h>
 #include <elf.h>
 #include <stdint.h>
 #include <sys/ptrace.h>
@@ -22,28 +21,23 @@
 
 #include <vector>
 
-#include "Elf.h"
-#include "ElfInterface.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "Check.h"
 #include "Machine.h"
-#include "MapInfo.h"
-#include "Regs.h"
+#include "Ucontext.h"
 #include "User.h"
 
-template <typename AddressType>
-uint64_t RegsTmpl<AddressType>::GetRelPc(Elf* elf, const MapInfo* map_info) {
-  uint64_t load_bias = 0;
-  if (elf->valid()) {
-    load_bias = elf->interface()->load_bias();
-  }
-
-  return pc_ - map_info->start + load_bias + map_info->elf_offset;
-}
+namespace unwindstack {
 
 template <typename AddressType>
-bool RegsTmpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) {
+bool RegsImpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) {
   switch (return_loc_.type) {
   case LOCATION_REGISTER:
-    assert(return_loc_.value < total_regs_);
+    CHECK(return_loc_.value < total_regs_);
     *value = regs_[return_loc_.value];
     return true;
   case LOCATION_SP_OFFSET:
@@ -59,9 +53,8 @@
   }
 }
 
-RegsArm::RegsArm() : RegsTmpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP,
-                                        Location(LOCATION_REGISTER, ARM_REG_LR)) {
-}
+RegsArm::RegsArm()
+    : RegsImpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
 
 uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
@@ -89,10 +82,14 @@
   return rel_pc - 4;
 }
 
-RegsArm64::RegsArm64() : RegsTmpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP,
-                                            Location(LOCATION_REGISTER, ARM64_REG_LR)) {
+void RegsArm::SetFromRaw() {
+  set_pc(regs_[ARM_REG_PC]);
+  set_sp(regs_[ARM_REG_SP]);
 }
 
+RegsArm64::RegsArm64()
+    : RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
+
 uint64_t RegsArm64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
     return rel_pc;
@@ -104,10 +101,14 @@
   return rel_pc - 4;
 }
 
-RegsX86::RegsX86() : RegsTmpl<uint32_t>(X86_REG_LAST, X86_REG_SP,
-                                        Location(LOCATION_SP_OFFSET, -4)) {
+void RegsArm64::SetFromRaw() {
+  set_pc(regs_[ARM64_REG_PC]);
+  set_sp(regs_[ARM64_REG_SP]);
 }
 
+RegsX86::RegsX86()
+    : RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
+
 uint64_t RegsX86::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
     return rel_pc;
@@ -119,10 +120,14 @@
   return rel_pc - 1;
 }
 
-RegsX86_64::RegsX86_64() : RegsTmpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP,
-                                              Location(LOCATION_SP_OFFSET, -8)) {
+void RegsX86::SetFromRaw() {
+  set_pc(regs_[X86_REG_PC]);
+  set_sp(regs_[X86_REG_SP]);
 }
 
+RegsX86_64::RegsX86_64()
+    : RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
+
 uint64_t RegsX86_64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
     return rel_pc;
@@ -135,15 +140,17 @@
   return rel_pc - 1;
 }
 
+void RegsX86_64::SetFromRaw() {
+  set_pc(regs_[X86_64_REG_PC]);
+  set_sp(regs_[X86_64_REG_SP]);
+}
+
 static Regs* ReadArm(void* remote_data) {
   arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data);
 
   RegsArm* regs = new RegsArm();
   memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t));
-
-  regs->set_pc(user->regs[ARM_REG_PC]);
-  regs->set_sp(user->regs[ARM_REG_SP]);
-
+  regs->SetFromRaw();
   return regs;
 }
 
@@ -152,9 +159,10 @@
 
   RegsArm64* regs = new RegsArm64();
   memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R31 + 1) * sizeof(uint64_t));
-  regs->set_pc(user->pc);
-  regs->set_sp(user->sp);
-
+  uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
+  reg_data[ARM64_REG_PC] = user->pc;
+  reg_data[ARM64_REG_SP] = user->sp;
+  regs->SetFromRaw();
   return regs;
 }
 
@@ -172,9 +180,7 @@
   (*regs)[X86_REG_ESP] = user->esp;
   (*regs)[X86_REG_EIP] = user->eip;
 
-  regs->set_pc(user->eip);
-  regs->set_sp(user->esp);
-
+  regs->SetFromRaw();
   return regs;
 }
 
@@ -200,9 +206,7 @@
   (*regs)[X86_64_REG_RSP] = user->rsp;
   (*regs)[X86_64_REG_RIP] = user->rip;
 
-  regs->set_pc(user->rip);
-  regs->set_sp(user->rsp);
-
+  regs->SetFromRaw();
   return regs;
 }
 
@@ -235,3 +239,113 @@
   }
   return nullptr;
 }
+
+static Regs* CreateFromArmUcontext(void* ucontext) {
+  arm_ucontext_t* arm_ucontext = reinterpret_cast<arm_ucontext_t*>(ucontext);
+
+  RegsArm* regs = new RegsArm();
+  memcpy(regs->RawData(), &arm_ucontext->uc_mcontext.regs[0], ARM_REG_LAST * sizeof(uint32_t));
+  regs->SetFromRaw();
+  return regs;
+}
+
+static Regs* CreateFromArm64Ucontext(void* ucontext) {
+  arm64_ucontext_t* arm64_ucontext = reinterpret_cast<arm64_ucontext_t*>(ucontext);
+
+  RegsArm64* regs = new RegsArm64();
+  memcpy(regs->RawData(), &arm64_ucontext->uc_mcontext.regs[0], ARM64_REG_LAST * sizeof(uint64_t));
+  regs->SetFromRaw();
+  return regs;
+}
+
+static Regs* CreateFromX86Ucontext(void* ucontext) {
+  x86_ucontext_t* x86_ucontext = reinterpret_cast<x86_ucontext_t*>(ucontext);
+
+  RegsX86* regs = new RegsX86();
+  // Put the registers in the expected order.
+  (*regs)[X86_REG_GS] = x86_ucontext->uc_mcontext.gs;
+  (*regs)[X86_REG_FS] = x86_ucontext->uc_mcontext.fs;
+  (*regs)[X86_REG_ES] = x86_ucontext->uc_mcontext.es;
+  (*regs)[X86_REG_DS] = x86_ucontext->uc_mcontext.ds;
+  (*regs)[X86_REG_EDI] = x86_ucontext->uc_mcontext.edi;
+  (*regs)[X86_REG_ESI] = x86_ucontext->uc_mcontext.esi;
+  (*regs)[X86_REG_EBP] = x86_ucontext->uc_mcontext.ebp;
+  (*regs)[X86_REG_ESP] = x86_ucontext->uc_mcontext.esp;
+  (*regs)[X86_REG_EBX] = x86_ucontext->uc_mcontext.ebx;
+  (*regs)[X86_REG_EDX] = x86_ucontext->uc_mcontext.edx;
+  (*regs)[X86_REG_ECX] = x86_ucontext->uc_mcontext.ecx;
+  (*regs)[X86_REG_EAX] = x86_ucontext->uc_mcontext.eax;
+  (*regs)[X86_REG_EIP] = x86_ucontext->uc_mcontext.eip;
+  regs->SetFromRaw();
+  return regs;
+}
+
+static Regs* CreateFromX86_64Ucontext(void* ucontext) {
+  x86_64_ucontext_t* x86_64_ucontext = reinterpret_cast<x86_64_ucontext_t*>(ucontext);
+
+  RegsX86_64* regs = new RegsX86_64();
+  // Put the registers in the expected order.
+
+  // R8-R15
+  memcpy(&(*regs)[X86_64_REG_R8], &x86_64_ucontext->uc_mcontext.r8, 8 * sizeof(uint64_t));
+
+  // Rest of the registers.
+  (*regs)[X86_64_REG_RDI] = x86_64_ucontext->uc_mcontext.rdi;
+  (*regs)[X86_64_REG_RSI] = x86_64_ucontext->uc_mcontext.rsi;
+  (*regs)[X86_64_REG_RBP] = x86_64_ucontext->uc_mcontext.rbp;
+  (*regs)[X86_64_REG_RBX] = x86_64_ucontext->uc_mcontext.rbx;
+  (*regs)[X86_64_REG_RDX] = x86_64_ucontext->uc_mcontext.rdx;
+  (*regs)[X86_64_REG_RAX] = x86_64_ucontext->uc_mcontext.rax;
+  (*regs)[X86_64_REG_RCX] = x86_64_ucontext->uc_mcontext.rcx;
+  (*regs)[X86_64_REG_RSP] = x86_64_ucontext->uc_mcontext.rsp;
+  (*regs)[X86_64_REG_RIP] = x86_64_ucontext->uc_mcontext.rip;
+
+  regs->SetFromRaw();
+  return regs;
+}
+
+Regs* Regs::CreateFromUcontext(uint32_t machine_type, void* ucontext) {
+  switch (machine_type) {
+    case EM_386:
+      return CreateFromX86Ucontext(ucontext);
+    case EM_X86_64:
+      return CreateFromX86_64Ucontext(ucontext);
+    case EM_ARM:
+      return CreateFromArmUcontext(ucontext);
+    case EM_AARCH64:
+      return CreateFromArm64Ucontext(ucontext);
+  }
+  return nullptr;
+}
+
+uint32_t Regs::GetMachineType() {
+#if defined(__arm__)
+  return EM_ARM;
+#elif defined(__aarch64__)
+  return EM_AARCH64;
+#elif defined(__i386__)
+  return EM_386;
+#elif defined(__x86_64__)
+  return EM_X86_64;
+#else
+  abort();
+#endif
+}
+
+Regs* Regs::CreateFromLocal() {
+  Regs* regs;
+#if defined(__arm__)
+  regs = new RegsArm();
+#elif defined(__aarch64__)
+  regs = new RegsArm64();
+#elif defined(__i386__)
+  regs = new RegsX86();
+#elif defined(__x86_64__)
+  regs = new RegsX86_64();
+#else
+  abort();
+#endif
+  return regs;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index 86c1233..42d816a 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -14,15 +14,18 @@
  * limitations under the License.
  */
 
-#include <assert.h>
 #include <elf.h>
 #include <stdint.h>
 
 #include <string>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
 #include "Symbols.h"
 
+namespace unwindstack {
+
 Symbols::Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
                  uint64_t str_size)
     : cur_offset_(offset),
@@ -58,7 +61,7 @@
   if (symbols_.size() != 0) {
     const Info* info = GetInfoFromCache(addr);
     if (info) {
-      assert(addr >= info->start_offset && addr <= info->end_offset);
+      CHECK(addr >= info->start_offset && addr <= info->end_offset);
       *func_offset = addr - info->start_offset;
       return elf_memory->ReadString(info->str_offset, name, str_end_ - info->str_offset);
     }
@@ -108,3 +111,5 @@
 // Instantiate all of the needed template functions.
 template bool Symbols::GetName<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
 template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index 3c0d033..689144b 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -22,6 +22,8 @@
 #include <string>
 #include <vector>
 
+namespace unwindstack {
+
 // Forward declaration.
 class Memory;
 
@@ -61,4 +63,6 @@
   std::vector<Info> symbols_;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_SYMBOLS_H
diff --git a/libunwindstack/Ucontext.h b/libunwindstack/Ucontext.h
new file mode 100644
index 0000000..410af26
--- /dev/null
+++ b/libunwindstack/Ucontext.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_H
+#define _LIBUNWINDSTACK_UCONTEXT_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+//-------------------------------------------------------------------
+// ARM ucontext structures
+//-------------------------------------------------------------------
+struct arm_stack_t {
+  uint32_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  uint32_t ss_size;  // size_t
+};
+
+struct arm_mcontext_t {
+  uint32_t trap_no;             // unsigned long
+  uint32_t error_code;          // unsigned long
+  uint32_t oldmask;             // unsigned long
+  uint32_t regs[ARM_REG_LAST];  // unsigned long
+  uint32_t cpsr;                // unsigned long
+  uint32_t fault_address;       // unsigned long
+};
+
+struct arm_ucontext_t {
+  uint32_t uc_flags;  // unsigned long
+  uint32_t uc_link;   // struct ucontext*
+  arm_stack_t uc_stack;
+  arm_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
+};
+//-------------------------------------------------------------------
+
+//-------------------------------------------------------------------
+// ARM64 ucontext structures
+//-------------------------------------------------------------------
+struct arm64_stack_t {
+  uint64_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  uint64_t ss_size;  // size_t
+};
+
+struct arm64_sigset_t {
+  uint64_t sig;  // unsigned long
+};
+
+struct arm64_mcontext_t {
+  uint64_t fault_address;         // __u64
+  uint64_t regs[ARM64_REG_LAST];  // __u64
+  uint64_t pstate;                // __u64
+  // Nothing else is used, so don't define it.
+};
+
+struct arm64_ucontext_t {
+  uint64_t uc_flags;  // unsigned long
+  uint64_t uc_link;   // struct ucontext*
+  arm64_stack_t uc_stack;
+  arm64_sigset_t uc_sigmask;
+  // The kernel adds extra padding after uc_sigmask to match glibc sigset_t on ARM64.
+  char __padding[128 - sizeof(arm64_sigset_t)];
+  // The full structure requires 16 byte alignment, but our partial structure
+  // doesn't, so force the alignment.
+  arm64_mcontext_t uc_mcontext __attribute__((aligned(16)));
+};
+//-------------------------------------------------------------------
+
+//-------------------------------------------------------------------
+// X86 ucontext structures
+//-------------------------------------------------------------------
+struct x86_stack_t {
+  uint32_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  uint32_t ss_size;  // size_t
+};
+
+struct x86_mcontext_t {
+  uint32_t gs;
+  uint32_t fs;
+  uint32_t es;
+  uint32_t ds;
+  uint32_t edi;
+  uint32_t esi;
+  uint32_t ebp;
+  uint32_t esp;
+  uint32_t ebx;
+  uint32_t edx;
+  uint32_t ecx;
+  uint32_t eax;
+  uint32_t trapno;
+  uint32_t err;
+  uint32_t eip;
+  uint32_t cs;
+  uint32_t efl;
+  uint32_t uesp;
+  uint32_t ss;
+  // Only care about the registers, skip everything else.
+};
+
+struct x86_ucontext_t {
+  uint32_t uc_flags;  // unsigned long
+  uint32_t uc_link;   // struct ucontext*
+  x86_stack_t uc_stack;
+  x86_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
+};
+//-------------------------------------------------------------------
+
+//-------------------------------------------------------------------
+// X86_64 ucontext structures
+//-------------------------------------------------------------------
+struct x86_64_stack_t {
+  uint64_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  uint64_t ss_size;  // size_t
+};
+
+struct x86_64_mcontext_t {
+  uint64_t r8;
+  uint64_t r9;
+  uint64_t r10;
+  uint64_t r11;
+  uint64_t r12;
+  uint64_t r13;
+  uint64_t r14;
+  uint64_t r15;
+  uint64_t rdi;
+  uint64_t rsi;
+  uint64_t rbp;
+  uint64_t rbx;
+  uint64_t rdx;
+  uint64_t rax;
+  uint64_t rcx;
+  uint64_t rsp;
+  uint64_t rip;
+  uint64_t efl;
+  uint64_t csgsfs;
+  uint64_t err;
+  uint64_t trapno;
+  uint64_t oldmask;
+  uint64_t cr2;
+  // Only care about the registers, skip everything else.
+};
+
+typedef struct x86_64_ucontext {
+  uint64_t uc_flags;  // unsigned long
+  uint64_t uc_link;   // struct ucontext*
+  x86_64_stack_t uc_stack;
+  x86_64_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
+} x86_64_ucontext_t;
+//-------------------------------------------------------------------
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_UCONTEXT_H
diff --git a/libunwindstack/User.h b/libunwindstack/User.h
index a695467..53f7e50 100644
--- a/libunwindstack/User.h
+++ b/libunwindstack/User.h
@@ -29,6 +29,8 @@
 #ifndef _LIBUNWINDSTACK_USER_H
 #define _LIBUNWINDSTACK_USER_H
 
+namespace unwindstack {
+
 struct x86_user_regs {
   uint32_t ebx;
   uint32_t ecx;
@@ -93,4 +95,6 @@
 // The largest user structure.
 constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10;
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_USER_H
diff --git a/libunwindstack/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h
similarity index 95%
rename from libunwindstack/DwarfLocation.h
rename to libunwindstack/include/unwindstack/DwarfLocation.h
index 062d125..3467e6a 100644
--- a/libunwindstack/DwarfLocation.h
+++ b/libunwindstack/include/unwindstack/DwarfLocation.h
@@ -21,6 +21,8 @@
 
 #include <unordered_map>
 
+namespace unwindstack {
+
 enum DwarfLocationEnum : uint8_t {
   DWARF_LOCATION_INVALID = 0,
   DWARF_LOCATION_UNDEFINED,
@@ -38,4 +40,6 @@
 
 typedef std::unordered_map<uint16_t, DwarfLocation> dwarf_loc_regs_t;
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_LOCATION_H
diff --git a/libunwindstack/DwarfMemory.h b/libunwindstack/include/unwindstack/DwarfMemory.h
similarity index 97%
rename from libunwindstack/DwarfMemory.h
rename to libunwindstack/include/unwindstack/DwarfMemory.h
index a304dd9..8dd8d2b 100644
--- a/libunwindstack/DwarfMemory.h
+++ b/libunwindstack/include/unwindstack/DwarfMemory.h
@@ -19,6 +19,8 @@
 
 #include <stdint.h>
 
+namespace unwindstack {
+
 // Forward declarations.
 class Memory;
 
@@ -69,4 +71,6 @@
   uint64_t text_offset_ = static_cast<uint64_t>(-1);
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_MEMORY_H
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
new file mode 100644
index 0000000..a97ca2b
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -0,0 +1,141 @@
+/*
+ * 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 _LIBUNWINDSTACK_DWARF_SECTION_H
+#define _LIBUNWINDSTACK_DWARF_SECTION_H
+
+#include <stdint.h>
+
+#include <iterator>
+#include <unordered_map>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+enum DwarfError : uint8_t;
+class Memory;
+class Regs;
+
+class DwarfSection {
+ public:
+  DwarfSection(Memory* memory);
+  virtual ~DwarfSection() = default;
+
+  class iterator : public std::iterator<std::bidirectional_iterator_tag, DwarfFde*> {
+   public:
+    iterator(DwarfSection* section, size_t index) : section_(section), index_(index) {}
+
+    iterator& operator++() {
+      index_++;
+      return *this;
+    }
+    iterator& operator++(int increment) {
+      index_ += increment;
+      return *this;
+    }
+    iterator& operator--() {
+      index_--;
+      return *this;
+    }
+    iterator& operator--(int decrement) {
+      index_ -= decrement;
+      return *this;
+    }
+
+    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_); }
+
+   private:
+    DwarfSection* section_ = nullptr;
+    size_t index_ = 0;
+  };
+
+  iterator begin() { return iterator(this, 0); }
+  iterator end() { return iterator(this, fde_count_); }
+
+  DwarfError last_error() { return last_error_; }
+
+  virtual bool Init(uint64_t offset, uint64_t size) = 0;
+
+  virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*) = 0;
+
+  virtual bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
+
+  virtual bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) = 0;
+
+  virtual const DwarfFde* GetFdeFromIndex(size_t index) = 0;
+
+  const DwarfFde* GetFdeFromPc(uint64_t pc);
+
+  virtual const DwarfFde* GetFdeFromOffset(uint64_t fde_offset) = 0;
+
+  virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
+
+  virtual bool IsCie32(uint32_t value32) = 0;
+
+  virtual bool IsCie64(uint64_t value64) = 0;
+
+  virtual uint64_t GetCieOffsetFromFde32(uint32_t pointer) = 0;
+
+  virtual uint64_t GetCieOffsetFromFde64(uint64_t pointer) = 0;
+
+  virtual uint64_t AdjustPcFromFde(uint64_t pc) = 0;
+
+  bool Step(uint64_t pc, Regs* regs, Memory* process_memory);
+
+ protected:
+  DwarfMemory memory_;
+  DwarfError last_error_;
+
+  uint64_t fde_count_;
+  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_;
+};
+
+template <typename AddressType>
+class DwarfSectionImpl : public DwarfSection {
+ public:
+  DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
+  virtual ~DwarfSectionImpl() = default;
+
+  bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
+            Regs* regs) 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, uint64_t load_bias, const DwarfFde* fde) override;
+
+ protected:
+  bool EvalExpression(const DwarfLocation& loc, uint8_t version, Memory* regular_memory,
+                      AddressType* value);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DWARF_SECTION_H
diff --git a/libunwindstack/DwarfStructs.h b/libunwindstack/include/unwindstack/DwarfStructs.h
similarity index 87%
rename from libunwindstack/DwarfStructs.h
rename to libunwindstack/include/unwindstack/DwarfStructs.h
index 57aac88..4b481f0 100644
--- a/libunwindstack/DwarfStructs.h
+++ b/libunwindstack/include/unwindstack/DwarfStructs.h
@@ -21,12 +21,12 @@
 
 #include <vector>
 
-#include "DwarfEncoding.h"
+namespace unwindstack {
 
-struct DwarfCIE {
+struct DwarfCie {
   uint8_t version = 0;
-  uint8_t fde_address_encoding = DW_EH_PE_absptr;
-  uint8_t lsda_encoding = DW_EH_PE_omit;
+  uint8_t fde_address_encoding = 0;
+  uint8_t lsda_encoding = 0;
   uint8_t segment_size = 0;
   std::vector<char> augmentation_string;
   uint64_t personality_handler = 0;
@@ -37,16 +37,18 @@
   uint64_t return_address_register = 0;
 };
 
-struct DwarfFDE {
+struct DwarfFde {
   uint64_t cie_offset = 0;
   uint64_t cfa_instructions_offset = 0;
   uint64_t cfa_instructions_end = 0;
   uint64_t pc_start = 0;
   uint64_t pc_end = 0;
   uint64_t lsda_address = 0;
-  const DwarfCIE* cie = nullptr;
+  const DwarfCie* cie = nullptr;
 };
 
 constexpr uint16_t CFA_REG = static_cast<uint16_t>(-1);
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_STRUCTS_H
diff --git a/libunwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
similarity index 74%
rename from libunwindstack/Elf.h
rename to libunwindstack/include/unwindstack/Elf.h
index 7bf45b8..d89a746 100644
--- a/libunwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -22,14 +22,17 @@
 #include <memory>
 #include <string>
 
-#include "ElfInterface.h"
-#include "Memory.h"
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
 
 #if !defined(EM_AARCH64)
 #define EM_AARCH64 183
 #endif
 
+namespace unwindstack {
+
 // Forward declaration.
+struct MapInfo;
 class Regs;
 
 class Elf {
@@ -41,20 +44,18 @@
 
   void InitGnuDebugdata();
 
-  bool GetSoname(std::string* name) {
-    return valid_ && interface_->GetSoname(name);
-  }
+  bool GetSoname(std::string* name);
 
-  bool GetFunctionName(uint64_t, std::string*, uint64_t*) {
-    return false;
-  }
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
 
-  bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
-    return valid_ && interface_->Step(rel_pc, regs, process_memory);
-  }
+  uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
+
+  bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory);
 
   ElfInterface* CreateInterfaceFromMemory(Memory* memory);
 
+  uint64_t GetLoadBias();
+
   bool valid() { return valid_; }
 
   uint32_t machine_type() { return machine_type_; }
@@ -65,6 +66,8 @@
 
   ElfInterface* interface() { return interface_.get(); }
 
+  ElfInterface* gnu_debugdata_interface() { return gnu_debugdata_interface_.get(); }
+
   static bool IsValidElf(Memory* memory);
 
  protected:
@@ -73,6 +76,11 @@
   std::unique_ptr<Memory> memory_;
   uint32_t machine_type_;
   uint8_t class_type_;
+
+  std::unique_ptr<Memory> gnu_debugdata_memory_;
+  std::unique_ptr<ElfInterface> gnu_debugdata_interface_;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_ELF_H
diff --git a/libunwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
similarity index 75%
rename from libunwindstack/ElfInterface.h
rename to libunwindstack/include/unwindstack/ElfInterface.h
index 944146c..5cac0d3 100644
--- a/libunwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -25,9 +25,14 @@
 #include <unordered_map>
 #include <vector>
 
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
+
 // Forward declarations.
 class Memory;
 class Regs;
+class Symbols;
 
 struct LoadInfo {
   uint64_t offset;
@@ -44,7 +49,7 @@
 class ElfInterface {
  public:
   ElfInterface(Memory* memory) : memory_(memory) {}
-  virtual ~ElfInterface() = default;
+  virtual ~ElfInterface();
 
   virtual bool Init() = 0;
 
@@ -68,10 +73,18 @@
   uint64_t dynamic_size() { return dynamic_size_; }
   uint64_t eh_frame_offset() { return eh_frame_offset_; }
   uint64_t eh_frame_size() { return eh_frame_size_; }
+  uint64_t debug_frame_offset() { return debug_frame_offset_; }
+  uint64_t debug_frame_size() { return debug_frame_size_; }
   uint64_t gnu_debugdata_offset() { return gnu_debugdata_offset_; }
   uint64_t gnu_debugdata_size() { return gnu_debugdata_size_; }
 
+  DwarfSection* eh_frame() { return eh_frame_.get(); }
+  DwarfSection* debug_frame() { return debug_frame_.get(); }
+
  protected:
+  template <typename AddressType>
+  void InitHeadersWithTemplate();
+
   template <typename EhdrType, typename PhdrType, typename ShdrType>
   bool ReadAllHeaders();
 
@@ -84,6 +97,9 @@
   template <typename DynType>
   bool GetSonameWithTemplate(std::string* soname);
 
+  template <typename SymType>
+  bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
+
   virtual bool HandleType(uint64_t, uint32_t) { return false; }
 
   Memory* memory_;
@@ -105,6 +121,11 @@
 
   uint8_t soname_type_ = SONAME_UNKNOWN;
   std::string soname_;
+
+  std::unique_ptr<DwarfSection> eh_frame_;
+  std::unique_ptr<DwarfSection> debug_frame_;
+
+  std::vector<Symbols*> symbols_;
 };
 
 class ElfInterface32 : public ElfInterface {
@@ -116,15 +137,14 @@
     return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>();
   }
 
-  void InitHeaders() override {
-  }
+  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
 
   bool GetSoname(std::string* soname) override {
     return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
   }
 
-  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override {
-    return false;
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+    return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
   }
 };
 
@@ -137,16 +157,17 @@
     return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>();
   }
 
-  void InitHeaders() override {
-  }
+  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
 
   bool GetSoname(std::string* soname) override {
     return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
   }
 
-  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override {
-    return false;
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+    return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
   }
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_ELF_INTERFACE_H
diff --git a/libunwindstack/Log.h b/libunwindstack/include/unwindstack/Log.h
similarity index 93%
rename from libunwindstack/Log.h
rename to libunwindstack/include/unwindstack/Log.h
index 2d01aa8..aa1219c 100644
--- a/libunwindstack/Log.h
+++ b/libunwindstack/include/unwindstack/Log.h
@@ -19,7 +19,11 @@
 
 #include <stdint.h>
 
+namespace unwindstack {
+
 void log_to_stdout(bool enable);
 void log(uint8_t indent, const char* format, ...);
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_LOG_H
diff --git a/libunwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
similarity index 95%
rename from libunwindstack/MapInfo.h
rename to libunwindstack/include/unwindstack/MapInfo.h
index 79a2ada..2a97dde 100644
--- a/libunwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -21,6 +21,8 @@
 
 #include <string>
 
+namespace unwindstack {
+
 // Forward declarations.
 class Elf;
 class Memory;
@@ -42,4 +44,6 @@
   Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
similarity index 96%
rename from libunwindstack/Maps.h
rename to libunwindstack/include/unwindstack/Maps.h
index 239b64a..0b02739 100644
--- a/libunwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -23,8 +23,9 @@
 #include <string>
 #include <vector>
 
-#include "Elf.h"
-#include "MapInfo.h"
+#include <unwindstack/MapInfo.h>
+
+namespace unwindstack {
 
 // Special flag to indicate a map is in /dev/. However, a map in
 // /dev/ashmem/... does not set this flag.
@@ -104,4 +105,6 @@
   bool Parse() override;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_MAPS_H
diff --git a/libunwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
similarity index 89%
rename from libunwindstack/Memory.h
rename to libunwindstack/include/unwindstack/Memory.h
index f9f6d56..0c05266 100644
--- a/libunwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -17,7 +17,6 @@
 #ifndef _LIBUNWINDSTACK_MEMORY_H
 #define _LIBUNWINDSTACK_MEMORY_H
 
-#include <assert.h>
 #include <stdint.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -25,6 +24,8 @@
 #include <string>
 #include <vector>
 
+namespace unwindstack {
+
 class Memory {
  public:
   Memory() = default;
@@ -46,13 +47,9 @@
     return Read(offset, field, size);
   }
 
-  inline bool Read32(uint64_t addr, uint32_t* dst) {
-    return Read(addr, dst, sizeof(uint32_t));
-  }
+  inline bool Read32(uint64_t addr, uint32_t* dst) { return Read(addr, dst, sizeof(uint32_t)); }
 
-  inline bool Read64(uint64_t addr, uint64_t* dst) {
-    return Read(addr, dst, sizeof(uint64_t));
-  }
+  inline bool Read64(uint64_t addr, uint64_t* dst) { return Read(addr, dst, sizeof(uint64_t)); }
 };
 
 class MemoryBuffer : public Memory {
@@ -128,10 +125,7 @@
 
 class MemoryRange : public Memory {
  public:
-  MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
-      : memory_(memory), begin_(begin), length_(end - begin) {
-    assert(end > begin);
-  }
+  MemoryRange(Memory* memory, uint64_t begin, uint64_t end);
   virtual ~MemoryRange() { delete memory_; }
 
   bool Read(uint64_t addr, void* dst, size_t size) override;
@@ -142,4 +136,6 @@
   uint64_t length_;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
similarity index 80%
rename from libunwindstack/Regs.h
rename to libunwindstack/include/unwindstack/Regs.h
index 718fc85..ab98f32 100644
--- a/libunwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -21,9 +21,12 @@
 
 #include <vector>
 
+namespace unwindstack {
+
 // Forward declarations.
 class Elf;
 struct MapInfo;
+class Memory;
 
 class Regs {
  public:
@@ -50,14 +53,17 @@
 
   virtual bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) = 0;
 
-  virtual uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) = 0;
-
   virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;
 
+  virtual void SetFromRaw() = 0;
+
   uint16_t sp_reg() { return sp_reg_; }
   uint16_t total_regs() { return total_regs_; }
 
+  static uint32_t GetMachineType();
   static Regs* RemoteGet(pid_t pid, uint32_t* machine_type);
+  static Regs* CreateFromUcontext(uint32_t machine_type, void* ucontext);
+  static Regs* CreateFromLocal();
 
  protected:
   uint16_t total_regs_;
@@ -66,13 +72,11 @@
 };
 
 template <typename AddressType>
-class RegsTmpl : public Regs {
+class RegsImpl : public Regs {
  public:
-  RegsTmpl(uint16_t total_regs, uint16_t sp_reg, Location return_loc)
+  RegsImpl(uint16_t total_regs, uint16_t sp_reg, Location return_loc)
       : Regs(total_regs, sp_reg, return_loc), regs_(total_regs) {}
-  virtual ~RegsTmpl() = default;
-
-  uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) override;
+  virtual ~RegsImpl() = default;
 
   bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) override;
 
@@ -92,36 +96,46 @@
   std::vector<AddressType> regs_;
 };
 
-class RegsArm : public RegsTmpl<uint32_t> {
+class RegsArm : public RegsImpl<uint32_t> {
  public:
   RegsArm();
   virtual ~RegsArm() = default;
 
   uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+  void SetFromRaw() override;
 };
 
-class RegsArm64 : public RegsTmpl<uint64_t> {
+class RegsArm64 : public RegsImpl<uint64_t> {
  public:
   RegsArm64();
   virtual ~RegsArm64() = default;
 
   uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+  void SetFromRaw() override;
 };
 
-class RegsX86 : public RegsTmpl<uint32_t> {
+class RegsX86 : public RegsImpl<uint32_t> {
  public:
   RegsX86();
   virtual ~RegsX86() = default;
 
   uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+  void SetFromRaw() override;
 };
 
-class RegsX86_64 : public RegsTmpl<uint64_t> {
+class RegsX86_64 : public RegsImpl<uint64_t> {
  public:
   RegsX86_64();
   virtual ~RegsX86_64() = default;
 
   uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+  void SetFromRaw() override;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_REGS_H
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
new file mode 100644
index 0000000..ffec213
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_GET_LOCAL_H
+#define _LIBUNWINDSTACK_REGS_GET_LOCAL_H
+
+namespace unwindstack {
+
+#if defined(__arm__)
+
+inline void RegsGetLocal(Regs* regs) {
+  void* reg_data = regs->RawData();
+  asm volatile(
+      ".align 2\n"
+      "bx pc\n"
+      "nop\n"
+      ".code 32\n"
+      "stmia %[base], {r0-r12}\n"
+      "add %[base], #52\n"
+      "mov r1, r13\n"
+      "mov r2, r14\n"
+      "mov r3, r15\n"
+      "stmia %[base], {r1-r3}\n"
+      "orr %[base], pc, #1\n"
+      "bx %[base]\n"
+      : [base] "+r"(reg_data)
+      :
+      : "memory");
+
+  regs->SetFromRaw();
+}
+
+#elif defined(__aarch64__)
+
+inline void RegsGetLocal(Regs* regs) {
+  void* reg_data = regs->RawData();
+  asm volatile(
+      "1:\n"
+      "stp x0, x1, [%[base], #0]\n"
+      "stp x2, x3, [%[base], #16]\n"
+      "stp x4, x5, [%[base], #32]\n"
+      "stp x6, x7, [%[base], #48]\n"
+      "stp x8, x9, [%[base], #64]\n"
+      "stp x10, x11, [%[base], #80]\n"
+      "stp x12, x13, [%[base], #96]\n"
+      "stp x14, x15, [%[base], #112]\n"
+      "stp x16, x17, [%[base], #128]\n"
+      "stp x18, x19, [%[base], #144]\n"
+      "stp x20, x21, [%[base], #160]\n"
+      "stp x22, x23, [%[base], #176]\n"
+      "stp x24, x25, [%[base], #192]\n"
+      "stp x26, x27, [%[base], #208]\n"
+      "stp x28, x29, [%[base], #224]\n"
+      "str x30, [%[base], #240]\n"
+      "mov x12, sp\n"
+      "adr x13, 1b\n"
+      "stp x12, x13, [%[base], #248]\n"
+      : [base] "+r"(reg_data)
+      :
+      : "x12", "x13", "memory");
+
+  regs->SetFromRaw();
+}
+
+#elif defined(__i386__) || defined(__x86_64__)
+
+extern "C" void AsmGetRegs(void* regs);
+
+inline void RegsGetLocal(Regs* regs) {
+  AsmGetRegs(regs->RawData());
+
+  regs->SetFromRaw();
+}
+
+#endif
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_GET_LOCAL_H
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 4fff48e..94cb493 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -23,13 +23,16 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/Log.h>
+#include <unwindstack/Regs.h>
+
 #include "ArmExidx.h"
-#include "Regs.h"
-#include "Log.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> {
  protected:
   void Init(Memory* process_memory = nullptr) {
@@ -1092,3 +1095,5 @@
 }
 
 INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest, ::testing::Values("logging", "no_logging"));
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
index aed75bf..caad131 100644
--- a/libunwindstack/tests/ArmExidxExtractTest.cpp
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -21,12 +21,15 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/Log.h>
+
 #include "ArmExidx.h"
-#include "Log.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class ArmExidxExtractTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -329,3 +332,5 @@
   ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
   ASSERT_EQ("4 unwind Raw Data: 0x11 0x22 0x33 0xb0\n", GetFakeLogPrint());
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index 3185bc3..b17ca33 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -23,15 +23,18 @@
 #include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+
 #include "DwarfCfa.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
-#include "DwarfStructs.h"
-#include "Log.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
 class DwarfCfaLogTest : public ::testing::Test {
  protected:
@@ -60,8 +63,8 @@
   MemoryFake memory_;
   std::unique_ptr<DwarfMemory> dmem_;
   std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
-  DwarfCIE cie_;
-  DwarfFDE fde_;
+  DwarfCie cie_;
+  DwarfFde fde_;
 };
 TYPED_TEST_CASE_P(DwarfCfaLogTest);
 
@@ -812,3 +815,5 @@
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 6cf028a..73a67ac 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -21,15 +21,19 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+
 #include "DwarfCfa.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
-#include "DwarfStructs.h"
-#include "Log.h"
+#include "DwarfError.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
 class DwarfCfaTest : public ::testing::Test {
  protected:
@@ -57,8 +61,8 @@
   MemoryFake memory_;
   std::unique_ptr<DwarfMemory> dmem_;
   std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
-  DwarfCIE cie_;
-  DwarfFDE fde_;
+  DwarfCie cie_;
+  DwarfFde fde_;
 };
 TYPED_TEST_CASE_P(DwarfCfaTest);
 
@@ -957,3 +961,5 @@
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaTest, DwarfCfaTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
new file mode 100644
index 0000000..69813e5
--- /dev/null
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -0,0 +1,461 @@
+/*
+ * 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 <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+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->offset_ = offset; }
+  void TestSetEndOffset(uint64_t offset) { this->end_offset_ = 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_);
+    ResetLogs();
+  }
+
+  void TearDown() override { delete debug_frame_; }
+
+  MemoryFake memory_;
+  MockDwarfDebugFrame<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');
+
+  // 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, 0xfc);
+  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);
+
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(4U, 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);
+
+  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, 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));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+}
+
+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');
+
+  // 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, 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);
+
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(4U, 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);
+
+  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));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+}
+
+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);
+
+  // 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));
+  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);
+}
+
+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);
+
+  // 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));
+  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);
+}
+
+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);
+  }
+
+  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_->last_error());
+
+  this->debug_frame_->TestSetFdeCount(9);
+  ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+  // 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_->last_error());
+  }
+
+  // Even number of elements.
+  this->debug_frame_->TestSetFdeCount(10);
+  info.start = 0xa000;
+  info.end = 0xaff0;
+  info.offset = 0x5120;
+  this->debug_frame_->TestPushFdeInfo(info);
+
+  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_->last_error());
+  }
+}
+
+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);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x14000);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x9000U, fde->pc_start);
+  EXPECT_EQ(0x9100U, fde->pc_end);
+  EXPECT_EQ(0xf000U, 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_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);
+}
+
+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);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x8000);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
+  EXPECT_EQ(0x5000U, fde->pc_start);
+  EXPECT_EQ(0x5300U, 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(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie, Init64,
+                           Init64_fde_not_following_cie, Init_version1, Init_version4,
+                           GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
new file mode 100644
index 0000000..e9501e3
--- /dev/null
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -0,0 +1,414 @@
+/*
+ * 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 <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "DwarfEhFrame.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class MockDwarfEhFrame : public DwarfEhFrame<TypeParam> {
+ public:
+  MockDwarfEhFrame(Memory* memory) : DwarfEhFrame<TypeParam>(memory) {}
+  ~MockDwarfEhFrame() = default;
+
+  void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; }
+  void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; }
+  void TestSetEntriesEnd(uint64_t end) { this->entries_end_ = end; }
+  void TestSetEntriesDataOffset(uint64_t offset) { this->entries_data_offset_ = offset; }
+  void TestSetCurEntriesOffset(uint64_t offset) { this->cur_entries_offset_ = offset; }
+  void TestSetTableEntrySize(size_t size) { this->table_entry_size_ = size; }
+
+  void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
+  void TestSetFdeInfo(uint64_t index, const typename DwarfEhFrame<TypeParam>::FdeInfo& info) {
+    this->fde_info_[index] = info;
+  }
+
+  uint8_t TestGetVersion() { return this->version_; }
+  uint8_t TestGetPtrEncoding() { return this->ptr_encoding_; }
+  uint64_t TestGetPtrOffset() { return this->ptr_offset_; }
+  uint8_t TestGetTableEncoding() { return this->table_encoding_; }
+  uint64_t TestGetTableEntrySize() { return this->table_entry_size_; }
+  uint64_t TestGetFdeCount() { return this->fde_count_; }
+  uint64_t TestGetEntriesOffset() { return this->entries_offset_; }
+  uint64_t TestGetEntriesEnd() { return this->entries_end_; }
+  uint64_t TestGetEntriesDataOffset() { return this->entries_data_offset_; }
+  uint64_t TestGetCurEntriesOffset() { return this->cur_entries_offset_; }
+};
+
+template <typename TypeParam>
+class DwarfEhFrameTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_.Clear();
+    eh_frame_ = new MockDwarfEhFrame<TypeParam>(&memory_);
+    ResetLogs();
+  }
+
+  void TearDown() override { delete eh_frame_; }
+
+  MemoryFake memory_;
+  MockDwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfEhFrameTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfEhFrameTest, Init) {
+  this->memory_.SetMemory(
+      0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
+  this->memory_.SetData16(0x1004, 0x500);
+  this->memory_.SetData32(0x1006, 126);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100));
+  EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+  EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
+  EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
+  EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+  EXPECT_EQ(126U, this->eh_frame_->TestGetFdeCount());
+  EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
+  EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
+  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+
+  // Verify an unexpected version will cause a fail.
+  this->memory_.SetData8(0x1000, 0);
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
+  this->memory_.SetData8(0x1000, 2);
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_expect_cache_fail) {
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+  ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
+  ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_pcrel) {
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x1380U, info->pc);
+  EXPECT_EQ(0x1540U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_datarel) {
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_datarel | DW_EH_PE_udata4);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x3340U, info->pc);
+  EXPECT_EQ(0x3500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_cached) {
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x340U, info->pc);
+  EXPECT_EQ(0x500U, info->offset);
+
+  // Clear the memory so that this will fail if it doesn't read cached data.
+  this->memory_.Clear();
+
+  info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x340U, info->pc);
+  EXPECT_EQ(0x500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetBinary_verify) {
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+  this->eh_frame_->TestSetFdeCount(10);
+
+  typename DwarfEhFrame<TypeParam>::FdeInfo info;
+  for (size_t i = 0; i < 10; i++) {
+    info.pc = 0x1000 * (i + 1);
+    info.offset = 0x5000 + i * 0x20;
+    this->eh_frame_->TestSetFdeInfo(i, info);
+  }
+
+  uint64_t fde_offset;
+  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10));
+  // Not an error, just not found.
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+  // Even number of elements.
+  for (size_t i = 0; i < 10; i++) {
+    TypeParam pc = 0x1000 * (i + 1);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 10)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 10)) << "Failed at index "
+                                                                              << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 10))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+  }
+  // Odd number of elements.
+  for (size_t i = 0; i < 9; i++) {
+    TypeParam pc = 0x1000 * (i + 1);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 9)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 9)) << "Failed at index "
+                                                                             << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 9))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+  }
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential) {
+  this->eh_frame_->TestSetFdeCount(10);
+  this->eh_frame_->TestSetEntriesDataOffset(0x100);
+  this->eh_frame_->TestSetEntriesEnd(0x2000);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  this->memory_.SetData32(0x1048, 0x440);
+  this->memory_.SetData32(0x104c, 0x600);
+
+  // Verify that if entries is zero, that it fails.
+  uint64_t fde_offset;
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset));
+  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset));
+  EXPECT_EQ(0x500U, fde_offset);
+
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset));
+  EXPECT_EQ(0x600U, fde_offset);
+
+  // Expect that the data is cached so no more memory reads will occur.
+  this->memory_.Clear();
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset));
+  EXPECT_EQ(0x600U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_last_element) {
+  this->eh_frame_->TestSetFdeCount(2);
+  this->eh_frame_->TestSetEntriesDataOffset(0x100);
+  this->eh_frame_->TestSetEntriesEnd(0x2000);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  this->memory_.SetData32(0x1048, 0x440);
+  this->memory_.SetData32(0x104c, 0x600);
+
+  uint64_t fde_offset;
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
+  EXPECT_EQ(0x600U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_end_check) {
+  this->eh_frame_->TestSetFdeCount(2);
+  this->eh_frame_->TestSetEntriesDataOffset(0x100);
+  this->eh_frame_->TestSetEntriesEnd(0x1048);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  this->memory_.SetData32(0x1048, 0x440);
+  this->memory_.SetData32(0x104c, 0x600);
+
+  uint64_t fde_offset;
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_fail_fde_count) {
+  this->eh_frame_->TestSetFdeCount(0);
+
+  uint64_t fde_offset;
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_binary_search) {
+  this->eh_frame_->TestSetTableEntrySize(16);
+  this->eh_frame_->TestSetFdeCount(10);
+
+  typename DwarfEhFrame<TypeParam>::FdeInfo info;
+  info.pc = 0x550;
+  info.offset = 0x10500;
+  this->eh_frame_->TestSetFdeInfo(5, info);
+  info.pc = 0x750;
+  info.offset = 0x10700;
+  this->eh_frame_->TestSetFdeInfo(7, info);
+  info.pc = 0x850;
+  info.offset = 0x10800;
+  this->eh_frame_->TestSetFdeInfo(8, info);
+
+  uint64_t fde_offset;
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x800, &fde_offset));
+  EXPECT_EQ(0x10700U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_sequential_search) {
+  this->eh_frame_->TestSetFdeCount(10);
+  this->eh_frame_->TestSetTableEntrySize(0);
+
+  typename DwarfEhFrame<TypeParam>::FdeInfo info;
+  info.pc = 0x50;
+  info.offset = 0x10000;
+  this->eh_frame_->TestSetFdeInfo(0, info);
+  info.pc = 0x150;
+  info.offset = 0x10100;
+  this->eh_frame_->TestSetFdeInfo(1, info);
+  info.pc = 0x250;
+  info.offset = 0x10200;
+  this->eh_frame_->TestSetFdeInfo(2, info);
+
+  uint64_t fde_offset;
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x200, &fde_offset));
+  EXPECT_EQ(0x10100U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetCieFde32) {
+  // 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(0x1d00cU, fde->pc_start);
+  EXPECT_EQ(0x1d10cU, fde->pc_end);
+  EXPECT_EQ(0xf000U, 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_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);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) {
+  // 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(0xd01cU, fde->pc_start);
+  EXPECT_EQ(0xd31cU, 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, Init, GetFdeInfoFromIndex_expect_cache_fail,
+                           GetFdeInfoFromIndex_read_pcrel, GetFdeInfoFromIndex_read_datarel,
+                           GetFdeInfoFromIndex_cached, GetFdeOffsetBinary_verify,
+                           GetFdeOffsetSequential, GetFdeOffsetSequential_last_element,
+                           GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count,
+                           GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search,
+                           GetCieFde32, GetCieFde64);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
index 4877f36..08fe7cf 100644
--- a/libunwindstack/tests/DwarfMemoryTest.cpp
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -21,10 +21,12 @@
 
 #include <gtest/gtest.h>
 
-#include "DwarfMemory.h"
+#include <unwindstack/DwarfMemory.h>
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class DwarfMemoryTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -237,9 +239,13 @@
   ASSERT_EQ(0U, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) { ReadEncodedValue_omit<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) {
+  ReadEncodedValue_omit<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) { ReadEncodedValue_omit<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) {
+  ReadEncodedValue_omit<uint64_t>();
+}
 
 TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint32_t) {
   uint64_t value = 100;
@@ -302,9 +308,13 @@
   ASSERT_EQ(0xffffffffffffe100ULL, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) { ReadEncodedValue_leb128<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) {
+  ReadEncodedValue_leb128<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) { ReadEncodedValue_leb128<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) {
+  ReadEncodedValue_leb128<uint64_t>();
+}
 
 template <typename AddressType>
 void DwarfMemoryTest::ReadEncodedValue_data1() {
@@ -319,9 +329,13 @@
   ASSERT_EQ(0xffffffffffffffe0ULL, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) { ReadEncodedValue_data1<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) {
+  ReadEncodedValue_data1<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) { ReadEncodedValue_data1<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) {
+  ReadEncodedValue_data1<uint64_t>();
+}
 
 template <typename AddressType>
 void DwarfMemoryTest::ReadEncodedValue_data2() {
@@ -336,9 +350,13 @@
   ASSERT_EQ(0xffffffffffffe000ULL, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) { ReadEncodedValue_data2<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) {
+  ReadEncodedValue_data2<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) { ReadEncodedValue_data2<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) {
+  ReadEncodedValue_data2<uint64_t>();
+}
 
 template <typename AddressType>
 void DwarfMemoryTest::ReadEncodedValue_data4() {
@@ -353,9 +371,13 @@
   ASSERT_EQ(0xffffffffe0000000ULL, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) { ReadEncodedValue_data4<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) {
+  ReadEncodedValue_data4<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) { ReadEncodedValue_data4<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) {
+  ReadEncodedValue_data4<uint64_t>();
+}
 
 template <typename AddressType>
 void DwarfMemoryTest::ReadEncodedValue_data8() {
@@ -370,9 +392,13 @@
   ASSERT_EQ(0xe000000000000000ULL, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) { ReadEncodedValue_data8<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) {
+  ReadEncodedValue_data8<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) { ReadEncodedValue_data8<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) {
+  ReadEncodedValue_data8<uint64_t>();
+}
 
 template <typename AddressType>
 void DwarfMemoryTest::ReadEncodedValue_non_zero_adjust() {
@@ -470,3 +496,5 @@
   ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
   ASSERT_EQ(0x14234U, value);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
index d18aad0..234d1c9 100644
--- a/libunwindstack/tests/DwarfOpLogTest.cpp
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -21,14 +21,17 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Regs.h>
+
 #include "DwarfError.h"
-#include "DwarfMemory.h"
 #include "DwarfOp.h"
-#include "Log.h"
-#include "Regs.h"
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
 class DwarfOpLogTest : public ::testing::Test {
  protected:
@@ -66,3 +69,5 @@
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 520c545..47a40cf 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -21,25 +21,16 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+
 #include "DwarfError.h"
-#include "DwarfMemory.h"
 #include "DwarfOp.h"
-#include "Log.h"
-#include "Regs.h"
 
 #include "MemoryFake.h"
+#include "RegsFake.h"
 
-template <typename TypeParam>
-class RegsFake : public RegsTmpl<TypeParam> {
- public:
-  RegsFake(uint16_t total_regs, uint16_t sp_reg)
-      : RegsTmpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
-  virtual ~RegsFake() = default;
-
-  uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; }
-  uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
-  bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
-};
+namespace unwindstack {
 
 template <typename TypeParam>
 class DwarfOpTest : public ::testing::Test {
@@ -1591,3 +1582,5 @@
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
new file mode 100644
index 0000000..b871539
--- /dev/null
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -0,0 +1,839 @@
+/*
+ * 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 <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfSection.h>
+
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class MockDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
+ public:
+  MockDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
+  virtual ~MockDwarfSectionImpl() = default;
+
+  MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+
+  MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, 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));
+
+  MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
+
+  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(); }
+
+  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_ = DWARF_ERROR_NONE; }
+};
+
+template <typename TypeParam>
+class DwarfSectionImplTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_.Clear();
+    section_ = new MockDwarfSectionImpl<TypeParam>(&memory_);
+    ResetLogs();
+  }
+
+  void TearDown() override { delete section_; }
+
+  MemoryFake memory_;
+  MockDwarfSectionImpl<TypeParam>* section_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfSectionImplTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x96, 0x96, 0x96});
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+  TypeParam cfa_value = 0x12345;
+  this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x12345U, regs.sp());
+  EXPECT_EQ(0x20U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x80000000U, regs.sp());
+  EXPECT_EQ(0x20U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x50, 0x96, 0x96});
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) {
+  DwarfCie cie{.return_address_register = 60};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_CFA_NOT_DEFINED, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+
+  this->section_->TestClearError();
+  loc_regs.erase(CFA_REG);
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_INVALID, {0, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+
+  this->section_->TestClearError();
+  loc_regs.erase(CFA_REG);
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_OFFSET, {0, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+
+  this->section_->TestClearError();
+  loc_regs.erase(CFA_REG);
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {9, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x20U, regs.pc());
+  EXPECT_EQ(0x2000U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_from_value) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[6] = 0x4000;
+  regs[9] = 0x3000;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {6, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x20U, regs.pc());
+  EXPECT_EQ(0x4000U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 0}};
+  loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {10, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  if (sizeof(TypeParam) == sizeof(uint64_t)) {
+    this->memory_.SetData64(0x2150, 0x12345678abcdef00ULL);
+  } else {
+    this->memory_.SetData32(0x2150, 0x12345678);
+  }
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[3] = 0x234;
+  regs[5] = 0x10;
+  regs[8] = 0x2100;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[1] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0x100, 0}};
+  loc_regs[2] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x50, 0}};
+  loc_regs[3] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x10U, regs.pc());
+  EXPECT_EQ(0x2100U, regs.sp());
+  EXPECT_EQ(0x2200U, regs[1]);
+  EXPECT_EQ(0x234U, regs[3]);
+  if (sizeof(TypeParam) == sizeof(uint64_t)) {
+    EXPECT_EQ(0x12345678abcdef00ULL, regs[2]);
+  } else {
+    EXPECT_EQ(0x12345678U, regs[2]);
+  }
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[5] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0U, regs.pc());
+  EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x20U, regs.pc());
+  EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  // This should not result in any errors.
+  loc_regs[20] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x20U, regs.pc());
+  EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[8] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+  TypeParam cfa_value = 0x12345;
+  this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[5] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x3000U, regs.sp());
+  EXPECT_EQ(0x12345U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[8] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[5] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x3000U, regs.sp());
+  EXPECT_EQ(0x80000000U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_same_cfa_same_pc) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x100;
+  regs[8] = 0x2000;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x2000U, regs.sp());
+  EXPECT_EQ(0x100U, 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_->last_error());
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+}
+
+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);
+
+  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  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_->last_error());
+
+  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_->last_error());
+
+  this->memory_.SetData8(0x5008, 0x2);
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+
+  this->memory_.SetData8(0x5008, 0x5);
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+}
+
+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);
+
+  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  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, 0xffffffff);
+  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);
+
+  EXPECT_CALL(*this->section_, IsCie64(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  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);
+
+  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  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});
+
+  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  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});
+
+  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  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_->last_error());
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+}
+
+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_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
+  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_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
+  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_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
+  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_, IsCie64(0x12345678)).WillOnce(::testing::Return(false));
+  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;
+  cie.cfa_instructions_end = 0x3002;
+  DwarfFde fde{};
+  fde.cie = &cie;
+  fde.cie_offset = 0x8000;
+  fde.cfa_instructions_offset = 0x6000;
+  fde.cfa_instructions_end = 0x6002;
+
+  this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x09, 0x02, 0x01});
+  this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
+
+  dwarf_loc_regs_t loc_regs;
+  ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+  ASSERT_EQ(2U, loc_regs.size());
+
+  auto entry = loc_regs.find(2);
+  ASSERT_NE(entry, loc_regs.end());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+  ASSERT_EQ(1U, entry->second.values[0]);
+
+  entry = loc_regs.find(4);
+  ASSERT_NE(entry, loc_regs.end());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+  ASSERT_EQ(3U, entry->second.values[0]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_cached) {
+  DwarfCie cie{};
+  cie.cfa_instructions_offset = 0x3000;
+  cie.cfa_instructions_end = 0x3002;
+  DwarfFde fde{};
+  fde.cie = &cie;
+  fde.cie_offset = 0x8000;
+  fde.cfa_instructions_offset = 0x6000;
+  fde.cfa_instructions_end = 0x6002;
+
+  dwarf_loc_regs_t cie_loc_regs{{6, {DWARF_LOCATION_REGISTER, {4, 0}}}};
+  this->section_->TestSetCachedCieLocRegs(0x8000, cie_loc_regs);
+  this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
+
+  dwarf_loc_regs_t loc_regs;
+  ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+  ASSERT_EQ(2U, loc_regs.size());
+
+  auto entry = loc_regs.find(6);
+  ASSERT_NE(entry, loc_regs.end());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+  ASSERT_EQ(4U, entry->second.values[0]);
+
+  entry = loc_regs.find(4);
+  ASSERT_NE(entry, loc_regs.end());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+  ASSERT_EQ(3U, entry->second.values[0]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Log) {
+  DwarfCie cie{};
+  cie.cfa_instructions_offset = 0x5000;
+  cie.cfa_instructions_end = 0x5001;
+  DwarfFde fde{};
+  fde.cie = &cie;
+  fde.cfa_instructions_offset = 0x6000;
+  fde.cfa_instructions_end = 0x6001;
+
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x00});
+  this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0xc2});
+  ASSERT_TRUE(this->section_->Log(2, 0x1000, 0x1000, &fde));
+
+  ASSERT_EQ(
+      "4 unwind     DW_CFA_nop\n"
+      "4 unwind     Raw Data: 0x00\n"
+      "4 unwind     DW_CFA_restore register(2)\n"
+      "4 unwind     Raw Data: 0xc2\n",
+      GetFakeLogPrint());
+  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_invalid_register, Eval_different_reg_locations, Eval_return_address_undefined,
+    Eval_return_address, Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+    Eval_same_cfa_same_pc, 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);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
new file mode 100644
index 0000000..fc67063
--- /dev/null
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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 <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfSection.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MockDwarfSection : public DwarfSection {
+ public:
+  MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
+  virtual ~MockDwarfSection() = default;
+
+  MOCK_METHOD4(Log, bool(uint8_t, uint64_t, uint64_t, const DwarfFde*));
+
+  MOCK_METHOD4(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*));
+
+  MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
+
+  MOCK_METHOD2(Init, bool(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));
+
+  MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
+};
+
+class DwarfSectionTest : public ::testing::Test {
+ protected:
+  MemoryFake memory_;
+};
+
+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));
+
+  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+}
+
+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));
+
+  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+}
+
+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::_))
+      .WillOnce(::testing::Return(false));
+
+  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+}
+
+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::_))
+      .WillOnce(::testing::Return(true));
+
+  MemoryFake process;
+  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr))
+      .WillOnce(::testing::Return(true));
+
+  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process));
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 67c9a6b..c7ef4a1 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -20,12 +20,15 @@
 
 #include <vector>
 
+#include <unwindstack/Regs.h>
+
 #include "ElfInterfaceArm.h"
 #include "Machine.h"
-#include "Regs.h"
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class ElfInterfaceArmTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -370,3 +373,5 @@
   ASSERT_EQ(0x10U, regs.pc());
   ASSERT_EQ(0x10U, regs[ARM_REG_PC]);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index c31903d..acb7320 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -20,7 +20,9 @@
 
 #include <gtest/gtest.h>
 
-#include "ElfInterface.h"
+#include <unwindstack/ElfInterface.h>
+
+#include "DwarfEncoding.h"
 #include "ElfInterfaceArm.h"
 
 #include "MemoryFake.h"
@@ -33,6 +35,8 @@
 #define EM_AARCH64 183
 #endif
 
+namespace unwindstack {
+
 class ElfInterfaceTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -67,9 +71,49 @@
   template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
   void SonameSize();
 
+  template <typename ElfType>
+  void InitHeadersEhFrameTest();
+
+  template <typename ElfType>
+  void InitHeadersDebugFrame();
+
+  template <typename ElfType>
+  void InitHeadersEhFrameFail();
+
+  template <typename ElfType>
+  void InitHeadersDebugFrameFail();
+
+  template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+  void InitSectionHeadersMalformed();
+
+  template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
+  void InitSectionHeaders(uint64_t entry_size);
+
+  template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+  void InitSectionHeadersOffsets();
+
+  template <typename Sym>
+  void InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
+               uint64_t sym_offset, const char* name);
+
   MemoryFake memory_;
 };
 
+template <typename Sym>
+void ElfInterfaceTest::InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
+                               uint64_t sym_offset, const char* name) {
+  Sym sym;
+  memset(&sym, 0, sizeof(sym));
+  sym.st_info = STT_FUNC;
+  sym.st_value = value;
+  sym.st_size = size;
+  sym.st_name = name_offset;
+  sym.st_shndx = SHN_COMMON;
+
+  memory_.SetMemory(offset, &sym, sizeof(sym));
+  memory_.SetMemory(sym_offset + name_offset, name, strlen(name) + 1);
+}
+
 template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
 void ElfInterfaceTest::SinglePtLoad() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
@@ -571,3 +615,315 @@
 TEST_F(ElfInterfaceTest, elf64_soname_size) {
   SonameSize<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
 }
+
+class MockElfInterface32 : public ElfInterface32 {
+ public:
+  MockElfInterface32(Memory* memory) : ElfInterface32(memory) {}
+  virtual ~MockElfInterface32() = default;
+
+  void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+  void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+
+  void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+  void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+class MockElfInterface64 : public ElfInterface64 {
+ public:
+  MockElfInterface64(Memory* memory) : ElfInterface64(memory) {}
+  virtual ~MockElfInterface64() = default;
+
+  void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+  void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+
+  void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+  void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersEhFrameTest() {
+  ElfType elf(&memory_);
+
+  elf.TestSetEhFrameOffset(0x10000);
+  elf.TestSetEhFrameSize(0);
+  elf.TestSetDebugFrameOffset(0);
+  elf.TestSetDebugFrameSize(0);
+
+  memory_.SetMemory(0x10000,
+                    std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata2, DW_EH_PE_udata2});
+  memory_.SetData32(0x10004, 0x500);
+  memory_.SetData32(0x10008, 250);
+
+  elf.InitHeaders();
+
+  EXPECT_FALSE(elf.eh_frame() == nullptr);
+  EXPECT_TRUE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame32) {
+  InitHeadersEhFrameTest<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame64) {
+  InitHeadersEhFrameTest<MockElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersDebugFrame() {
+  ElfType elf(&memory_);
+
+  elf.TestSetEhFrameOffset(0);
+  elf.TestSetEhFrameSize(0);
+  elf.TestSetDebugFrameOffset(0x5000);
+  elf.TestSetDebugFrameSize(0x200);
+
+  memory_.SetData32(0x5000, 0xfc);
+  memory_.SetData32(0x5004, 0xffffffff);
+  memory_.SetData8(0x5008, 1);
+  memory_.SetData8(0x5009, '\0');
+
+  memory_.SetData32(0x5100, 0xfc);
+  memory_.SetData32(0x5104, 0);
+  memory_.SetData32(0x5108, 0x1500);
+  memory_.SetData32(0x510c, 0x200);
+
+  elf.InitHeaders();
+
+  EXPECT_TRUE(elf.eh_frame() == nullptr);
+  EXPECT_FALSE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame32) {
+  InitHeadersDebugFrame<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame64) {
+  InitHeadersDebugFrame<MockElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersEhFrameFail() {
+  ElfType elf(&memory_);
+
+  elf.TestSetEhFrameOffset(0x1000);
+  elf.TestSetEhFrameSize(0x100);
+  elf.TestSetDebugFrameOffset(0);
+  elf.TestSetDebugFrameSize(0);
+
+  elf.InitHeaders();
+
+  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<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame64_fail) {
+  InitHeadersEhFrameFail<MockElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersDebugFrameFail() {
+  ElfType elf(&memory_);
+
+  elf.TestSetEhFrameOffset(0);
+  elf.TestSetEhFrameSize(0);
+  elf.TestSetDebugFrameOffset(0x1000);
+  elf.TestSetDebugFrameSize(0x100);
+
+  elf.InitHeaders();
+
+  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<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame64_fail) {
+  InitHeadersDebugFrameFail<MockElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersMalformed() {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  Ehdr ehdr;
+  memset(&ehdr, 0, sizeof(ehdr));
+  ehdr.e_shoff = 0x1000;
+  ehdr.e_shnum = 10;
+  ehdr.e_shentsize = sizeof(Shdr);
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  ASSERT_TRUE(elf->Init());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed32) {
+  InitSectionHeadersMalformed<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed64) {
+  InitSectionHeadersMalformed<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeaders(uint64_t entry_size) {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x1000;
+
+  Ehdr ehdr;
+  memset(&ehdr, 0, sizeof(ehdr));
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 10;
+  ehdr.e_shentsize = entry_size;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  Shdr shdr;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_SYMTAB;
+  shdr.sh_link = 4;
+  shdr.sh_addr = 0x5000;
+  shdr.sh_offset = 0x5000;
+  shdr.sh_entsize = sizeof(Sym);
+  shdr.sh_size = shdr.sh_entsize * 10;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_DYNSYM;
+  shdr.sh_link = 4;
+  shdr.sh_addr = 0x6000;
+  shdr.sh_offset = 0x6000;
+  shdr.sh_entsize = sizeof(Sym);
+  shdr.sh_size = shdr.sh_entsize * 10;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_name = 0xa000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for the entries.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  InitSym<Sym>(0x5000, 0x90000, 0x1000, 0x100, 0xf000, "function_one");
+  InitSym<Sym>(0x6000, 0xd0000, 0x1000, 0x300, 0xf000, "function_two");
+
+  ASSERT_TRUE(elf->Init());
+  EXPECT_EQ(0U, elf->debug_frame_offset());
+  EXPECT_EQ(0U, elf->debug_frame_size());
+  EXPECT_EQ(0U, elf->gnu_debugdata_offset());
+  EXPECT_EQ(0U, elf->gnu_debugdata_size());
+
+  // Look in the first symbol table.
+  std::string name;
+  uint64_t name_offset;
+  ASSERT_TRUE(elf->GetFunctionName(0x90010, &name, &name_offset));
+  EXPECT_EQ("function_one", name);
+  EXPECT_EQ(16U, name_offset);
+  ASSERT_TRUE(elf->GetFunctionName(0xd0020, &name, &name_offset));
+  EXPECT_EQ("function_two", name);
+  EXPECT_EQ(32U, name_offset);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers32) {
+  InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(sizeof(Elf32_Shdr));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers64) {
+  InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(sizeof(Elf64_Shdr));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size32) {
+  InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(0x100);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size64) {
+  InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(0x100);
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersOffsets() {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x2000;
+
+  Ehdr ehdr;
+  memset(&ehdr, 0, sizeof(ehdr));
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 10;
+  ehdr.e_shentsize = sizeof(Shdr);
+  ehdr.e_shstrndx = 2;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  Shdr shdr;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_link = 2;
+  shdr.sh_name = 0x200;
+  shdr.sh_addr = 0x5000;
+  shdr.sh_offset = 0x5000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = 0x800;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for section header names.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_link = 2;
+  shdr.sh_name = 0x100;
+  shdr.sh_addr = 0x6000;
+  shdr.sh_offset = 0x6000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = 0x500;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame"));
+  memory_.SetMemory(0xf200, ".gnu_debugdata", sizeof(".gnu_debugdata"));
+
+  ASSERT_TRUE(elf->Init());
+  EXPECT_EQ(0x6000U, elf->debug_frame_offset());
+  EXPECT_EQ(0x500U, elf->debug_frame_size());
+  EXPECT_EQ(0x5000U, elf->gnu_debugdata_offset());
+  EXPECT_EQ(0x800U, elf->gnu_debugdata_size());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets32) {
+  InitSectionHeadersOffsets<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets64) {
+  InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 25fec8e..72ceb85 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -15,20 +15,24 @@
  */
 
 #include <elf.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <gtest/gtest.h>
 
-#include "Elf.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
 
+#include "ElfTestUtils.h"
 #include "MemoryFake.h"
 
 #if !defined(PT_ARM_EXIDX)
 #define PT_ARM_EXIDX 0x70000001
 #endif
 
-#if !defined(EM_AARCH64)
-#define EM_AARCH64 183
-#endif
+namespace unwindstack {
 
 class ElfTest : public ::testing::Test {
  protected:
@@ -36,35 +40,16 @@
     memory_ = new MemoryFake;
   }
 
-  template <typename Ehdr>
-  void InitEhdr(Ehdr* ehdr) {
-    memset(ehdr, 0, sizeof(Ehdr));
-    memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
-    ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
-    ehdr->e_ident[EI_VERSION] = EV_CURRENT;
-    ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
-  }
-
-  void InitElf32(uint32_t type) {
+  void InitElf32(uint32_t machine_type) {
     Elf32_Ehdr ehdr;
+    TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, machine_type);
 
-    InitEhdr<Elf32_Ehdr>(&ehdr);
-    ehdr.e_ident[EI_CLASS] = ELFCLASS32;
-
-    ehdr.e_type = ET_DYN;
-    ehdr.e_machine = type;
-    ehdr.e_version = EV_CURRENT;
-    ehdr.e_entry = 0;
     ehdr.e_phoff = 0x100;
-    ehdr.e_shoff = 0;
-    ehdr.e_flags = 0;
     ehdr.e_ehsize = sizeof(ehdr);
     ehdr.e_phentsize = sizeof(Elf32_Phdr);
     ehdr.e_phnum = 1;
     ehdr.e_shentsize = sizeof(Elf32_Shdr);
-    ehdr.e_shnum = 0;
-    ehdr.e_shstrndx = 0;
-    if (type == EM_ARM) {
+    if (machine_type == EM_ARM) {
       ehdr.e_flags = 0x5000200;
       ehdr.e_phnum = 2;
     }
@@ -73,16 +58,13 @@
     Elf32_Phdr phdr;
     memset(&phdr, 0, sizeof(phdr));
     phdr.p_type = PT_LOAD;
-    phdr.p_offset = 0;
-    phdr.p_vaddr = 0;
-    phdr.p_paddr = 0;
     phdr.p_filesz = 0x10000;
     phdr.p_memsz = 0x10000;
     phdr.p_flags = PF_R | PF_X;
     phdr.p_align = 0x1000;
     memory_->SetMemory(0x100, &phdr, sizeof(phdr));
 
-    if (type == EM_ARM) {
+    if (machine_type == EM_ARM) {
       memset(&phdr, 0, sizeof(phdr));
       phdr.p_type = PT_ARM_EXIDX;
       phdr.p_offset = 0x30000;
@@ -96,33 +78,21 @@
     }
   }
 
-  void InitElf64(uint32_t type) {
+  void InitElf64(uint32_t machine_type) {
     Elf64_Ehdr ehdr;
+    TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, machine_type);
 
-    InitEhdr<Elf64_Ehdr>(&ehdr);
-    ehdr.e_ident[EI_CLASS] = ELFCLASS64;
-
-    ehdr.e_type = ET_DYN;
-    ehdr.e_machine = type;
-    ehdr.e_version = EV_CURRENT;
-    ehdr.e_entry = 0;
     ehdr.e_phoff = 0x100;
-    ehdr.e_shoff = 0;
     ehdr.e_flags = 0x5000200;
     ehdr.e_ehsize = sizeof(ehdr);
     ehdr.e_phentsize = sizeof(Elf64_Phdr);
     ehdr.e_phnum = 1;
     ehdr.e_shentsize = sizeof(Elf64_Shdr);
-    ehdr.e_shnum = 0;
-    ehdr.e_shstrndx = 0;
     memory_->SetMemory(0, &ehdr, sizeof(ehdr));
 
     Elf64_Phdr phdr;
     memset(&phdr, 0, sizeof(phdr));
     phdr.p_type = PT_LOAD;
-    phdr.p_offset = 0;
-    phdr.p_vaddr = 0;
-    phdr.p_paddr = 0;
     phdr.p_filesz = 0x10000;
     phdr.p_memsz = 0x10000;
     phdr.p_flags = PF_R | PF_X;
@@ -208,3 +178,95 @@
   ASSERT_EQ(ELFCLASS64, elf.class_type());
   ASSERT_TRUE(elf.interface() != nullptr);
 }
+
+TEST_F(ElfTest, gnu_debugdata_init_fail32) {
+  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
+                                               [&](uint64_t offset, const void* ptr, size_t size) {
+                                                 memory_->SetMemory(offset, ptr, size);
+                                               });
+
+  Elf elf(memory_);
+  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.interface() != nullptr);
+  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+  EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
+  EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
+}
+
+TEST_F(ElfTest, gnu_debugdata_init_fail64) {
+  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
+                                               [&](uint64_t offset, const void* ptr, size_t size) {
+                                                 memory_->SetMemory(offset, ptr, size);
+                                               });
+
+  Elf elf(memory_);
+  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.interface() != nullptr);
+  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+  EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
+  EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
+}
+
+TEST_F(ElfTest, gnu_debugdata_init32) {
+  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
+                                               [&](uint64_t offset, const void* ptr, size_t size) {
+                                                 memory_->SetMemory(offset, ptr, size);
+                                               });
+
+  Elf elf(memory_);
+  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.interface() != nullptr);
+  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+  EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
+  EXPECT_EQ(0x8cU, elf.interface()->gnu_debugdata_size());
+
+  elf.InitGnuDebugdata();
+  ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
+}
+
+TEST_F(ElfTest, gnu_debugdata_init64) {
+  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
+                                               [&](uint64_t offset, const void* ptr, size_t size) {
+                                                 memory_->SetMemory(offset, ptr, size);
+                                               });
+
+  Elf elf(memory_);
+  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.interface() != nullptr);
+  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+  EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
+  EXPECT_EQ(0x90U, elf.interface()->gnu_debugdata_size());
+
+  elf.InitGnuDebugdata();
+  ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
+}
+
+class MockElf : public Elf {
+ public:
+  MockElf(Memory* memory) : Elf(memory) {}
+  virtual ~MockElf() = default;
+
+  void set_valid(bool valid) { valid_ = valid; }
+  void set_elf_interface(ElfInterface* interface) { interface_.reset(interface); }
+};
+
+TEST_F(ElfTest, rel_pc) {
+  MockElf elf(memory_);
+
+  ElfInterface* interface = new ElfInterface32(memory_);
+  elf.set_elf_interface(interface);
+
+  elf.set_valid(true);
+  interface->set_load_bias(0);
+  MapInfo map_info{.start = 0x1000, .end = 0x2000};
+
+  ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+
+  interface->set_load_bias(0x3000);
+  ASSERT_EQ(0x3101U, elf.GetRelPc(0x1101, &map_info));
+
+  elf.set_valid(false);
+  ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.cpp b/libunwindstack/tests/ElfTestUtils.cpp
new file mode 100644
index 0000000..069386b
--- /dev/null
+++ b/libunwindstack/tests/ElfTestUtils.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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 <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "ElfTestUtils.h"
+
+namespace unwindstack {
+
+template <typename Ehdr>
+void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type) {
+  memset(ehdr, 0, sizeof(Ehdr));
+  memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
+  ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+  ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+  ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
+  ehdr->e_ident[EI_CLASS] = elf_class;
+  ehdr->e_type = ET_DYN;
+  ehdr->e_machine = machine_type;
+  ehdr->e_version = EV_CURRENT;
+  ehdr->e_ehsize = sizeof(Ehdr);
+}
+
+static std::string GetTestFileDirectory() {
+  std::string exec(testing::internal::GetArgvs()[0]);
+  auto const value = exec.find_last_of('/');
+  if (value == std::string::npos) {
+    return "tests/files/";
+  }
+  return exec.substr(0, value + 1) + "tests/files/";
+}
+
+template <typename Ehdr, typename Shdr>
+void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine, bool init_gnu_debugdata,
+                          TestCopyFuncType copy_func) {
+  Ehdr ehdr;
+
+  TestInitEhdr(&ehdr, elf_class, machine);
+
+  uint64_t offset = sizeof(Ehdr);
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 3;
+  ehdr.e_shentsize = sizeof(Shdr);
+  ehdr.e_shstrndx = 2;
+  copy_func(0, &ehdr, sizeof(ehdr));
+
+  Shdr shdr;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_NULL;
+  copy_func(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // Skip this header, it will contain the gnu_debugdata information.
+  uint64_t gnu_offset = offset;
+  offset += ehdr.e_shentsize;
+
+  uint64_t symtab_offset = sizeof(ehdr) + ehdr.e_shnum * ehdr.e_shentsize;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_name = 1;
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_offset = symtab_offset;
+  shdr.sh_size = 0x100;
+  copy_func(offset, &shdr, sizeof(shdr));
+
+  char value = '\0';
+  uint64_t symname_offset = symtab_offset;
+  copy_func(symname_offset, &value, 1);
+  symname_offset++;
+  std::string name(".shstrtab");
+  copy_func(symname_offset, name.c_str(), name.size() + 1);
+  symname_offset += name.size() + 1;
+  name = ".gnu_debugdata";
+  copy_func(symname_offset, name.c_str(), name.size() + 1);
+
+  ssize_t bytes = 0x100;
+  offset = symtab_offset + 0x100;
+  if (init_gnu_debugdata) {
+    // Read in the compressed elf data and copy it in.
+    name = GetTestFileDirectory();
+    if (elf_class == ELFCLASS32) {
+      name += "elf32.xz";
+    } else {
+      name += "elf64.xz";
+    }
+    int fd = TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY));
+    ASSERT_NE(-1, fd) << "Cannot open " + name;
+    // Assumes the file is less than 1024 bytes.
+    std::vector<uint8_t> buf(1024);
+    bytes = TEMP_FAILURE_RETRY(read(fd, buf.data(), buf.size()));
+    ASSERT_GT(bytes, 0);
+    // Make sure the file isn't too big.
+    ASSERT_NE(static_cast<size_t>(bytes), buf.size())
+        << "File " + name + " is too big, increase buffer size.";
+    close(fd);
+    buf.resize(bytes);
+    copy_func(offset, buf.data(), buf.size());
+  }
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_name = symname_offset - symtab_offset;
+  shdr.sh_addr = offset;
+  shdr.sh_offset = offset;
+  shdr.sh_size = bytes;
+  copy_func(gnu_offset, &shdr, sizeof(shdr));
+}
+
+template void TestInitEhdr<Elf32_Ehdr>(Elf32_Ehdr*, uint32_t, uint32_t);
+template void TestInitEhdr<Elf64_Ehdr>(Elf64_Ehdr*, uint32_t, uint32_t);
+
+template void TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(uint32_t, uint32_t, bool,
+                                                           TestCopyFuncType);
+template void TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(uint32_t, uint32_t, bool,
+                                                           TestCopyFuncType);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.h b/libunwindstack/tests/ElfTestUtils.h
new file mode 100644
index 0000000..6ef00e1
--- /dev/null
+++ b/libunwindstack/tests/ElfTestUtils.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
+#define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
+
+#include <functional>
+
+namespace unwindstack {
+
+typedef std::function<void(uint64_t, const void*, size_t)> TestCopyFuncType;
+
+template <typename Ehdr>
+void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type);
+
+template <typename Ehdr, typename Shdr>
+void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine_type, bool init_gnu_debudata,
+                          TestCopyFuncType copy_func);
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
diff --git a/libunwindstack/tests/GenGnuDebugdata.cpp b/libunwindstack/tests/GenGnuDebugdata.cpp
new file mode 100644
index 0000000..2644582
--- /dev/null
+++ b/libunwindstack/tests/GenGnuDebugdata.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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 <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#if !defined(EM_AARCH64)
+#define EM_AARCH64 183
+#endif
+
+template <typename Ehdr>
+void InitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine) {
+  memset(ehdr, 0, sizeof(Ehdr));
+  memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
+  ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+  ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+  ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
+  ehdr->e_ident[EI_CLASS] = elf_class;
+  ehdr->e_type = ET_DYN;
+  ehdr->e_machine = machine;
+  ehdr->e_version = EV_CURRENT;
+  ehdr->e_ehsize = sizeof(Ehdr);
+}
+
+template <typename Ehdr, typename Shdr>
+void GenElf(Ehdr* ehdr, int fd) {
+  uint64_t offset = sizeof(Ehdr);
+  ehdr->e_shoff = offset;
+  ehdr->e_shnum = 3;
+  ehdr->e_shentsize = sizeof(Shdr);
+  ehdr->e_shstrndx = 2;
+  TEMP_FAILURE_RETRY(write(fd, ehdr, sizeof(Ehdr)));
+
+  Shdr shdr;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_name = 0;
+  shdr.sh_type = SHT_NULL;
+  TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr)));
+  offset += ehdr->e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_name = 11;
+  shdr.sh_addr = 0x5000;
+  shdr.sh_offset = 0x5000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = 0x800;
+  TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr)));
+  offset += ehdr->e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 1;
+  shdr.sh_offset = 0x200;
+  shdr.sh_size = 24;
+  TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr)));
+
+  // Write out the name entries information.
+  lseek(fd, 0x200, SEEK_SET);
+  std::string name;
+  TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1));
+  name = ".shstrtab";
+  TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1));
+  name = ".debug_frame";
+  TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1));
+}
+
+int main() {
+  int elf32_fd = TEMP_FAILURE_RETRY(open("elf32", O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+  if (elf32_fd == -1) {
+    printf("Failed to create elf32: %s\n", strerror(errno));
+    return 1;
+  }
+
+  int elf64_fd = TEMP_FAILURE_RETRY(open("elf64", O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+  if (elf64_fd == -1) {
+    printf("Failed to create elf64: %s\n", strerror(errno));
+    return 1;
+  }
+
+  Elf32_Ehdr ehdr32;
+  InitEhdr<Elf32_Ehdr>(&ehdr32, ELFCLASS32, EM_ARM);
+  GenElf<Elf32_Ehdr, Elf32_Shdr>(&ehdr32, elf32_fd);
+  close(elf32_fd);
+
+  Elf64_Ehdr ehdr64;
+  InitEhdr<Elf64_Ehdr>(&ehdr64, ELFCLASS64, EM_AARCH64);
+  GenElf<Elf64_Ehdr, Elf64_Shdr>(&ehdr64, elf64_fd);
+  close(elf64_fd);
+}
diff --git a/libunwindstack/tests/LogFake.cpp b/libunwindstack/tests/LogFake.cpp
index 411594a..537ccaf 100644
--- a/libunwindstack/tests/LogFake.cpp
+++ b/libunwindstack/tests/LogFake.cpp
@@ -25,7 +25,6 @@
 #include "LogFake.h"
 
 // Forward declarations.
-class Backtrace;
 struct EventTagMap;
 struct AndroidLogEntry;
 
@@ -33,6 +32,8 @@
 
 std::string g_fake_log_print;
 
+namespace unwindstack {
+
 void ResetLogs() {
   g_fake_log_buf = "";
   g_fake_log_print = "";
@@ -46,6 +47,8 @@
   return g_fake_log_print;
 }
 
+}  // namespace unwindstack
+
 extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
   g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
   g_fake_log_buf += tag;
diff --git a/libunwindstack/tests/LogFake.h b/libunwindstack/tests/LogFake.h
index 006d393..e1dc50d 100644
--- a/libunwindstack/tests/LogFake.h
+++ b/libunwindstack/tests/LogFake.h
@@ -19,8 +19,12 @@
 
 #include <string>
 
+namespace unwindstack {
+
 void ResetLogs();
 std::string GetFakeLogBuf();
 std::string GetFakeLogPrint();
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
similarity index 83%
rename from libunwindstack/tests/MapInfoTest.cpp
rename to libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index c846ad7..9e45e78 100644
--- a/libunwindstack/tests/MapInfoTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -30,11 +30,13 @@
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
-#include "Elf.h"
-#include "MapInfo.h"
-#include "Memory.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
 
-class MapInfoTest : public ::testing::Test {
+namespace unwindstack {
+
+class MapInfoCreateMemoryTest : public ::testing::Test {
  protected:
   static void SetUpTestCase() {
     std::vector<uint8_t> buffer(1024);
@@ -58,10 +60,10 @@
 
   static TemporaryFile elf_at_100_;
 };
-TemporaryFile MapInfoTest::elf_;
-TemporaryFile MapInfoTest::elf_at_100_;
+TemporaryFile MapInfoCreateMemoryTest::elf_;
+TemporaryFile MapInfoCreateMemoryTest::elf_at_100_;
 
-TEST_F(MapInfoTest, end_le_start) {
+TEST_F(MapInfoCreateMemoryTest, end_le_start) {
   MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
 
   std::unique_ptr<Memory> memory;
@@ -75,12 +77,12 @@
   // Make sure this test is valid.
   info.end = 0x101;
   memory.reset(info.CreateMemory(getpid()));
-  ASSERT_FALSE(info.CreateMemory(getpid()) == nullptr);
+  ASSERT_TRUE(memory.get() != nullptr);
 }
 
 // Verify that if the offset is non-zero but there is no elf at the offset,
 // that the full file is used.
-TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_full_file) {
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
   MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path};
 
   std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
@@ -100,7 +102,7 @@
 
 // Verify that if the offset is non-zero and there is an elf at that
 // offset, that only part of the file is used.
-TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_partial_file) {
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
   MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path};
 
   std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
@@ -119,7 +121,7 @@
 }
 
 // Verify that device file names will never result in Memory object creation.
-TEST_F(MapInfoTest, create_memory_check_device_maps) {
+TEST_F(MapInfoCreateMemoryTest, check_device_maps) {
   // Set up some memory so that a valid local memory object would
   // be returned if the file mapping fails, but the device check is incorrect.
   std::vector<uint8_t> buffer(1024);
@@ -135,7 +137,7 @@
   ASSERT_TRUE(memory.get() == nullptr);
 }
 
-TEST_F(MapInfoTest, create_memory_local_memory) {
+TEST_F(MapInfoCreateMemoryTest, local_memory) {
   // Set up some memory for a valid local memory object.
   std::vector<uint8_t> buffer(1024);
   for (size_t i = 0; i < buffer.size(); i++) {
@@ -160,7 +162,7 @@
   ASSERT_FALSE(memory->Read(read_buffer.size(), read_buffer.data(), 1));
 }
 
-TEST_F(MapInfoTest, create_memory_remote_memory) {
+TEST_F(MapInfoCreateMemoryTest, remote_memory) {
   std::vector<uint8_t> buffer(1024);
   memset(buffer.data(), 0xa, buffer.size());
 
@@ -201,20 +203,7 @@
   ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
 
   kill(pid, SIGKILL);
+  ASSERT_EQ(pid, wait(nullptr));
 }
 
-TEST_F(MapInfoTest, get_elf) {
-  // Create a map to use as initialization data.
-  void* map = mmap(nullptr, 1024, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-  ASSERT_NE(MAP_FAILED, map);
-
-  uint64_t start = reinterpret_cast<uint64_t>(map);
-  MapInfo info{.start = start, .end = start + 1024, .offset = 0, .name = ""};
-
-  // The map contains garbage, but this should still produce an elf object.
-  Elf* elf = info.GetElf(getpid(), false);
-  ASSERT_TRUE(elf != nullptr);
-  ASSERT_FALSE(elf->valid());
-
-  ASSERT_EQ(0, munmap(map, 1024));
-}
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
new file mode 100644
index 0000000..abfa172
--- /dev/null
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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 <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfTestUtils.h"
+
+namespace unwindstack {
+
+class MapInfoGetElfTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    map_ = mmap(nullptr, kMapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+    ASSERT_NE(MAP_FAILED, map_);
+
+    uint64_t start = reinterpret_cast<uint64_t>(map_);
+    info_.reset(new MapInfo{.start = start, .end = start + 1024, .offset = 0, .name = ""});
+  }
+
+  void TearDown() override { munmap(map_, kMapSize); }
+
+  const size_t kMapSize = 4096;
+
+  void* map_ = nullptr;
+  std::unique_ptr<MapInfo> info_;
+};
+
+TEST_F(MapInfoGetElfTest, invalid) {
+  // The map is empty, but this should still create an invalid elf object.
+  std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+  ASSERT_TRUE(elf.get() != nullptr);
+  ASSERT_FALSE(elf->valid());
+}
+
+TEST_F(MapInfoGetElfTest, valid32) {
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memcpy(map_, &ehdr, sizeof(ehdr));
+
+  std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+  ASSERT_TRUE(elf.get() != nullptr);
+  ASSERT_TRUE(elf->valid());
+  EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+  EXPECT_EQ(ELFCLASS32, elf->class_type());
+}
+
+TEST_F(MapInfoGetElfTest, valid64) {
+  Elf64_Ehdr ehdr;
+  TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+  memcpy(map_, &ehdr, sizeof(ehdr));
+
+  std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+  ASSERT_TRUE(elf.get() != nullptr);
+  ASSERT_TRUE(elf->valid());
+  EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+  EXPECT_EQ(ELFCLASS64, elf->class_type());
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
+  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(
+      ELFCLASS32, EM_ARM, false, [&](uint64_t offset, const void* ptr, size_t size) {
+        memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
+      });
+
+  std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+  ASSERT_TRUE(elf.get() != nullptr);
+  ASSERT_TRUE(elf->valid());
+  EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+  EXPECT_EQ(ELFCLASS32, elf->class_type());
+  EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
+  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(
+      ELFCLASS64, EM_AARCH64, false, [&](uint64_t offset, const void* ptr, size_t size) {
+        memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
+      });
+
+  std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+  ASSERT_TRUE(elf.get() != nullptr);
+  ASSERT_TRUE(elf->valid());
+  EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+  EXPECT_EQ(ELFCLASS64, elf->class_type());
+  EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
+  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(
+      ELFCLASS32, EM_ARM, true, [&](uint64_t offset, const void* ptr, size_t size) {
+        memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
+      });
+
+  std::unique_ptr<Elf> elf(info_->GetElf(getpid(), true));
+  ASSERT_TRUE(elf.get() != nullptr);
+  ASSERT_TRUE(elf->valid());
+  EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+  EXPECT_EQ(ELFCLASS32, elf->class_type());
+  EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
+  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(
+      ELFCLASS64, EM_AARCH64, true, [&](uint64_t offset, const void* ptr, size_t size) {
+        memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
+      });
+
+  std::unique_ptr<Elf> elf(info_->GetElf(getpid(), true));
+  ASSERT_TRUE(elf.get() != nullptr);
+  ASSERT_TRUE(elf->valid());
+  EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+  EXPECT_EQ(ELFCLASS64, elf->class_type());
+  EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 7eb9bae..9430cf3 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -20,7 +20,9 @@
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
-#include "Maps.h"
+#include <unwindstack/Maps.h>
+
+namespace unwindstack {
 
 TEST(MapsTest, parse_permissions) {
   BufferMaps maps(
@@ -235,3 +237,5 @@
   ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
   ASSERT_EQ("/system/lib/fake5.so", info->name);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryBuffer.cpp b/libunwindstack/tests/MemoryBufferTest.cpp
similarity index 96%
rename from libunwindstack/tests/MemoryBuffer.cpp
rename to libunwindstack/tests/MemoryBufferTest.cpp
index af3d6b9..50a8a1b 100644
--- a/libunwindstack/tests/MemoryBuffer.cpp
+++ b/libunwindstack/tests/MemoryBufferTest.cpp
@@ -18,10 +18,12 @@
 
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
 
 #include "LogFake.h"
 
+namespace unwindstack {
+
 class MemoryBufferTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -75,3 +77,5 @@
 
   ASSERT_FALSE(memory_->Read(UINT64_MAX - 100, buffer.data(), 200));
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
index afb1029..2026acc 100644
--- a/libunwindstack/tests/MemoryFake.cpp
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -21,6 +21,8 @@
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
   const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
   for (size_t i = 0; i < length; i++, addr++) {
@@ -44,3 +46,5 @@
   }
   return true;
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index 70ef30a..d374261 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -23,7 +23,9 @@
 #include <vector>
 #include <unordered_map>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 class MemoryFake : public Memory {
  public:
@@ -87,4 +89,6 @@
   }
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
index aa7a23a..a204bae 100644
--- a/libunwindstack/tests/MemoryFileTest.cpp
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -21,7 +21,9 @@
 #include <android-base/file.h>
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 class MemoryFileTest : public ::testing::Test {
  protected:
@@ -269,3 +271,5 @@
     ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i;
   }
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
index ab999da..73eebdd 100644
--- a/libunwindstack/tests/MemoryLocalTest.cpp
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -21,7 +21,9 @@
 
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 TEST(MemoryLocalTest, read) {
   std::vector<uint8_t> src(1024);
@@ -64,3 +66,5 @@
   uint64_t value;
   ASSERT_FALSE(local.Read(reinterpret_cast<uint64_t>(&value), dst.data(), SIZE_MAX));
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index ee5ba01..6d1366c 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -22,25 +22,19 @@
 
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
 
 #include "MemoryFake.h"
 
-class MemoryRangeTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    memory_ = new MemoryFake;
-  }
+namespace unwindstack {
 
-  MemoryFake* memory_;
-};
-
-TEST_F(MemoryRangeTest, read) {
+TEST(MemoryRangeTest, read) {
   std::vector<uint8_t> src(1024);
   memset(src.data(), 0x4c, 1024);
-  memory_->SetMemory(9001, src);
+  MemoryFake* memory = new MemoryFake;
+  memory->SetMemory(9001, src);
 
-  MemoryRange range(memory_, 9001, 9001 + src.size());
+  MemoryRange range(memory, 9001, 9001 + src.size());
 
   std::vector<uint8_t> dst(1024);
   ASSERT_TRUE(range.Read(0, dst.data(), src.size()));
@@ -49,12 +43,13 @@
   }
 }
 
-TEST_F(MemoryRangeTest, read_near_limit) {
+TEST(MemoryRangeTest, read_near_limit) {
   std::vector<uint8_t> src(4096);
   memset(src.data(), 0x4c, 4096);
-  memory_->SetMemory(1000, src);
+  MemoryFake* memory = new MemoryFake;
+  memory->SetMemory(1000, src);
 
-  MemoryRange range(memory_, 1000, 2024);
+  MemoryRange range(memory, 1000, 2024);
 
   std::vector<uint8_t> dst(1024);
   ASSERT_TRUE(range.Read(1020, dst.data(), 4));
@@ -71,9 +66,11 @@
   ASSERT_TRUE(range.Read(1020, dst.data(), 4));
 }
 
-TEST_F(MemoryRangeTest, read_overflow) {
+TEST(MemoryRangeTest, read_overflow) {
   std::vector<uint8_t> buffer(100);
 
   std::unique_ptr<MemoryRange> overflow(new MemoryRange(new MemoryFakeAlwaysReadZero, 100, 200));
   ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100));
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index e48edf7..f8965b2 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -31,10 +31,12 @@
 #include <android-base/file.h>
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class MemoryRemoteTest : public ::testing::Test {
  protected:
   static uint64_t NanoTime() {
@@ -91,6 +93,7 @@
   ASSERT_TRUE(Detach(pid));
 
   kill(pid, SIGKILL);
+  ASSERT_EQ(pid, wait(nullptr));
 }
 
 TEST_F(MemoryRemoteTest, read_fail) {
@@ -131,6 +134,7 @@
   ASSERT_TRUE(Detach(pid));
 
   kill(pid, SIGKILL);
+  ASSERT_EQ(pid, wait(nullptr));
 }
 
 TEST_F(MemoryRemoteTest, read_overflow) {
@@ -160,4 +164,7 @@
   ASSERT_TRUE(Detach(pid));
 
   kill(pid, SIGKILL);
+  ASSERT_EQ(pid, wait(nullptr));
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
index 51b5d7d..4a9ed9f 100644
--- a/libunwindstack/tests/MemoryTest.cpp
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -22,10 +22,12 @@
 
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 TEST(MemoryTest, read32) {
   MemoryFakeAlwaysReadZero memory;
 
@@ -124,3 +126,5 @@
   ASSERT_TRUE(memory.ReadString(0, &dst_name));
   ASSERT_EQ("short", dst_name);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
new file mode 100644
index 0000000..e796c9b
--- /dev/null
+++ b/libunwindstack/tests/RegsFake.h
@@ -0,0 +1,41 @@
+/*
+ * 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 _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
+
+#include <stdint.h>
+
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class RegsFake : public RegsImpl<TypeParam> {
+ public:
+  RegsFake(uint16_t total_regs, uint16_t sp_reg)
+      : RegsImpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+  virtual ~RegsFake() = default;
+
+  uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+  void SetFromRaw() override {}
+  bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 0dac278..3613689 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -18,13 +18,15 @@
 
 #include <gtest/gtest.h>
 
-#include "Elf.h"
-#include "ElfInterface.h"
-#include "MapInfo.h"
-#include "Regs.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Regs.h>
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class ElfFake : public Elf {
  public:
   ElfFake(Memory* memory) : Elf(memory) { valid_ = true; }
@@ -48,15 +50,16 @@
 };
 
 template <typename TypeParam>
-class RegsTestTmpl : public RegsTmpl<TypeParam> {
+class RegsTestImpl : public RegsImpl<TypeParam> {
  public:
-  RegsTestTmpl(uint16_t total_regs, uint16_t regs_sp)
-      : RegsTmpl<TypeParam>(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
-  RegsTestTmpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc)
-      : RegsTmpl<TypeParam>(total_regs, regs_sp, return_loc) {}
-  virtual ~RegsTestTmpl() = default;
+  RegsTestImpl(uint16_t total_regs, uint16_t regs_sp)
+      : RegsImpl<TypeParam>(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+  RegsTestImpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc)
+      : RegsImpl<TypeParam>(total_regs, regs_sp, return_loc) {}
+  virtual ~RegsTestImpl() = default;
 
-  uint64_t GetAdjustedPc(uint64_t, Elf*) { return 0; }
+  uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+  void SetFromRaw() override {}
 };
 
 class RegsTest : public ::testing::Test {
@@ -69,9 +72,6 @@
   }
 
   template <typename AddressType>
-  void regs_rel_pc();
-
-  template <typename AddressType>
   void regs_return_address_register();
 
   ElfInterfaceFake* elf_interface_;
@@ -80,7 +80,7 @@
 };
 
 TEST_F(RegsTest, regs32) {
-  RegsTestTmpl<uint32_t> regs32(50, 10);
+  RegsTestImpl<uint32_t> regs32(50, 10);
   ASSERT_EQ(50U, regs32.total_regs());
   ASSERT_EQ(10U, regs32.sp_reg());
 
@@ -103,7 +103,7 @@
 }
 
 TEST_F(RegsTest, regs64) {
-  RegsTestTmpl<uint64_t> regs64(30, 12);
+  RegsTestImpl<uint64_t> regs64(30, 12);
   ASSERT_EQ(30U, regs64.total_regs());
   ASSERT_EQ(12U, regs64.sp_reg());
 
@@ -126,28 +126,8 @@
 }
 
 template <typename AddressType>
-void RegsTest::regs_rel_pc() {
-  RegsTestTmpl<AddressType> regs(30, 12);
-
-  elf_interface_->set_load_bias(0);
-  MapInfo map_info{.start = 0x1000, .end = 0x2000};
-  regs.set_pc(0x1101);
-  ASSERT_EQ(0x101U, regs.GetRelPc(elf_.get(), &map_info));
-  elf_interface_->set_load_bias(0x3000);
-  ASSERT_EQ(0x3101U, regs.GetRelPc(elf_.get(), &map_info));
-}
-
-TEST_F(RegsTest, regs32_rel_pc) {
-  regs_rel_pc<uint32_t>();
-}
-
-TEST_F(RegsTest, regs64_rel_pc) {
-  regs_rel_pc<uint64_t>();
-}
-
-template <typename AddressType>
 void RegsTest::regs_return_address_register() {
-  RegsTestTmpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
+  RegsTestImpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
 
   regs[5] = 0x12345;
   uint64_t value;
@@ -164,7 +144,7 @@
 }
 
 TEST_F(RegsTest, regs32_return_address_sp_offset) {
-  RegsTestTmpl<uint32_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2));
+  RegsTestImpl<uint32_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2));
 
   regs.set_sp(0x2002);
   memory_->SetData32(0x2000, 0x12345678);
@@ -174,7 +154,7 @@
 }
 
 TEST_F(RegsTest, regs64_return_address_sp_offset) {
-  RegsTestTmpl<uint64_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8));
+  RegsTestImpl<uint64_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8));
 
   regs.set_sp(0x2008);
   memory_->SetData64(0x2000, 0x12345678aabbccddULL);
@@ -249,18 +229,60 @@
   MapInfo map_info{.start = 0x1000, .end = 0x2000};
 
   regs_arm.set_pc(0x1500);
-  ASSERT_EQ(0x500U, regs_arm.GetRelPc(&invalid_elf, &map_info));
+  ASSERT_EQ(0x500U, invalid_elf.GetRelPc(regs_arm.pc(), &map_info));
   ASSERT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, &invalid_elf));
 
   regs_arm64.set_pc(0x1600);
-  ASSERT_EQ(0x600U, regs_arm64.GetRelPc(&invalid_elf, &map_info));
+  ASSERT_EQ(0x600U, invalid_elf.GetRelPc(regs_arm64.pc(), &map_info));
   ASSERT_EQ(0x600U, regs_arm64.GetAdjustedPc(0x600U, &invalid_elf));
 
   regs_x86.set_pc(0x1700);
-  ASSERT_EQ(0x700U, regs_x86.GetRelPc(&invalid_elf, &map_info));
+  ASSERT_EQ(0x700U, invalid_elf.GetRelPc(regs_x86.pc(), &map_info));
   ASSERT_EQ(0x700U, regs_x86.GetAdjustedPc(0x700U, &invalid_elf));
 
   regs_x86_64.set_pc(0x1800);
-  ASSERT_EQ(0x800U, regs_x86_64.GetRelPc(&invalid_elf, &map_info));
+  ASSERT_EQ(0x800U, invalid_elf.GetRelPc(regs_x86_64.pc(), &map_info));
   ASSERT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, &invalid_elf));
 }
+
+TEST_F(RegsTest, arm_set_from_raw) {
+  RegsArm arm;
+  uint32_t* regs = reinterpret_cast<uint32_t*>(arm.RawData());
+  regs[13] = 0x100;
+  regs[15] = 0x200;
+  arm.SetFromRaw();
+  EXPECT_EQ(0x100U, arm.sp());
+  EXPECT_EQ(0x200U, arm.pc());
+}
+
+TEST_F(RegsTest, arm64_set_from_raw) {
+  RegsArm64 arm64;
+  uint64_t* regs = reinterpret_cast<uint64_t*>(arm64.RawData());
+  regs[31] = 0xb100000000ULL;
+  regs[32] = 0xc200000000ULL;
+  arm64.SetFromRaw();
+  EXPECT_EQ(0xb100000000U, arm64.sp());
+  EXPECT_EQ(0xc200000000U, arm64.pc());
+}
+
+TEST_F(RegsTest, x86_set_from_raw) {
+  RegsX86 x86;
+  uint32_t* regs = reinterpret_cast<uint32_t*>(x86.RawData());
+  regs[4] = 0x23450000;
+  regs[8] = 0xabcd0000;
+  x86.SetFromRaw();
+  EXPECT_EQ(0x23450000U, x86.sp());
+  EXPECT_EQ(0xabcd0000U, x86.pc());
+}
+
+TEST_F(RegsTest, x86_64_set_from_raw) {
+  RegsX86_64 x86_64;
+  uint64_t* regs = reinterpret_cast<uint64_t*>(x86_64.RawData());
+  regs[7] = 0x1200000000ULL;
+  regs[16] = 0x4900000000ULL;
+  x86_64.SetFromRaw();
+  EXPECT_EQ(0x1200000000U, x86_64.sp());
+  EXPECT_EQ(0x4900000000U, x86_64.pc());
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index a0a21e6..da258a6 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -32,10 +32,13 @@
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
 #include "MemoryFake.h"
 #include "Symbols.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
 class SymbolsTest : public ::testing::Test {
  protected:
@@ -333,3 +336,5 @@
 
 typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
new file mode 100644
index 0000000..72065c9
--- /dev/null
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2017 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 <signal.h>
+#include <stdint.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <atomic>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+namespace unwindstack {
+
+static std::atomic_bool g_ready(false);
+static volatile bool g_ready_for_remote = false;
+static std::atomic_bool g_finish(false);
+static std::atomic_uintptr_t g_ucontext;
+
+static void Signal(int, siginfo_t*, void* sigcontext) {
+  g_ucontext = reinterpret_cast<uintptr_t>(sigcontext);
+  while (!g_finish.load()) {
+  }
+}
+
+static std::string ErrorMsg(const char** function_names, size_t index,
+                            std::stringstream& unwind_stream) {
+  return std::string(
+             "Unwind completed without finding all frames\n"
+             "  Looking for function: ") +
+         function_names[index] + "\n" + "Unwind data:\n" + unwind_stream.str();
+}
+
+static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs) {
+  const char* function_names[] = {
+      "InnerFunction", "MiddleFunction", "OuterFunction",
+  };
+  size_t function_name_index = 0;
+
+  std::stringstream unwind_stream;
+  unwind_stream << std::hex;
+  for (size_t frame_num = 0; frame_num < 64; frame_num++) {
+    ASSERT_NE(0U, regs->pc()) << ErrorMsg(function_names, function_name_index, unwind_stream);
+    MapInfo* map_info = maps->Find(regs->pc());
+    ASSERT_TRUE(map_info != nullptr) << ErrorMsg(function_names, function_name_index, unwind_stream);
+
+    Elf* elf = map_info->GetElf(pid, true);
+    uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
+    uint64_t adjusted_rel_pc = rel_pc;
+    if (frame_num != 0) {
+      adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
+    }
+    unwind_stream << "  PC: 0x" << regs->pc() << " Rel: 0x" << adjusted_rel_pc;
+    unwind_stream << " Map: ";
+    if (!map_info->name.empty()) {
+      unwind_stream << map_info->name;
+    } else {
+      unwind_stream << " anonymous";
+    }
+    unwind_stream << "<" << map_info->start << "-" << map_info->end << ">";
+
+    std::string name;
+    uint64_t func_offset;
+    if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
+      if (name == function_names[function_name_index]) {
+        function_name_index++;
+        if (function_name_index == sizeof(function_names) / sizeof(const char*)) {
+          return;
+        }
+      }
+      unwind_stream << " " << name;
+    }
+    unwind_stream << "\n";
+    ASSERT_TRUE(elf->Step(rel_pc + map_info->elf_offset, regs, memory))
+        << ErrorMsg(function_names, function_name_index, unwind_stream);
+  }
+  ASSERT_TRUE(false) << ErrorMsg(function_names, function_name_index, unwind_stream);
+}
+
+// This test assumes that this code is compiled with optimizations turned
+// off. If this doesn't happen, then all of the calls will be optimized
+// away.
+extern "C" void InnerFunction(bool local) {
+  if (local) {
+    LocalMaps maps;
+    ASSERT_TRUE(maps.Parse());
+    std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+    RegsGetLocal(regs.get());
+    MemoryLocal memory;
+
+    VerifyUnwind(getpid(), &memory, &maps, regs.get());
+  } else {
+    g_ready_for_remote = true;
+    g_ready = true;
+    while (!g_finish.load()) {
+    }
+  }
+}
+
+extern "C" void MiddleFunction(bool local) {
+  InnerFunction(local);
+}
+
+extern "C" void OuterFunction(bool local) {
+  MiddleFunction(local);
+}
+
+TEST(UnwindTest, local) {
+  OuterFunction(true);
+}
+
+TEST(UnwindTest, remote) {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    OuterFunction(false);
+    exit(0);
+  }
+  ASSERT_NE(-1, pid);
+
+  bool ready = false;
+  uint64_t addr = reinterpret_cast<uint64_t>(&g_ready_for_remote);
+  for (size_t i = 0; i < 100; i++) {
+    ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, 0, 0));
+    for (size_t j = 0; j < 100; j++) {
+      siginfo_t si;
+      if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+        // Check to see if process is ready to be unwound.
+        MemoryRemote memory(pid);
+        // Read the remote value to see if we are ready.
+        bool value;
+        if (memory.Read(addr, &value, sizeof(value)) && value) {
+          ready = true;
+          break;
+        }
+      }
+      usleep(1000);
+    }
+    if (ready) {
+      break;
+    }
+    ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
+    usleep(1000);
+  }
+  ASSERT_TRUE(read) << "Timed out waiting for remote process to be ready.";
+
+  RemoteMaps maps(pid);
+  ASSERT_TRUE(maps.Parse());
+  MemoryRemote memory(pid);
+  uint32_t machine_type;
+  std::unique_ptr<Regs> regs(Regs::RemoteGet(pid, &machine_type));
+  ASSERT_TRUE(regs.get() != nullptr);
+
+  VerifyUnwind(pid, &memory, &maps, regs.get());
+
+  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
+
+  kill(pid, SIGKILL);
+  ASSERT_EQ(pid, wait(nullptr));
+}
+
+TEST(UnwindTest, from_context) {
+  std::atomic_int tid(0);
+  std::thread thread([&]() {
+    tid = syscall(__NR_gettid);
+    OuterFunction(false);
+  });
+
+  struct sigaction act, oldact;
+  memset(&act, 0, sizeof(act));
+  act.sa_sigaction = Signal;
+  act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+  ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+  // Wait for the tid to get set.
+  for (size_t i = 0; i < 100; i++) {
+    if (tid.load() != 0) {
+      break;
+    }
+    usleep(1000);
+  }
+  ASSERT_NE(0, tid.load());
+  // Portable tgkill method.
+  ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Failed because "
+                                                                    << strerror(errno);
+
+  // Wait for context data.
+  void* ucontext;
+  for (size_t i = 0; i < 2000; i++) {
+    ucontext = reinterpret_cast<void*>(g_ucontext.load());
+    if (ucontext != nullptr) {
+      break;
+    }
+    usleep(1000);
+  }
+  ASSERT_TRUE(ucontext != nullptr) << "Timed out waiting for thread to respond to signal.";
+
+  LocalMaps maps;
+  ASSERT_TRUE(maps.Parse());
+  std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::GetMachineType(), ucontext));
+  MemoryLocal memory;
+
+  VerifyUnwind(tid.load(), &memory, &maps, regs.get());
+
+  ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
+
+  g_finish = true;
+  thread.join();
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/files/elf32.xz b/libunwindstack/tests/files/elf32.xz
new file mode 100644
index 0000000..f25d433
--- /dev/null
+++ b/libunwindstack/tests/files/elf32.xz
Binary files differ
diff --git a/libunwindstack/tests/files/elf64.xz b/libunwindstack/tests/files/elf64.xz
new file mode 100644
index 0000000..eb1618e
--- /dev/null
+++ b/libunwindstack/tests/files/elf64.xz
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
new file mode 100644
index 0000000..642105a
--- /dev/null
+++ b/libunwindstack/tools/unwind.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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 <elf.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+static bool Attach(pid_t pid) {
+  if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+    return false;
+  }
+
+  // Allow at least 1 second to attach properly.
+  for (size_t i = 0; i < 1000; i++) {
+    siginfo_t si;
+    if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+      return true;
+    }
+    usleep(1000);
+  }
+  printf("%d: Failed to stop.\n", pid);
+  return false;
+}
+
+static bool Detach(pid_t pid) {
+  return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
+}
+
+void DoUnwind(pid_t pid) {
+  unwindstack::RemoteMaps remote_maps(pid);
+  if (!remote_maps.Parse()) {
+    printf("Failed to parse map data.\n");
+    return;
+  }
+
+  uint32_t machine_type;
+  unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid, &machine_type);
+  if (regs == nullptr) {
+    printf("Unable to get remote reg data\n");
+    return;
+  }
+
+  bool bits32 = true;
+  printf("ABI: ");
+  switch (machine_type) {
+    case EM_ARM:
+      printf("arm");
+      break;
+    case EM_386:
+      printf("x86");
+      break;
+    case EM_AARCH64:
+      printf("arm64");
+      bits32 = false;
+      break;
+    case EM_X86_64:
+      printf("x86_64");
+      bits32 = false;
+      break;
+    default:
+      printf("unknown\n");
+      return;
+  }
+  printf("\n");
+
+  unwindstack::MemoryRemote remote_memory(pid);
+  for (size_t frame_num = 0; frame_num < 64; frame_num++) {
+    if (regs->pc() == 0) {
+      break;
+    }
+    unwindstack::MapInfo* map_info = remote_maps.Find(regs->pc());
+    if (map_info == nullptr) {
+      printf("Failed to find map data for the pc\n");
+      break;
+    }
+
+    unwindstack::Elf* elf = map_info->GetElf(pid, true);
+
+    uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
+    uint64_t adjusted_rel_pc = rel_pc;
+    // Don't need to adjust the first frame pc.
+    if (frame_num != 0) {
+      adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
+    }
+
+    std::string name;
+    if (bits32) {
+      printf("  #%02zu pc %08" PRIx64, frame_num, adjusted_rel_pc);
+    } else {
+      printf("  #%02zu pc %016" PRIx64, frame_num, adjusted_rel_pc);
+    }
+    if (!map_info->name.empty()) {
+      printf("  %s", map_info->name.c_str());
+      if (map_info->elf_offset != 0) {
+        printf(" (offset 0x%" PRIx64 ")", map_info->elf_offset);
+      }
+    } else {
+      printf("  <anonymous:%" PRIx64 ">", map_info->offset);
+    }
+    uint64_t func_offset;
+    if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
+      printf(" (%s", name.c_str());
+      if (func_offset != 0) {
+        printf("+%" PRId64, func_offset);
+      }
+      printf(")");
+    }
+    printf("\n");
+
+    if (!elf->Step(rel_pc + map_info->elf_offset, regs, &remote_memory)) {
+      break;
+    }
+  }
+}
+
+int main(int argc, char** argv) {
+  if (argc != 2) {
+    printf("Usage: unwind <PID>\n");
+    return 1;
+  }
+
+  pid_t pid = atoi(argv[1]);
+  if (!Attach(pid)) {
+    printf("Failed to attach to pid %d: %s\n", pid, strerror(errno));
+    return 1;
+  }
+
+  DoUnwind(pid);
+
+  Detach(pid);
+
+  return 0;
+}
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
new file mode 100644
index 0000000..66a9439
--- /dev/null
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -0,0 +1,184 @@
+/*
+ * 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 <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+
+#include "ArmExidx.h"
+#include "ElfInterfaceArm.h"
+
+namespace unwindstack {
+
+void DumpArm(ElfInterfaceArm* interface) {
+  if (interface == nullptr) {
+    printf("No ARM Unwind Information.\n\n");
+    return;
+  }
+
+  printf("ARM Unwind Information:\n");
+  for (const auto& entry : interface->pt_loads()) {
+    uint64_t load_bias = entry.second.table_offset;
+    printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
+           entry.second.table_size + load_bias);
+    for (auto addr : *interface) {
+      std::string name;
+      printf("  PC 0x%" PRIx64, addr + load_bias);
+      uint64_t func_offset;
+      uint64_t pc = addr + load_bias;
+      // This might be a thumb function, so set the low bit.
+      if (interface->GetFunctionName(pc | 1, &name, &func_offset) && !name.empty()) {
+        printf(" <%s>", name.c_str());
+      }
+      printf("\n");
+      uint64_t entry;
+      if (!interface->FindEntry(pc, &entry)) {
+        printf("    Cannot find entry for address.\n");
+        continue;
+      }
+      ArmExidx arm(nullptr, interface->memory(), nullptr);
+      arm.set_log(true);
+      arm.set_log_skip_execution(true);
+      arm.set_log_indent(2);
+      if (!arm.ExtractEntryData(entry)) {
+        if (arm.status() != ARM_STATUS_NO_UNWIND) {
+          printf("    Error trying to extract data.\n");
+        }
+        continue;
+      }
+      if (arm.data()->size() > 0) {
+        if (!arm.Eval() && arm.status() != ARM_STATUS_NO_UNWIND) {
+          printf("      Error trying to evaluate dwarf data.\n");
+        }
+      }
+    }
+  }
+  printf("\n");
+}
+
+void DumpDwarfSection(ElfInterface* interface, DwarfSection* section, uint64_t load_bias) {
+  for (const DwarfFde* fde : *section) {
+    // Sometimes there are entries that have empty length, skip those since
+    // they don't contain any interesting information.
+    if (fde->pc_start == fde->pc_end) {
+      continue;
+    }
+    printf("\n  PC 0x%" PRIx64, fde->pc_start + load_bias);
+    std::string name;
+    uint64_t func_offset;
+    if (interface->GetFunctionName(fde->pc_start + load_bias, &name, &func_offset) &&
+        !name.empty()) {
+      printf(" <%s>", name.c_str());
+    }
+    printf("\n");
+    if (!section->Log(2, UINT64_MAX, load_bias, fde)) {
+      printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
+    }
+  }
+}
+
+int GetElfInfo(const char* file) {
+  // Send all log messages to stdout.
+  log_to_stdout(true);
+
+  MemoryFileAtOffset* memory = new MemoryFileAtOffset;
+  if (!memory->Init(file, 0)) {
+    // Initializatation failed.
+    printf("Failed to init\n");
+    return 1;
+  }
+
+  Elf elf(memory);
+  if (!elf.Init() || !elf.valid()) {
+    printf("%s is not a valid elf file.\n", file);
+    return 1;
+  }
+
+  ElfInterface* interface = elf.interface();
+  if (elf.machine_type() == EM_ARM) {
+    DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
+    printf("\n");
+  }
+
+  if (interface->eh_frame() != nullptr) {
+    printf("eh_frame information:\n");
+    DumpDwarfSection(interface, interface->eh_frame(), interface->load_bias());
+    printf("\n");
+  } else {
+    printf("\nno eh_frame information\n");
+  }
+
+  if (interface->debug_frame() != nullptr) {
+    printf("\ndebug_frame information:\n");
+    DumpDwarfSection(interface, interface->debug_frame(), interface->load_bias());
+    printf("\n");
+  } else {
+    printf("\nno debug_frame information\n");
+  }
+
+  // If there is a gnu_debugdata interface, dump the information for that.
+  ElfInterface* gnu_debugdata_interface = elf.gnu_debugdata_interface();
+  if (gnu_debugdata_interface != nullptr) {
+    if (gnu_debugdata_interface->eh_frame() != nullptr) {
+      printf("\ngnu_debugdata (eh_frame):\n");
+      DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->eh_frame(), 0);
+      printf("\n");
+    }
+    if (gnu_debugdata_interface->debug_frame() != nullptr) {
+      printf("\ngnu_debugdata (debug_frame):\n");
+      DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->debug_frame(), 0);
+      printf("\n");
+    }
+  } else {
+    printf("\nno valid gnu_debugdata information\n");
+  }
+
+  return 0;
+}
+
+}  // namespace unwindstack
+
+int main(int argc, char** argv) {
+  if (argc != 2) {
+    printf("Need to pass the name of an elf file to the program.\n");
+    return 1;
+  }
+
+  struct stat st;
+  if (stat(argv[1], &st) == -1) {
+    printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+    return 1;
+  }
+  if (!S_ISREG(st.st_mode)) {
+    printf("%s is not a regular file.\n", argv[1]);
+    return 1;
+  }
+
+  return unwindstack::GetElfInfo(argv[1]);
+}
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
new file mode 100644
index 0000000..b757c1e
--- /dev/null
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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 <elf.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+
+int main(int argc, char** argv) {
+  if (argc != 2) {
+    printf("Need to pass the name of an elf file to the program.\n");
+    return 1;
+  }
+
+  struct stat st;
+  if (stat(argv[1], &st) == -1) {
+    printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+    return 1;
+  }
+  if (!S_ISREG(st.st_mode)) {
+    printf("%s is not a regular file.\n", argv[1]);
+    return 1;
+  }
+
+  // Send all log messages to stdout.
+  unwindstack::log_to_stdout(true);
+
+  unwindstack::MemoryFileAtOffset* memory = new unwindstack::MemoryFileAtOffset;
+  if (!memory->Init(argv[1], 0)) {
+    printf("Failed to init\n");
+    return 1;
+  }
+
+  unwindstack::Elf elf(memory);
+  if (!elf.Init() || !elf.valid()) {
+    printf("%s is not a valid elf file.\n", argv[1]);
+    return 1;
+  }
+
+  switch (elf.machine_type()) {
+    case EM_ARM:
+      printf("ABI: arm\n");
+      break;
+    case EM_AARCH64:
+      printf("ABI: arm64\n");
+      break;
+    case EM_386:
+      printf("ABI: x86\n");
+      break;
+    case EM_X86_64:
+      printf("ABI: x86_64\n");
+      break;
+    default:
+      printf("ABI: unknown\n");
+      return 1;
+  }
+
+  // This is a crude way to get the symbols in order.
+  std::string name;
+  uint64_t load_bias = elf.interface()->load_bias();
+  for (const auto& entry : elf.interface()->pt_loads()) {
+    uint64_t start = entry.second.offset + load_bias;
+    uint64_t end = entry.second.table_size + load_bias;
+    for (uint64_t addr = start; addr < end; addr += 4) {
+      std::string cur_name;
+      uint64_t func_offset;
+      if (elf.GetFunctionName(addr, &cur_name, &func_offset)) {
+        if (cur_name != name) {
+          printf("<0x%" PRIx64 "> Function: %s\n", addr - func_offset, cur_name.c_str());
+        }
+        name = cur_name;
+      }
+    }
+  }
+
+  return 0;
+}
diff --git a/libunwindstack/unwind_info.cpp b/libunwindstack/unwind_info.cpp
deleted file mode 100644
index 6f158b0..0000000
--- a/libunwindstack/unwind_info.cpp
+++ /dev/null
@@ -1,121 +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 <elf.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "ArmExidx.h"
-#include "Elf.h"
-#include "ElfInterface.h"
-#include "ElfInterfaceArm.h"
-#include "Log.h"
-
-void DumpArm(ElfInterfaceArm* interface) {
-  if (interface == nullptr) {
-    printf("No ARM Unwind Information.\n\n");
-    return;
-  }
-
-  printf("ARM Unwind Information:\n");
-  for (const auto& entry : interface->pt_loads()) {
-    uint64_t load_bias = entry.second.table_offset;
-    printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
-           entry.second.table_size + load_bias);
-    for (auto addr : *interface) {
-      std::string name;
-      printf("  PC 0x%" PRIx64, addr + load_bias);
-      uint64_t func_offset;
-      if (interface->GetFunctionName(addr + load_bias + 1, &name, &func_offset) && !name.empty()) {
-        printf(" <%s>", name.c_str());
-      }
-      printf("\n");
-      uint64_t entry;
-      if (!interface->FindEntry(addr + load_bias, &entry)) {
-        printf("    Cannot find entry for address.\n");
-        continue;
-      }
-      ArmExidx arm(nullptr, interface->memory(), nullptr);
-      arm.set_log(true);
-      arm.set_log_skip_execution(true);
-      arm.set_log_indent(2);
-      if (!arm.ExtractEntryData(entry)) {
-        if (arm.status() != ARM_STATUS_NO_UNWIND) {
-          printf("    Error trying to extract data.\n");
-        }
-        continue;
-      }
-      if (arm.data()->size() > 0) {
-        if (!arm.Eval() && arm.status() != ARM_STATUS_NO_UNWIND) {
-          printf("      Error trying to evaluate dwarf data.\n");
-        }
-      }
-    }
-  }
-  printf("\n");
-}
-
-int main(int argc, char** argv) {
-  if (argc != 2) {
-    printf("Need to pass the name of an elf file to the program.\n");
-    return 1;
-  }
-
-  struct stat st;
-  if (stat(argv[1], &st) == -1) {
-    printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
-    return 1;
-  }
-  if (!S_ISREG(st.st_mode)) {
-    printf("%s is not a regular file.\n", argv[1]);
-    return 1;
-  }
-  if (S_ISDIR(st.st_mode)) {
-    printf("%s is a directory.\n", argv[1]);
-    return 1;
-  }
-
-  // Send all log messages to stdout.
-  log_to_stdout(true);
-
-  MemoryFileAtOffset* memory = new MemoryFileAtOffset;
-  if (!memory->Init(argv[1], 0)) {
-    // Initializatation failed.
-    printf("Failed to init\n");
-    return 1;
-  }
-
-  Elf elf(memory);
-  if (!elf.Init() || !elf.valid()) {
-    printf("%s is not a valid elf file.\n", argv[1]);
-    return 1;
-  }
-
-  ElfInterface* interface = elf.interface();
-  if (elf.machine_type() == EM_ARM) {
-    DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
-    printf("\n");
-  }
-
-  return 0;
-}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 33770ba..0125eac 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -18,10 +18,12 @@
     host_supported: true,
 
     header_libs: [
+        "liblog_headers",
         "libsystem_headers",
         "libcutils_headers"
     ],
     export_header_lib_headers: [
+        "liblog_headers",
         "libsystem_headers",
         "libcutils_headers"
     ],
@@ -76,6 +78,10 @@
     header_libs: ["libutils_headers"],
     export_header_lib_headers: ["libutils_headers"],
 
+    shared_libs: [
+        "liblog",
+    ],
+
     arch: {
         mips: {
             cflags: ["-DALIGN_DOUBLE"],
@@ -97,7 +103,6 @@
                 "libbacktrace",
                 "libcutils",
                 "libdl",
-                "liblog",
             ],
 
             sanitize: {
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index 98cd2c6..84af293 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -47,9 +47,11 @@
 #ifndef _WIN32
     if (vasprintf(&formattedString, format, arglist) < 0) { // returns -1 on error
         ALOGE("%s: Failed to format string", __FUNCTION__);
+        va_end(arglist);
         return;
     }
 #else
+    va_end(arglist);
     return;
 #endif
 
diff --git a/libziparchive/.clang-format b/libziparchive/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libziparchive/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 44daf36..333835c 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -50,17 +50,29 @@
         "libbase",
         "liblog",
     ],
-}
 
+    export_include_dirs: ["include"],
+}
 
 cc_library {
     name: "libziparchive",
     host_supported: true,
-    defaults: ["libziparchive_defaults", "libziparchive_flags"],
-    shared_libs: ["liblog", "libbase"],
+    vendor_available: true,
+
+    defaults: [
+        "libziparchive_defaults",
+        "libziparchive_flags",
+    ],
+    shared_libs: [
+        "liblog",
+        "libbase",
+    ],
     target: {
         android: {
-            shared_libs: ["libz", "libutils"],
+            shared_libs: [
+                "libz",
+                "libutils",
+            ],
         },
         host: {
             static_libs: ["libutils"],
@@ -86,7 +98,10 @@
     name: "libziparchive-host",
     host_supported: true,
     device_supported: false,
-    defaults: ["libziparchive_defaults", "libziparchive_flags"],
+    defaults: [
+        "libziparchive_defaults",
+        "libziparchive_flags",
+    ],
     shared_libs: ["libz-host"],
     static_libs: ["libutils"],
 }
@@ -122,3 +137,39 @@
         },
     },
 }
+
+// Performance benchmarks.
+cc_benchmark {
+    name: "ziparchive-benchmarks",
+    defaults: ["libziparchive_flags"],
+
+    srcs: [
+        "zip_archive_benchmark.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+
+    static_libs: [
+        "libziparchive",
+        "libz",
+        "libutils",
+    ],
+
+    target: {
+        host: {
+            cppflags: ["-Wno-unnamed-type-template-args"],
+        },
+    },
+}
+
+cc_binary {
+    name: "unzip",
+    defaults: ["libziparchive_flags"],
+    srcs: ["unzip.cpp"],
+    shared_libs: [
+        "libbase",
+        "libziparchive",
+    ],
+}
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h
index ddbc286..5fc2fb4 100644
--- a/libziparchive/entry_name_utils-inl.h
+++ b/libziparchive/entry_name_utils-inl.h
@@ -55,5 +55,4 @@
   return true;
 }
 
-
 #endif  // LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
diff --git a/libziparchive/entry_name_utils_test.cc b/libziparchive/entry_name_utils_test.cc
index 20715bb..d83d854 100644
--- a/libziparchive/entry_name_utils_test.cc
+++ b/libziparchive/entry_name_utils_test.cc
@@ -20,44 +20,43 @@
 
 TEST(entry_name_utils, NullChars) {
   // 'A', 'R', '\0', 'S', 'E'
-  const uint8_t zeroes[] = { 0x41, 0x52, 0x00, 0x53, 0x45 };
+  const uint8_t zeroes[] = {0x41, 0x52, 0x00, 0x53, 0x45};
   ASSERT_FALSE(IsValidEntryName(zeroes, sizeof(zeroes)));
 
-  const uint8_t zeroes_continuation_chars[] = { 0xc2, 0xa1, 0xc2, 0x00 };
-  ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars,
-                                sizeof(zeroes_continuation_chars)));
+  const uint8_t zeroes_continuation_chars[] = {0xc2, 0xa1, 0xc2, 0x00};
+  ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars, sizeof(zeroes_continuation_chars)));
 }
 
 TEST(entry_name_utils, InvalidSequence) {
   // 0xfe is an invalid start byte
-  const uint8_t invalid[] = { 0x41, 0xfe };
+  const uint8_t invalid[] = {0x41, 0xfe};
   ASSERT_FALSE(IsValidEntryName(invalid, sizeof(invalid)));
 
   // 0x91 is an invalid start byte (it's a valid continuation byte).
-  const uint8_t invalid2[] = { 0x41, 0x91 };
+  const uint8_t invalid2[] = {0x41, 0x91};
   ASSERT_FALSE(IsValidEntryName(invalid2, sizeof(invalid2)));
 }
 
 TEST(entry_name_utils, TruncatedContinuation) {
   // Malayalam script with truncated bytes. There should be 2 bytes
   // after 0xe0
-  const uint8_t truncated[] = { 0xe0, 0xb4, 0x85, 0xe0, 0xb4 };
+  const uint8_t truncated[] = {0xe0, 0xb4, 0x85, 0xe0, 0xb4};
   ASSERT_FALSE(IsValidEntryName(truncated, sizeof(truncated)));
 
   // 0xc2 is the start of a 2 byte sequence that we've subsequently
   // dropped.
-  const uint8_t truncated2[] = { 0xc2, 0xc2, 0xa1 };
+  const uint8_t truncated2[] = {0xc2, 0xc2, 0xa1};
   ASSERT_FALSE(IsValidEntryName(truncated2, sizeof(truncated2)));
 }
 
 TEST(entry_name_utils, BadContinuation) {
   // 0x41 is an invalid continuation char, since it's MSBs
   // aren't "10..." (are 01).
-  const uint8_t bad[] = { 0xc2, 0xa1, 0xc2, 0x41 };
+  const uint8_t bad[] = {0xc2, 0xa1, 0xc2, 0x41};
   ASSERT_FALSE(IsValidEntryName(bad, sizeof(bad)));
 
   // 0x41 is an invalid continuation char, since it's MSBs
   // aren't "10..." (are 11).
-  const uint8_t bad2[] = { 0xc2, 0xa1, 0xc2, 0xfe };
+  const uint8_t bad2[] = {0xc2, 0xa1, 0xc2, 0xfe};
   ASSERT_FALSE(IsValidEntryName(bad2, sizeof(bad2)));
 }
diff --git a/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
similarity index 86%
rename from include/ziparchive/zip_archive.h
rename to libziparchive/include/ziparchive/zip_archive.h
index 31fc2df..73ae68d 100644
--- a/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -28,8 +28,8 @@
 
 /* Zip compression methods we support */
 enum {
-  kCompressStored     = 0,        // no compression
-  kCompressDeflated   = 8,        // standard deflate
+  kCompressStored = 0,    // no compression
+  kCompressDeflated = 8,  // standard deflate
 };
 
 struct ZipString {
@@ -44,19 +44,17 @@
   explicit ZipString(const char* entry_name);
 
   bool operator==(const ZipString& rhs) const {
-    return name && (name_length == rhs.name_length) &&
-        (memcmp(name, rhs.name, name_length) == 0);
+    return name && (name_length == rhs.name_length) && (memcmp(name, rhs.name, name_length) == 0);
   }
 
   bool StartsWith(const ZipString& prefix) const {
     return name && (name_length >= prefix.name_length) &&
-        (memcmp(name, prefix.name, prefix.name_length) == 0);
+           (memcmp(name, prefix.name, prefix.name_length) == 0);
   }
 
   bool EndsWith(const ZipString& suffix) const {
     return name && (name_length >= suffix.name_length) &&
-        (memcmp(name + name_length - suffix.name_length, suffix.name,
-                suffix.name_length) == 0);
+           (memcmp(name + name_length - suffix.name_length, suffix.name, suffix.name_length) == 0);
   }
 };
 
@@ -71,8 +69,17 @@
   // Modification time. The zipfile format specifies
   // that the first two little endian bytes contain the time
   // and the last two little endian bytes contain the date.
+  // See `GetModificationTime`.
+  // TODO: should be overridden by extra time field, if present.
   uint32_t mod_time;
 
+  // Returns `mod_time` as a broken-down struct tm.
+  struct tm GetModificationTime() const;
+
+  // Suggested Unix mode for this entry, from the zip archive if created on
+  // Unix, or a default otherwise.
+  mode_t unix_mode;
+
   // 1 if this entry contains a data descriptor segment, 0
   // otherwise.
   uint8_t has_data_descriptor;
@@ -125,11 +132,11 @@
  *
  * Returns 0 on success, and negative values on failure.
  */
-int32_t OpenArchiveFd(const int fd, const char* debugFileName,
-                      ZipArchiveHandle *handle, bool assume_ownership = true);
+int32_t OpenArchiveFd(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
+                      bool assume_ownership = true);
 
 int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debugFileName,
-                              ZipArchiveHandle *handle);
+                              ZipArchiveHandle* handle);
 /*
  * Close archive, releasing resources associated with it. This will
  * unmap the central directory of the zipfile and free all internal
@@ -155,8 +162,7 @@
  * On non-Windows platforms this method does not modify internal state and
  * can be called concurrently.
  */
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
-                  ZipEntry* data);
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data);
 
 /*
  * Start iterating over all entries of a zip file. The order of iteration
@@ -171,8 +177,7 @@
  *
  * Returns 0 on success and negative values on failure.
  */
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
-                       const ZipString* optional_prefix,
+int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
                        const ZipString* optional_suffix);
 
 /*
@@ -208,8 +213,7 @@
  *
  * Returns 0 on success and negative values on failure.
  */
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
-                        uint8_t* begin, uint32_t size);
+int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size);
 
 int GetFileDescriptor(const ZipArchiveHandle handle);
 
@@ -221,9 +225,9 @@
 /*
  * Stream the uncompressed data through the supplied function,
  * passing cookie to it each time it gets called.
-*/
+ */
 int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
-        ProcessZipEntryFunction func, void* cookie);
+                                ProcessZipEntryFunction func, void* cookie);
 #endif
 
 #endif  // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/include/ziparchive/zip_archive_stream_entry.h b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
similarity index 100%
rename from include/ziparchive/zip_archive_stream_entry.h
rename to libziparchive/include/ziparchive/zip_archive_stream_entry.h
diff --git a/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
similarity index 96%
rename from include/ziparchive/zip_writer.h
rename to libziparchive/include/ziparchive/zip_writer.h
index 08ead48..c350a27 100644
--- a/include/ziparchive/zip_writer.h
+++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -19,7 +19,6 @@
 
 #include <cstdio>
 #include <ctime>
-#include <zlib.h>
 
 #include <memory>
 #include <string>
@@ -28,6 +27,9 @@
 #include "android-base/macros.h"
 #include "utils/Compat.h"
 
+struct z_stream_s;
+typedef struct z_stream_s z_stream;
+
 /**
  * Writes a Zip file via a stateful interface.
  *
@@ -50,7 +52,7 @@
  *   fclose(file);
  */
 class ZipWriter {
-public:
+ public:
   enum {
     /**
      * Flag to compress the zip entry using deflate.
@@ -120,8 +122,7 @@
   /**
    * Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry.
    */
-  int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time,
-                                    uint32_t alignment);
+  int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time, uint32_t alignment);
 
   /**
    * Writes bytes to the zip file for the previously started zip entry.
@@ -156,7 +157,7 @@
    */
   int32_t Finish();
 
-private:
+ private:
   DISALLOW_COPY_AND_ASSIGN(ZipWriter);
 
   int32_t HandleError(int32_t error_code);
@@ -179,7 +180,7 @@
   std::vector<FileEntry> files_;
   FileEntry current_file_entry_;
 
-  std::unique_ptr<z_stream, void(*)(z_stream*)> z_stream_;
+  std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_;
   std::vector<uint8_t> buffer_;
 };
 
diff --git a/libziparchive/testdata/bad_filename.zip b/libziparchive/testdata/bad_filename.zip
new file mode 100644
index 0000000..294eaf5
--- /dev/null
+++ b/libziparchive/testdata/bad_filename.zip
Binary files differ
diff --git a/libziparchive/testdata/crash.apk b/libziparchive/testdata/crash.apk
new file mode 100644
index 0000000..d6dd52d
--- /dev/null
+++ b/libziparchive/testdata/crash.apk
Binary files differ
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
new file mode 100644
index 0000000..6756007
--- /dev/null
+++ b/libziparchive/unzip.cpp
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2017 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 <error.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <ziparchive/zip_archive.h>
+
+enum OverwriteMode {
+  kAlways,
+  kNever,
+  kPrompt,
+};
+
+static OverwriteMode overwrite_mode = kPrompt;
+static const char* flag_d = nullptr;
+static bool flag_l = false;
+static bool flag_p = false;
+static bool flag_q = false;
+static bool flag_v = false;
+static const char* archive_name = nullptr;
+static std::set<std::string> includes;
+static std::set<std::string> excludes;
+static uint64_t total_uncompressed_length = 0;
+static uint64_t total_compressed_length = 0;
+static size_t file_count = 0;
+
+static bool Filter(const std::string& name) {
+  if (!excludes.empty() && excludes.find(name) != excludes.end()) return true;
+  if (!includes.empty() && includes.find(name) == includes.end()) return true;
+  return false;
+}
+
+static bool MakeDirectoryHierarchy(const std::string& path) {
+  // stat rather than lstat because a symbolic link to a directory is fine too.
+  struct stat sb;
+  if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return true;
+
+  // Ensure the parent directories exist first.
+  if (!MakeDirectoryHierarchy(android::base::Dirname(path))) return false;
+
+  // Then try to create this directory.
+  return (mkdir(path.c_str(), 0777) != -1);
+}
+
+static int CompressionRatio(int64_t uncompressed, int64_t compressed) {
+  if (uncompressed == 0) return 0;
+  return (100LL * (uncompressed - compressed)) / uncompressed;
+}
+
+static void MaybeShowHeader() {
+  if (!flag_q) printf("Archive:  %s\n", archive_name);
+  if (flag_v) {
+    printf(
+        " Length   Method    Size  Cmpr    Date    Time   CRC-32   Name\n"
+        "--------  ------  ------- ---- ---------- ----- --------  ----\n");
+  } else if (flag_l) {
+    printf(
+        "  Length      Date    Time    Name\n"
+        "---------  ---------- -----   ----\n");
+  }
+}
+
+static void MaybeShowFooter() {
+  if (flag_v) {
+    printf(
+        "--------          -------  ---                            -------\n"
+        "%8" PRId64 "         %8" PRId64 " %3d%%                            %zu file%s\n",
+        total_uncompressed_length, total_compressed_length,
+        CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
+        (file_count == 1) ? "" : "s");
+  } else if (flag_l) {
+    printf(
+        "---------                     -------\n"
+        "%9" PRId64 "                     %zu file%s\n",
+        total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
+  }
+}
+
+static bool PromptOverwrite(const std::string& dst) {
+  // TODO: [r]ename not implemented because it doesn't seem useful.
+  printf("replace %s? [y]es, [n]o, [A]ll, [N]one: ", dst.c_str());
+  fflush(stdout);
+  while (true) {
+    char* line = nullptr;
+    size_t n;
+    if (getline(&line, &n, stdin) == -1) {
+      error(1, 0, "(EOF/read error; assuming [N]one...)");
+      overwrite_mode = kNever;
+      return false;
+    }
+    if (n == 0) continue;
+    char cmd = line[0];
+    free(line);
+    switch (cmd) {
+      case 'y':
+        return true;
+      case 'n':
+        return false;
+      case 'A':
+        overwrite_mode = kAlways;
+        return true;
+      case 'N':
+        overwrite_mode = kNever;
+        return false;
+    }
+  }
+}
+
+static void ExtractToPipe(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+  // We need to extract to memory because ExtractEntryToFile insists on
+  // being able to seek and truncate, and you can't do that with stdout.
+  uint8_t* buffer = new uint8_t[entry.uncompressed_length];
+  int err = ExtractToMemory(zah, &entry, buffer, entry.uncompressed_length);
+  if (err < 0) {
+    error(1, 0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
+  }
+  if (!android::base::WriteFully(1, buffer, entry.uncompressed_length)) {
+    error(1, errno, "failed to write %s to stdout", name.c_str());
+  }
+  delete[] buffer;
+}
+
+static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+  // Bad filename?
+  if (android::base::StartsWith(name, "/") || android::base::StartsWith(name, "../") ||
+      name.find("/../") != std::string::npos) {
+    error(1, 0, "bad filename %s", name.c_str());
+  }
+
+  // Where are we actually extracting to (for human-readable output)?
+  std::string dst;
+  if (flag_d) {
+    dst = flag_d;
+    if (!android::base::EndsWith(dst, "/")) dst += '/';
+  }
+  dst += name;
+
+  // Ensure the directory hierarchy exists.
+  if (!MakeDirectoryHierarchy(android::base::Dirname(name))) {
+    error(1, errno, "couldn't create directory hierarchy for %s", dst.c_str());
+  }
+
+  // An entry in a zip file can just be a directory itself.
+  if (android::base::EndsWith(name, "/")) {
+    if (mkdir(name.c_str(), entry.unix_mode) == -1) {
+      // If the directory already exists, that's fine.
+      if (errno == EEXIST) {
+        struct stat sb;
+        if (stat(name.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return;
+      }
+      error(1, errno, "couldn't extract directory %s", dst.c_str());
+    }
+    return;
+  }
+
+  // Create the file.
+  int fd = open(name.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC | O_EXCL, entry.unix_mode);
+  if (fd == -1 && errno == EEXIST) {
+    if (overwrite_mode == kNever) return;
+    if (overwrite_mode == kPrompt && !PromptOverwrite(dst)) return;
+    // Either overwrite_mode is kAlways or the user consented to this specific case.
+    fd = open(name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, entry.unix_mode);
+  }
+  if (fd == -1) error(1, errno, "couldn't create file %s", dst.c_str());
+
+  // Actually extract into the file.
+  if (!flag_q) printf("  inflating: %s\n", dst.c_str());
+  int err = ExtractEntryToFile(zah, &entry, fd);
+  if (err < 0) error(1, 0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
+  close(fd);
+}
+
+static void ListOne(const ZipEntry& entry, const std::string& name) {
+  tm t = entry.GetModificationTime();
+  char time[32];
+  snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
+           t.tm_mday, t.tm_hour, t.tm_min);
+  if (flag_v) {
+    printf("%8d  %s  %7d %3d%% %s %08x  %s\n", entry.uncompressed_length,
+           (entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length,
+           CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32,
+           name.c_str());
+  } else {
+    printf("%9d  %s   %s\n", entry.uncompressed_length, time, name.c_str());
+  }
+}
+
+static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+  if (flag_l || flag_v) {
+    // -l or -lv or -lq or -v.
+    ListOne(entry, name);
+  } else {
+    // Actually extract.
+    if (flag_p) {
+      ExtractToPipe(zah, entry, name);
+    } else {
+      ExtractOne(zah, entry, name);
+    }
+  }
+  total_uncompressed_length += entry.uncompressed_length;
+  total_compressed_length += entry.compressed_length;
+  ++file_count;
+}
+
+static void ProcessAll(ZipArchiveHandle zah) {
+  MaybeShowHeader();
+
+  // libziparchive iteration order doesn't match the central directory.
+  // We could sort, but that would cost extra and wouldn't match either.
+  void* cookie;
+  int err = StartIteration(zah, &cookie, nullptr, nullptr);
+  if (err != 0) {
+    error(1, 0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
+  }
+
+  ZipEntry entry;
+  ZipString string;
+  while ((err = Next(cookie, &entry, &string)) >= 0) {
+    std::string name(string.name, string.name + string.name_length);
+    if (!Filter(name)) ProcessOne(zah, entry, name);
+  }
+
+  if (err < -1) error(1, 0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
+  EndIteration(cookie);
+
+  MaybeShowFooter();
+}
+
+static void ShowHelp(bool full) {
+  fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
+  if (!full) exit(EXIT_FAILURE);
+
+  printf(
+      "\n"
+      "Extract FILEs from ZIP archive. Default is all files.\n"
+      "\n"
+      "-d DIR	Extract into DIR\n"
+      "-l	List contents (-lq excludes archive name, -lv is verbose)\n"
+      "-n	Never overwrite files (default: prompt)\n"
+      "-o	Always overwrite files\n"
+      "-p	Pipe to stdout\n"
+      "-q	Quiet\n"
+      "-v	List contents verbosely\n"
+      "-x FILE	Exclude files\n");
+  exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char* argv[]) {
+  static struct option opts[] = {
+      {"help", no_argument, 0, 'h'},
+  };
+  bool saw_x = false;
+  int opt;
+  while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
+    switch (opt) {
+      case 'd':
+        flag_d = optarg;
+        break;
+      case 'h':
+        ShowHelp(true);
+        break;
+      case 'l':
+        flag_l = true;
+        break;
+      case 'n':
+        overwrite_mode = kNever;
+        break;
+      case 'o':
+        overwrite_mode = kAlways;
+        break;
+      case 'p':
+        flag_p = flag_q = true;
+        break;
+      case 'q':
+        flag_q = true;
+        break;
+      case 'v':
+        flag_v = true;
+        break;
+      case 'x':
+        saw_x = true;
+        break;
+      case 1:
+        // -x swallows all following arguments, so we use '-' in the getopt
+        // string and collect files here.
+        if (!archive_name) {
+          archive_name = optarg;
+        } else if (saw_x) {
+          excludes.insert(optarg);
+        } else {
+          includes.insert(optarg);
+        }
+        break;
+      default:
+        ShowHelp(false);
+    }
+  }
+
+  if (!archive_name) error(1, 0, "missing archive filename");
+
+  // We can't support "-" to unzip from stdin because libziparchive relies on mmap.
+  ZipArchiveHandle zah;
+  int32_t err;
+  if ((err = OpenArchive(archive_name, &zah)) != 0) {
+    error(1, 0, "couldn't open %s: %s", archive_name, ErrorCodeString(err));
+  }
+
+  // Implement -d by changing into that directory.
+  // We'll create implicit directories based on paths in the zip file, but we
+  // require that the -d directory already exists.
+  if (flag_d && chdir(flag_d) == -1) error(1, errno, "couldn't chdir to %s", flag_d);
+
+  ProcessAll(zah);
+
+  CloseArchive(zah);
+  return 0;
+}
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 246575f..17c268b 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -27,6 +27,7 @@
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 
 #include <memory>
@@ -48,6 +49,10 @@
 
 using android::base::get_unaligned;
 
+// Used to turn on crc checks - verify that the content CRC matches the values
+// specified in the local file header and the central directory.
+static const bool kCrcChecksEnabled = false;
+
 // This is for windows. If we don't open a file in binary mode, weird
 // things will happen.
 #ifndef O_BINARY
@@ -57,68 +62,6 @@
 // The maximum number of bytes to scan backwards for the EOCD start.
 static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
 
-static const char* kErrorMessages[] = {
-  "Unknown return code.",
-  "Iteration ended",
-  "Zlib error",
-  "Invalid file",
-  "Invalid handle",
-  "Duplicate entries in archive",
-  "Empty archive",
-  "Entry not found",
-  "Invalid offset",
-  "Inconsistent information",
-  "Invalid entry name",
-  "I/O Error",
-  "File mapping failed"
-};
-
-static const int32_t kErrorMessageUpperBound = 0;
-
-static const int32_t kIterationEnd = -1;
-
-// We encountered a Zlib error when inflating a stream from this file.
-// Usually indicates file corruption.
-static const int32_t kZlibError = -2;
-
-// The input file cannot be processed as a zip archive. Usually because
-// it's too small, too large or does not have a valid signature.
-static const int32_t kInvalidFile = -3;
-
-// An invalid iteration / ziparchive handle was passed in as an input
-// argument.
-static const int32_t kInvalidHandle = -4;
-
-// The zip archive contained two (or possibly more) entries with the same
-// name.
-static const int32_t kDuplicateEntry = -5;
-
-// The zip archive contains no entries.
-static const int32_t kEmptyArchive = -6;
-
-// The specified entry was not found in the archive.
-static const int32_t kEntryNotFound = -7;
-
-// The zip archive contained an invalid local file header pointer.
-static const int32_t kInvalidOffset = -8;
-
-// The zip archive contained inconsistent entry information. This could
-// be because the central directory & local file header did not agree, or
-// if the actual uncompressed length or crc32 do not match their declared
-// values.
-static const int32_t kInconsistentInformation = -9;
-
-// An invalid entry name was encountered.
-static const int32_t kInvalidEntryName = -10;
-
-// An I/O related system call (read, lseek, ftruncate, map) failed.
-static const int32_t kIoError = -11;
-
-// We were not able to mmap the central directory or entry contents.
-static const int32_t kMmapFailed = -12;
-
-static const int32_t kErrorMessageLowerBound = -13;
-
 /*
  * A Read-only Zip archive.
  *
@@ -172,8 +115,7 @@
  * Convert a ZipEntry to a hash table index, verifying that it's in a
  * valid range.
  */
-static int64_t EntryToIndex(const ZipString* hash_table,
-                            const uint32_t hash_table_size,
+static int64_t EntryToIndex(const ZipString* hash_table, const uint32_t hash_table_size,
                             const ZipString& name) {
   const uint32_t hash = ComputeHash(name);
 
@@ -194,7 +136,7 @@
 /*
  * Add a new entry to the hash table.
  */
-static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
+static int32_t AddToHash(ZipString* hash_table, const uint64_t hash_table_size,
                          const ZipString& name) {
   const uint64_t hash = ComputeHash(name);
   uint32_t ent = hash & (hash_table_size - 1);
@@ -218,13 +160,12 @@
 }
 
 static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
-                                    off64_t file_length, off64_t read_amount,
-                                    uint8_t* scan_buffer) {
+                                    off64_t file_length, off64_t read_amount, uint8_t* scan_buffer) {
   const off64_t search_start = file_length - read_amount;
 
-  if(!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
-    ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed",
-          static_cast<int64_t>(read_amount), static_cast<int64_t>(search_start));
+  if (!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
+    ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed", static_cast<int64_t>(read_amount),
+          static_cast<int64_t>(search_start));
     return kIoError;
   }
 
@@ -255,8 +196,7 @@
    * Verify that there's no trailing space at the end of the central directory
    * and its comment.
    */
-  const off64_t calculated_length = eocd_offset + sizeof(EocdRecord)
-      + eocd->comment_length;
+  const off64_t calculated_length = eocd_offset + sizeof(EocdRecord) + eocd->comment_length;
   if (calculated_length != file_length) {
     ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
           static_cast<int64_t>(file_length - calculated_length));
@@ -269,7 +209,7 @@
    */
   if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
     ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
-        eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
+          eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
 #if defined(__ANDROID__)
     if (eocd->cd_start_offset + eocd->cd_size <= eocd_offset) {
       android_errorWriteLog(0x534e4554, "31251826");
@@ -282,8 +222,8 @@
     return kEmptyArchive;
   }
 
-  ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32,
-        eocd->num_records, eocd->cd_size, eocd->cd_start_offset);
+  ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32, eocd->num_records,
+        eocd->cd_size, eocd->cd_start_offset);
 
   /*
    * It all looks good.  Create a mapping for the CD, and set the fields
@@ -312,7 +252,6 @@
  *   num_entries
  */
 static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) {
-
   // Test file length. We use lseek64 to make sure the file
   // is small enough to be a zip file (Its size must be less than
   // 0xffffffff bytes).
@@ -349,8 +288,8 @@
   }
 
   std::vector<uint8_t> scan_buffer(read_amount);
-  int32_t result = MapCentralDirectory0(debug_file_name, archive, file_length, read_amount,
-                                        scan_buffer.data());
+  int32_t result =
+      MapCentralDirectory0(debug_file_name, archive, file_length, read_amount, scan_buffer.data());
   return result;
 }
 
@@ -371,8 +310,13 @@
    * least one unused entry to avoid an infinite loop during creation.
    */
   archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
-  archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
-      sizeof(ZipString)));
+  archive->hash_table =
+      reinterpret_cast<ZipString*>(calloc(archive->hash_table_size, sizeof(ZipString)));
+  if (archive->hash_table == nullptr) {
+    ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu",
+          archive->hash_table_size, sizeof(ZipString));
+    return -1;
+  }
 
   /*
    * Walk through the central directory, adding entries to the hash
@@ -381,22 +325,24 @@
   const uint8_t* const cd_end = cd_ptr + cd_length;
   const uint8_t* ptr = cd_ptr;
   for (uint16_t i = 0; i < num_entries; i++) {
-    const CentralDirectoryRecord* cdr =
-        reinterpret_cast<const CentralDirectoryRecord*>(ptr);
-    if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
-      ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
+    if (ptr > cd_end - sizeof(CentralDirectoryRecord)) {
+      ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
+#if defined(__ANDROID__)
+      android_errorWriteLog(0x534e4554, "36392138");
+#endif
       return -1;
     }
 
-    if (ptr + sizeof(CentralDirectoryRecord) > cd_end) {
-      ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
+    const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
+    if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
+      ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
       return -1;
     }
 
     const off64_t local_header_offset = cdr->local_file_header_offset;
     if (local_header_offset >= archive->directory_offset) {
       ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
-          static_cast<int64_t>(local_header_offset), i);
+            static_cast<int64_t>(local_header_offset), i);
       return -1;
     }
 
@@ -405,6 +351,13 @@
     const uint16_t comment_length = cdr->comment_length;
     const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
 
+    if (file_name + file_name_length > cd_end) {
+      ALOGW(
+          "Zip: file name boundary exceeds the central directory range, file_name_length: "
+          "%" PRIx16 ", cd_length: %zu",
+          file_name_length, cd_length);
+      return -1;
+    }
     /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
     if (!IsValidEntryName(file_name, file_name_length)) {
       return -1;
@@ -414,8 +367,7 @@
     ZipString entry_name;
     entry_name.name = file_name;
     entry_name.name_length = file_name_length;
-    const int add_result = AddToHash(archive->hash_table,
-        archive->hash_table_size, entry_name);
+    const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name);
     if (add_result != 0) {
       ALOGW("Zip: Error adding entry to hash table %d", add_result);
       return add_result;
@@ -423,8 +375,7 @@
 
     ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
     if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
-      ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16,
-          ptr - cd_ptr, cd_length, i);
+      ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16, ptr - cd_ptr, cd_length, i);
       return -1;
     }
   }
@@ -433,8 +384,7 @@
   return 0;
 }
 
-static int32_t OpenArchiveInternal(ZipArchive* archive,
-                                   const char* debug_file_name) {
+static int32_t OpenArchiveInternal(ZipArchive* archive, const char* debug_file_name) {
   int32_t result = -1;
   if ((result = MapCentralDirectory(debug_file_name, archive)) != 0) {
     return result;
@@ -447,8 +397,8 @@
   return 0;
 }
 
-int32_t OpenArchiveFd(int fd, const char* debug_file_name,
-                      ZipArchiveHandle* handle, bool assume_ownership) {
+int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
+                      bool assume_ownership) {
   ZipArchive* archive = new ZipArchive(fd, assume_ownership);
   *handle = archive;
   return OpenArchiveInternal(archive, debug_file_name);
@@ -468,7 +418,7 @@
 }
 
 int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debug_file_name,
-                              ZipArchiveHandle *handle) {
+                              ZipArchiveHandle* handle) {
   ZipArchive* archive = new ZipArchive(address, length);
   *handle = archive;
   return OpenArchiveInternal(archive, debug_file_name);
@@ -483,8 +433,7 @@
   delete archive;
 }
 
-static int32_t UpdateEntryFromDataDescriptor(MappedZipFile& mapped_zip,
-                                             ZipEntry *entry) {
+static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, ZipEntry* entry) {
   uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
   if (!mapped_zip.ReadData(ddBuf, sizeof(ddBuf))) {
     return kIoError;
@@ -494,15 +443,22 @@
   const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
   const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
 
-  entry->crc32 = descriptor->crc32;
-  entry->compressed_length = descriptor->compressed_size;
-  entry->uncompressed_length = descriptor->uncompressed_size;
+  // Validate that the values in the data descriptor match those in the central
+  // directory.
+  if (entry->compressed_length != descriptor->compressed_size ||
+      entry->uncompressed_length != descriptor->uncompressed_size ||
+      entry->crc32 != descriptor->crc32) {
+    ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32
+          "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
+          entry->compressed_length, entry->uncompressed_length, entry->crc32,
+          descriptor->compressed_size, descriptor->uncompressed_size, descriptor->crc32);
+    return kInconsistentInformation;
+  }
 
   return 0;
 }
 
-static int32_t FindEntry(const ZipArchive* archive, const int ent,
-                         ZipEntry* data) {
+static int32_t FindEntry(const ZipArchive* archive, const int ent, ZipEntry* data) {
   const uint16_t nameLen = archive->hash_table[ent].name_length;
 
   // Recover the start of the central directory entry from the filename
@@ -520,8 +476,7 @@
     return kInvalidOffset;
   }
 
-  const CentralDirectoryRecord *cdr =
-      reinterpret_cast<const CentralDirectoryRecord*>(ptr);
+  const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
 
   // The offset of the start of the central directory in the zipfile.
   // We keep this lying around so that we can sanity check all our lengths
@@ -549,46 +504,62 @@
   uint8_t lfh_buf[sizeof(LocalFileHeader)];
   if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) {
     ALOGW("Zip: failed reading lfh name from offset %" PRId64,
-        static_cast<int64_t>(local_header_offset));
+          static_cast<int64_t>(local_header_offset));
     return kIoError;
   }
 
-  const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
+  const LocalFileHeader* lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
 
   if (lfh->lfh_signature != LocalFileHeader::kSignature) {
     ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
-        static_cast<int64_t>(local_header_offset));
+          static_cast<int64_t>(local_header_offset));
     return kInvalidOffset;
   }
 
   // Paranoia: Match the values specified in the local file header
   // to those specified in the central directory.
 
-  // Verify that the central directory and local file header agree on the use of a trailing
-  // Data Descriptor.
+  // Warn if central directory and local file header don't agree on the use
+  // of a trailing Data Descriptor. The reference implementation is inconsistent
+  // and appears to use the LFH value during extraction (unzip) but the CD value
+  // while displayng information about archives (zipinfo). The spec remains
+  // silent on this inconsistency as well.
+  //
+  // For now, always use the version from the LFH but make sure that the values
+  // specified in the central directory match those in the data descriptor.
+  //
+  // NOTE: It's also worth noting that unzip *does* warn about inconsistencies in
+  // bit 11 (EFS: The language encoding flag, marking that filename and comment are
+  // encoded using UTF-8). This implementation does not check for the presence of
+  // that flag and always enforces that entry names are valid UTF-8.
   if ((lfh->gpb_flags & kGPBDDFlagMask) != (cdr->gpb_flags & kGPBDDFlagMask)) {
-    ALOGW("Zip: gpb flag mismatch. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
+    ALOGW("Zip: gpb flag mismatch at bit 3. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
           cdr->gpb_flags, lfh->gpb_flags);
-    return kInconsistentInformation;
   }
 
   // If there is no trailing data descriptor, verify that the central directory and local file
   // header agree on the crc, compressed, and uncompressed sizes of the entry.
   if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
     data->has_data_descriptor = 0;
-    if (data->compressed_length != lfh->compressed_size
-        || data->uncompressed_length != lfh->uncompressed_size
-        || data->crc32 != lfh->crc32) {
-      ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32
-        ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
-        data->compressed_length, data->uncompressed_length, data->crc32,
-        lfh->compressed_size, lfh->uncompressed_size, lfh->crc32);
+    if (data->compressed_length != lfh->compressed_size ||
+        data->uncompressed_length != lfh->uncompressed_size || data->crc32 != lfh->crc32) {
+      ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32
+            "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
+            data->compressed_length, data->uncompressed_length, data->crc32, lfh->compressed_size,
+            lfh->uncompressed_size, lfh->crc32);
       return kInconsistentInformation;
     }
   } else {
     data->has_data_descriptor = 1;
   }
 
+  // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
+  if ((cdr->version_made_by >> 8) == 3) {
+    data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
+  } else {
+    data->unix_mode = 0777;
+  }
+
   // Check that the local file header name matches the declared
   // name in the central directory.
   if (lfh->file_name_length == nameLen) {
@@ -613,8 +584,8 @@
     return kInconsistentInformation;
   }
 
-  const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader)
-      + lfh->file_name_length + lfh->extra_field_length;
+  const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader) +
+                              lfh->file_name_length + lfh->extra_field_length;
   if (data_offset > cd_offset) {
     ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
     return kInvalidOffset;
@@ -622,16 +593,17 @@
 
   if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
     ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
-      static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset));
+          static_cast<int64_t>(data_offset), data->compressed_length,
+          static_cast<int64_t>(cd_offset));
     return kInvalidOffset;
   }
 
   if (data->method == kCompressStored &&
-    static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
-     ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
-       static_cast<int64_t>(data_offset), data->uncompressed_length,
-       static_cast<int64_t>(cd_offset));
-     return kInvalidOffset;
+      static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
+    ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
+          static_cast<int64_t>(data_offset), data->uncompressed_length,
+          static_cast<int64_t>(cd_offset));
+    return kInvalidOffset;
   }
 
   data->offset = data_offset;
@@ -646,8 +618,7 @@
   ZipString suffix;
   ZipArchive* archive;
 
-  IterationHandle(const ZipString* in_prefix,
-                  const ZipString* in_suffix) {
+  IterationHandle(const ZipString* in_prefix, const ZipString* in_suffix) {
     if (in_prefix) {
       uint8_t* name_copy = new uint8_t[in_prefix->name_length];
       memcpy(name_copy, in_prefix->name, in_prefix->name_length);
@@ -674,8 +645,7 @@
   }
 };
 
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
-                       const ZipString* optional_prefix,
+int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
                        const ZipString* optional_suffix) {
   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
 
@@ -688,7 +658,7 @@
   cookie->position = 0;
   cookie->archive = archive;
 
-  *cookie_ptr = cookie ;
+  *cookie_ptr = cookie;
   return 0;
 }
 
@@ -696,16 +666,14 @@
   delete reinterpret_cast<IterationHandle*>(cookie);
 }
 
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
-                  ZipEntry* data) {
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data) {
   const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
   if (entryName.name_length == 0) {
     ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
     return kInvalidEntryName;
   }
 
-  const int64_t ent = EntryToIndex(archive->hash_table,
-    archive->hash_table_size, entryName);
+  const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName);
 
   if (ent < 0) {
     ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
@@ -733,10 +701,8 @@
 
   for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
     if (hash_table[i].name != NULL &&
-        (handle->prefix.name_length == 0 ||
-         hash_table[i].StartsWith(handle->prefix)) &&
-        (handle->suffix.name_length == 0 ||
-         hash_table[i].EndsWith(handle->suffix))) {
+        (handle->prefix.name_length == 0 || hash_table[i].StartsWith(handle->prefix)) &&
+        (handle->suffix.name_length == 0 || hash_table[i].EndsWith(handle->suffix))) {
       handle->position = (i + 1);
       const int error = FindEntry(archive, i, data);
       if (!error) {
@@ -756,8 +722,10 @@
  public:
   virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
   virtual ~Writer() {}
+
  protected:
   Writer() = default;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(Writer);
 };
@@ -767,14 +735,12 @@
 // the data appended to it.
 class MemoryWriter : public Writer {
  public:
-  MemoryWriter(uint8_t* buf, size_t size) : Writer(),
-      buf_(buf), size_(size), bytes_written_(0) {
-  }
+  MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {}
 
   virtual bool Append(uint8_t* buf, size_t buf_size) override {
     if (bytes_written_ + buf_size > size_) {
-      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
-            size_, bytes_written_ + buf_size);
+      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", size_,
+            bytes_written_ + buf_size);
       return false;
     }
 
@@ -793,7 +759,6 @@
 // The file will be truncated to the end of the written data.
 class FileWriter : public Writer {
  public:
-
   // Creates a FileWriter for |fd| and prepare to write |entry| to it,
   // guaranteeing that the file descriptor is valid and that there's enough
   // space on the volume to write out the entry completely and that the file
@@ -852,8 +817,8 @@
 
   virtual bool Append(uint8_t* buf, size_t buf_size) override {
     if (total_bytes_written_ + buf_size > declared_length_) {
-      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
-            declared_length_, total_bytes_written_ + buf_size);
+      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", declared_length_,
+            total_bytes_written_ + buf_size);
       return false;
     }
 
@@ -866,13 +831,10 @@
 
     return result;
   }
+
  private:
-  FileWriter(const int fd, const size_t declared_length) :
-      Writer(),
-      fd_(fd),
-      declared_length_(declared_length),
-      total_bytes_written_(0) {
-  }
+  FileWriter(const int fd, const size_t declared_length)
+      : Writer(), fd_(fd), declared_length_(declared_length), total_bytes_written_(0) {}
 
   const int fd_;
   const size_t declared_length_;
@@ -915,8 +877,7 @@
   zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
   if (zerr != Z_OK) {
     if (zerr == Z_VERSION_ERROR) {
-      ALOGE("Installed zlib is not compatible with linked version (%s)",
-        ZLIB_VERSION);
+      ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
     } else {
       ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
     }
@@ -925,13 +886,14 @@
   }
 
   auto zstream_deleter = [](z_stream* stream) {
-    inflateEnd(stream);  /* free up any allocated structures */
+    inflateEnd(stream); /* free up any allocated structures */
   };
 
   std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
 
   const uint32_t uncompressed_length = entry->uncompressed_length;
 
+  uint64_t crc = 0;
   uint32_t compressed_length = entry->compressed_length;
   do {
     /* read as much as we can */
@@ -951,19 +913,19 @@
     /* uncompress the data */
     zerr = inflate(&zstream, Z_NO_FLUSH);
     if (zerr != Z_OK && zerr != Z_STREAM_END) {
-      ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
-          zerr, zstream.next_in, zstream.avail_in,
-          zstream.next_out, zstream.avail_out);
+      ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, zstream.next_in,
+            zstream.avail_in, zstream.next_out, zstream.avail_out);
       return kZlibError;
     }
 
     /* write when we're full or when we're done */
-    if (zstream.avail_out == 0 ||
-      (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
+    if (zstream.avail_out == 0 || (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
       const size_t write_size = zstream.next_out - &write_buf[0];
       if (!writer->Append(&write_buf[0], write_size)) {
         // The file might have declared a bogus length.
         return kInconsistentInformation;
+      } else {
+        crc = crc32(crc, &write_buf[0], write_size);
       }
 
       zstream.next_out = &write_buf[0];
@@ -971,14 +933,19 @@
     }
   } while (zerr == Z_OK);
 
-  assert(zerr == Z_STREAM_END);     /* other errors should've been caught */
+  assert(zerr == Z_STREAM_END); /* other errors should've been caught */
 
-  // stream.adler holds the crc32 value for such streams.
-  *crc_out = zstream.adler;
+  // NOTE: zstream.adler is always set to 0, because we're using the -MAX_WBITS
+  // "feature" of zlib to tell it there won't be a zlib file header. zlib
+  // doesn't bother calculating the checksum in that scenario. We just do
+  // it ourselves above because there are no additional gains to be made by
+  // having zlib calculate it for us, since they do it by calling crc32 in
+  // the same manner that we have above.
+  *crc_out = crc;
 
   if (zstream.total_out != uncompressed_length || compressed_length != 0) {
-    ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
-        zstream.total_out, uncompressed_length);
+    ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", zstream.total_out,
+          uncompressed_length);
     return kInconsistentInformation;
   }
 
@@ -986,7 +953,7 @@
 }
 
 static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry, Writer* writer,
-                                 uint64_t *crc_out) {
+                                 uint64_t* crc_out) {
   static const uint32_t kBufSize = 32768;
   std::vector<uint8_t> buf(kBufSize);
 
@@ -1016,8 +983,7 @@
   return 0;
 }
 
-int32_t ExtractToWriter(ZipArchiveHandle handle,
-                        ZipEntry* entry, Writer* writer) {
+int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, Writer* writer) {
   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
   const uint16_t method = entry->method;
   off64_t data_offset = entry->offset;
@@ -1037,15 +1003,14 @@
   }
 
   if (!return_value && entry->has_data_descriptor) {
-    return_value = UpdateEntryFromDataDescriptor(archive->mapped_zip, entry);
+    return_value = ValidateDataDescriptor(archive->mapped_zip, entry);
     if (return_value) {
       return return_value;
     }
   }
 
-  // TODO: Fix this check by passing the right flags to inflate2 so that
-  // it calculates the CRC for us.
-  if (entry->crc32 != crc && false) {
+  // Validate that the CRC matches the calculated value.
+  if (kCrcChecksEnabled && (entry->crc32 != static_cast<uint32_t>(crc))) {
     ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
     return kInconsistentInformation;
   }
@@ -1053,14 +1018,12 @@
   return return_value;
 }
 
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
-                        uint8_t* begin, uint32_t size) {
+int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size) {
   std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
   return ExtractToWriter(handle, entry, writer.get());
 }
 
-int32_t ExtractEntryToFile(ZipArchiveHandle handle,
-                           ZipEntry* entry, int fd) {
+int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd) {
   std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
   if (writer.get() == nullptr) {
     return kIoError;
@@ -1070,19 +1033,24 @@
 }
 
 const char* ErrorCodeString(int32_t error_code) {
-  if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) {
-    return kErrorMessages[error_code * -1];
+  // Make sure that the number of entries in kErrorMessages and ErrorCodes
+  // match.
+  static_assert((-kLastErrorCode + 1) == arraysize(kErrorMessages),
+                "(-kLastErrorCode + 1) != arraysize(kErrorMessages)");
+
+  const uint32_t idx = -error_code;
+  if (idx < arraysize(kErrorMessages)) {
+    return kErrorMessages[idx];
   }
 
-  return kErrorMessages[0];
+  return "Unknown return code";
 }
 
 int GetFileDescriptor(const ZipArchiveHandle handle) {
   return reinterpret_cast<ZipArchive*>(handle)->mapped_zip.GetFileDescriptor();
 }
 
-ZipString::ZipString(const char* entry_name)
-    : name(reinterpret_cast<const uint8_t*>(entry_name)) {
+ZipString::ZipString(const char* entry_name) : name(reinterpret_cast<const uint8_t*>(entry_name)) {
   size_t len = strlen(entry_name);
   CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
   name_length = static_cast<uint16_t>(len);
@@ -1091,10 +1059,8 @@
 #if !defined(_WIN32)
 class ProcessWriter : public Writer {
  public:
-  ProcessWriter(ProcessZipEntryFunction func, void* cookie) : Writer(),
-    proc_function_(func),
-    cookie_(cookie) {
-  }
+  ProcessWriter(ProcessZipEntryFunction func, void* cookie)
+      : Writer(), proc_function_(func), cookie_(cookie) {}
 
   virtual bool Append(uint8_t* buf, size_t buf_size) override {
     return proc_function_(buf, buf_size, cookie_);
@@ -1111,7 +1077,7 @@
   return ExtractToWriter(handle, entry, &writer);
 }
 
-#endif //!defined(_WIN32)
+#endif  //! defined(_WIN32)
 
 int MappedZipFile::GetFileDescriptor() const {
   if (!has_fd_) {
@@ -1154,8 +1120,7 @@
     return true;
   } else {
     if (offset < 0 || offset > static_cast<off64_t>(data_length_)) {
-      ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n" , offset,
-            data_length_);
+      ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n", offset, data_length_);
       return false;
     }
 
@@ -1166,7 +1131,7 @@
 
 bool MappedZipFile::ReadData(uint8_t* buffer, size_t read_amount) {
   if (has_fd_) {
-    if(!android::base::ReadFully(fd_, buffer, read_amount)) {
+    if (!android::base::ReadFully(fd_, buffer, read_amount)) {
       ALOGE("Zip: read from %d failed\n", fd_);
       return false;
     }
@@ -1192,7 +1157,6 @@
     return false;
   }
   return ReadData(buf, len);
-
 }
 
 void CentralDirectory::Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size) {
@@ -1203,13 +1167,13 @@
 bool ZipArchive::InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
                                             size_t cd_size) {
   if (mapped_zip.HasFd()) {
-    if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(),
-                               cd_start_offset, cd_size, true /* read only */)) {
+    if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(), cd_start_offset,
+                               cd_size, true /* read only */)) {
       return false;
     }
 
     CHECK_EQ(directory_map->getDataLength(), cd_size);
-    central_directory.Initialize(directory_map->getDataPtr(), 0/*offset*/, cd_size);
+    central_directory.Initialize(directory_map->getDataPtr(), 0 /*offset*/, cd_size);
   } else {
     if (mapped_zip.GetBasePtr() == nullptr) {
       ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n");
@@ -1217,9 +1181,10 @@
     }
     if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
         mapped_zip.GetFileLength()) {
-      ALOGE("Zip: Failed to map central directory, offset exceeds mapped memory region ("
-            "start_offset %"  PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
-            static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
+      ALOGE(
+          "Zip: Failed to map central directory, offset exceeds mapped memory region ("
+          "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
+          static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
       return false;
     }
 
@@ -1227,3 +1192,17 @@
   }
   return true;
 }
+
+tm ZipEntry::GetModificationTime() const {
+  tm t = {};
+
+  t.tm_hour = (mod_time >> 11) & 0x1f;
+  t.tm_min = (mod_time >> 5) & 0x3f;
+  t.tm_sec = (mod_time & 0x1f) << 1;
+
+  t.tm_year = ((mod_time >> 25) & 0x7f) + 80;
+  t.tm_mon = ((mod_time >> 21) & 0xf) - 1;
+  t.tm_mday = (mod_time >> 16) & 0x1f;
+
+  return t;
+}
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
new file mode 100644
index 0000000..cd3e164
--- /dev/null
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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 <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <android-base/test_utils.h>
+#include <benchmark/benchmark.h>
+#include <ziparchive/zip_archive.h>
+#include <ziparchive/zip_archive_stream_entry.h>
+#include <ziparchive/zip_writer.h>
+
+static TemporaryFile* CreateZip() {
+  TemporaryFile* result = new TemporaryFile;
+  FILE* fp = fdopen(result->fd, "w");
+
+  ZipWriter writer(fp);
+  std::string lastName = "file";
+  for (size_t i = 0; i < 1000; i++) {
+    // Make file names longer and longer.
+    lastName = lastName + std::to_string(i);
+    writer.StartEntry(lastName.c_str(), ZipWriter::kCompress);
+    writer.WriteBytes("helo", 4);
+    writer.FinishEntry();
+  }
+  writer.Finish();
+  fclose(fp);
+
+  return result;
+}
+
+static void FindEntry_no_match(benchmark::State& state) {
+  // Create a temporary zip archive.
+  std::unique_ptr<TemporaryFile> temp_file(CreateZip());
+  ZipArchiveHandle handle;
+  ZipEntry data;
+
+  // In order to walk through all file names in the archive, look for a name
+  // that does not exist in the archive.
+  ZipString name("thisFileNameDoesNotExist");
+
+  // Start the benchmark.
+  while (state.KeepRunning()) {
+    OpenArchive(temp_file->path, &handle);
+    FindEntry(handle, name, &data);
+    CloseArchive(handle);
+  }
+}
+BENCHMARK(FindEntry_no_match);
+
+static void Iterate_all_files(benchmark::State& state) {
+  std::unique_ptr<TemporaryFile> temp_file(CreateZip());
+  ZipArchiveHandle handle;
+  void* iteration_cookie;
+  ZipEntry data;
+  ZipString name;
+
+  while (state.KeepRunning()) {
+    OpenArchive(temp_file->path, &handle);
+    StartIteration(handle, &iteration_cookie, nullptr, nullptr);
+    while (Next(iteration_cookie, &data, &name) == 0) {
+    }
+    EndIteration(iteration_cookie);
+    CloseArchive(handle);
+  }
+}
+BENCHMARK(Iterate_all_files);
+
+BENCHMARK_MAIN()
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h
index ca42509..8b99bde 100644
--- a/libziparchive/zip_archive_common.h
+++ b/libziparchive/zip_archive_common.h
@@ -57,6 +57,7 @@
   uint32_t cd_start_offset;
   // Length of the central directory comment.
   uint16_t comment_length;
+
  private:
   EocdRecord() = default;
   DISALLOW_COPY_AND_ASSIGN(EocdRecord);
@@ -73,7 +74,7 @@
 
   // The start of record signature. Must be |kSignature|.
   uint32_t record_signature;
-  // Tool version. Ignored by this implementation.
+  // Source tool version. Top byte gives source OS.
   uint16_t version_made_by;
   // Tool version. Ignored by this implementation.
   uint16_t version_needed;
@@ -106,11 +107,12 @@
   uint16_t file_start_disk;
   // File attributes. Ignored by this implementation.
   uint16_t internal_file_attributes;
-  // File attributes. Ignored by this implementation.
+  // File attributes. For archives created on Unix, the top bits are the mode.
   uint32_t external_file_attributes;
   // The offset to the local file header for this entry, from the
   // beginning of this archive.
   uint32_t local_file_header_offset;
+
  private:
   CentralDirectoryRecord() = default;
   DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
@@ -149,6 +151,7 @@
   // The length of the extra field info (in bytes). This data
   // will appear immediately after the entry file name.
   uint16_t extra_field_length;
+
  private:
   LocalFileHeader() = default;
   DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
@@ -164,6 +167,7 @@
   uint32_t compressed_size;
   // Uncompressed size of the entry.
   uint32_t uncompressed_size;
+
  private:
   DataDescriptor() = default;
   DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 971db4f..840f1af 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -26,24 +26,83 @@
 
 #include <utils/FileMap.h>
 #include <ziparchive/zip_archive.h>
+#include "android-base/macros.h"
+
+static const char* kErrorMessages[] = {
+    "Success",
+    "Iteration ended",
+    "Zlib error",
+    "Invalid file",
+    "Invalid handle",
+    "Duplicate entries in archive",
+    "Empty archive",
+    "Entry not found",
+    "Invalid offset",
+    "Inconsistent information",
+    "Invalid entry name",
+    "I/O error",
+    "File mapping failed",
+};
+
+enum ErrorCodes : int32_t {
+  kIterationEnd = -1,
+
+  // We encountered a Zlib error when inflating a stream from this file.
+  // Usually indicates file corruption.
+  kZlibError = -2,
+
+  // The input file cannot be processed as a zip archive. Usually because
+  // it's too small, too large or does not have a valid signature.
+  kInvalidFile = -3,
+
+  // An invalid iteration / ziparchive handle was passed in as an input
+  // argument.
+  kInvalidHandle = -4,
+
+  // The zip archive contained two (or possibly more) entries with the same
+  // name.
+  kDuplicateEntry = -5,
+
+  // The zip archive contains no entries.
+  kEmptyArchive = -6,
+
+  // The specified entry was not found in the archive.
+  kEntryNotFound = -7,
+
+  // The zip archive contained an invalid local file header pointer.
+  kInvalidOffset = -8,
+
+  // The zip archive contained inconsistent entry information. This could
+  // be because the central directory & local file header did not agree, or
+  // if the actual uncompressed length or crc32 do not match their declared
+  // values.
+  kInconsistentInformation = -9,
+
+  // An invalid entry name was encountered.
+  kInvalidEntryName = -10,
+
+  // An I/O related system call (read, lseek, ftruncate, map) failed.
+  kIoError = -11,
+
+  // We were not able to mmap the central directory or entry contents.
+  kMmapFailed = -12,
+
+  kLastErrorCode = kMmapFailed,
+};
 
 class MappedZipFile {
  public:
-  explicit MappedZipFile(const int fd) :
-    has_fd_(true),
-    fd_(fd),
-    base_ptr_(nullptr),
-    data_length_(0),
-    read_pos_(0) {}
+  explicit MappedZipFile(const int fd)
+      : has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0), read_pos_(0) {}
 
-  explicit MappedZipFile(void* address, size_t length) :
-    has_fd_(false),
-    fd_(-1),
-    base_ptr_(address),
-    data_length_(static_cast<off64_t>(length)),
-    read_pos_(0) {}
+  explicit MappedZipFile(void* address, size_t length)
+      : has_fd_(false),
+        fd_(-1),
+        base_ptr_(address),
+        data_length_(static_cast<off64_t>(length)),
+        read_pos_(0) {}
 
-  bool HasFd() const {return has_fd_;}
+  bool HasFd() const { return has_fd_; }
 
   int GetFileDescriptor() const;
 
@@ -74,13 +133,11 @@
 
 class CentralDirectory {
  public:
-  CentralDirectory(void) :
-    base_ptr_(nullptr),
-    length_(0) {}
+  CentralDirectory(void) : base_ptr_(nullptr), length_(0) {}
 
-  const uint8_t* GetBasePtr() const {return base_ptr_;}
+  const uint8_t* GetBasePtr() const { return base_ptr_; }
 
-  size_t GetMapLength() const {return length_;}
+  size_t GetMapLength() const { return length_; }
 
   void Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);
 
@@ -109,25 +166,25 @@
   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(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(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) {
@@ -139,7 +196,6 @@
 
   bool InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
                                   size_t cd_size);
-
 };
 
 #endif  // LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
index 3f336a6..50352ef 100644
--- a/libziparchive/zip_archive_stream_entry.cc
+++ b/libziparchive/zip_archive_stream_entry.cc
@@ -162,8 +162,7 @@
   int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS);
   if (zerr != Z_OK) {
     if (zerr == Z_VERSION_ERROR) {
-      ALOGE("Installed zlib is not compatible with linked version (%s)",
-        ZLIB_VERSION);
+      ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
     } else {
       ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
     }
@@ -193,13 +192,14 @@
 
 bool ZipArchiveStreamEntryCompressed::Verify() {
   return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
-      crc32_ == computed_crc32_;
+         crc32_ == computed_crc32_;
 }
 
 const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
   if (z_stream_.avail_out == 0) {
     z_stream_.next_out = out_.data();
-    z_stream_.avail_out = out_.size();;
+    z_stream_.avail_out = out_.size();
+    ;
   }
 
   while (true) {
@@ -226,9 +226,8 @@
 
     int zerr = inflate(&z_stream_, Z_NO_FLUSH);
     if (zerr != Z_OK && zerr != Z_STREAM_END) {
-      ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
-          zerr, z_stream_.next_in, z_stream_.avail_in,
-          z_stream_.next_out, z_stream_.avail_out);
+      ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, z_stream_.next_in,
+            z_stream_.avail_in, z_stream_.next_out, z_stream_.avail_out);
       return nullptr;
     }
 
@@ -276,8 +275,8 @@
   return length_ == 0;
 }
 
-ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(
-    ZipArchiveHandle handle, const ZipEntry& entry) {
+ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(ZipArchiveHandle handle,
+                                                     const ZipEntry& entry) {
   ZipArchiveStreamEntry* stream = nullptr;
   if (entry.method != kCompressStored) {
     stream = new ZipArchiveStreamEntryCompressed(handle);
@@ -292,8 +291,8 @@
   return stream;
 }
 
-ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(
-    ZipArchiveHandle handle, const ZipEntry& entry) {
+ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(ZipArchiveHandle handle,
+                                                        const ZipEntry& entry) {
   ZipArchiveStreamEntry* stream = nullptr;
   if (entry.method == kCompressStored) {
     // Not compressed, don't need to do anything special.
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 9dd6cc0..dbc14f0 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "zip_archive_private.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
@@ -38,23 +40,17 @@
 static const std::string kValidZip = "valid.zip";
 static const std::string kLargeZip = "large.zip";
 static const std::string kBadCrcZip = "bad_crc.zip";
+static const std::string kCrashApk = "crash.apk";
+static const std::string kBadFilenameZip = "bad_filename.zip";
 static const std::string kUpdateZip = "dummy-update.zip";
 
-static const std::vector<uint8_t> kATxtContents {
-  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
-  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
-  '\n'
-};
+static const std::vector<uint8_t> kATxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a',
+                                                'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
 
-static const std::vector<uint8_t> kATxtContentsCompressed {
-  'K', 'L', 'J', 'N', 'I', 'M', 'K', 207, 'H',
-  132, 210, '\\', '\0'
-};
+static const std::vector<uint8_t> kATxtContentsCompressed{'K', 'L', 'J', 'N', 'I',  'M', 'K',
+                                                          207, 'H', 132, 210, '\\', '\0'};
 
-static const std::vector<uint8_t> kBTxtContents {
-  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
-  '\n'
-};
+static const std::vector<uint8_t> kBTxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
 
 static const std::string kATxtName("a.txt");
 static const std::string kBTxtName("b.txt");
@@ -63,14 +59,12 @@
 static const std::string kLargeCompressTxtName("compress.txt");
 static const std::string kLargeUncompressTxtName("uncompress.txt");
 
-static int32_t OpenArchiveWrapper(const std::string& name,
-                                  ZipArchiveHandle* handle) {
+static int32_t OpenArchiveWrapper(const std::string& name, ZipArchiveHandle* handle) {
   const std::string abs_path = test_data_dir + "/" + name;
   return OpenArchive(abs_path.c_str(), handle);
 }
 
-static void AssertNameEquals(const std::string& name_str,
-                             const ZipString& name) {
+static void AssertNameEquals(const std::string& name_str, const ZipString& name) {
   ASSERT_EQ(name_str.size(), name.name_length);
   ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
 }
@@ -83,7 +77,15 @@
 TEST(ziparchive, Open) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+  CloseArchive(handle);
 
+  ASSERT_EQ(-1, OpenArchiveWrapper(kBadFilenameZip, &handle));
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, OutOfBound) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(-8, OpenArchiveWrapper(kCrashApk, &handle));
   CloseArchive(handle);
 }
 
@@ -333,47 +335,36 @@
 }
 
 static const uint32_t kEmptyEntriesZip[] = {
-      0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000,
-      0x00090000, 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13,
-      0x52e25c24, 0x000b7875, 0x42890401, 0x88040000, 0x50000013, 0x1e02014b,
-      0x00000a03, 0x60000000, 0x00443863, 0x00000000, 0x00000000, 0x09000000,
-      0x00001800, 0x00000000, 0xa0000000, 0x00000081, 0x706d6500, 0x742e7974,
-      0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400,
-      0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 };
+    0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 0x00090000,
+    0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 0x52e25c24, 0x000b7875,
+    0x42890401, 0x88040000, 0x50000013, 0x1e02014b, 0x00000a03, 0x60000000, 0x00443863,
+    0x00000000, 0x00000000, 0x09000000, 0x00001800, 0x00000000, 0xa0000000, 0x00000081,
+    0x706d6500, 0x742e7974, 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904,
+    0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000};
 
 // This is a zip file containing a single entry (ab.txt) that contains
 // 90072 repetitions of the string "ab\n" and has an uncompressed length
 // of 270216 bytes.
 static const uint16_t kAbZip[] = {
-  0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0,
-  0x2cda, 0x011b, 0x0000, 0x1f88, 0x0004, 0x0006, 0x001c, 0x6261,
-  0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
-  0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000,
-  0xc2ed, 0x0d31, 0x0000, 0x030c, 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa,
-  0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02,
-  0x1403, 0x0000, 0x0800, 0xd200, 0x9851, 0xb046, 0xdac4, 0x1b2c,
-  0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
-  0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574,
-  0x0554, 0x0300, 0x097c, 0x553a, 0x7875, 0x000b, 0x0401, 0x4289,
-  0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
-  0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000
-};
+    0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, 0x2cda, 0x011b, 0x0000, 0x1f88,
+    0x0004, 0x0006, 0x001c, 0x6261, 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
+    0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, 0xc2ed, 0x0d31, 0x0000, 0x030c,
+    0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, 0x1403, 0x0000, 0x0800, 0xd200,
+    0x9851, 0xb046, 0xdac4, 0x1b2c, 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
+    0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, 0x0554, 0x0300, 0x097c, 0x553a,
+    0x7875, 0x000b, 0x0401, 0x4289, 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
+    0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000};
 
 static const std::string kAbTxtName("ab.txt");
 static const size_t kAbUncompressedSize = 270216;
@@ -394,7 +385,6 @@
   uint8_t buffer[1];
   ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
 
-
   TemporaryFile tmp_output_file;
   ASSERT_NE(-1, tmp_output_file.fd);
   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
@@ -408,7 +398,7 @@
   TemporaryFile tmp_file;
   ASSERT_NE(-1, tmp_file.fd);
   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
-                         sizeof(kAbZip) - 1));
+                                        sizeof(kAbZip) - 1));
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle));
 
@@ -436,8 +426,7 @@
   // the same as the memory buffer we extracted directly to.
   std::vector<uint8_t> file_contents(kAbUncompressedSize);
   ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET));
-  ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0],
-                                       file_contents.size()));
+  ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
   ASSERT_EQ(file_contents, buffer);
 
   for (int i = 0; i < 90072; ++i) {
@@ -453,7 +442,7 @@
   ASSERT_NE(-1, tmp_file.fd);
 
   // Create a file with 8 bytes of random garbage.
-  static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' };
+  static const uint8_t trailer[] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'z'};
   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
 
@@ -464,7 +453,7 @@
 TEST(ziparchive, ExtractToFile) {
   TemporaryFile tmp_file;
   ASSERT_NE(-1, tmp_file.fd);
-  const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
+  const uint8_t data[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
   const size_t data_size = sizeof(data);
 
   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
@@ -478,7 +467,6 @@
   ASSERT_EQ(0, FindEntry(handle, name, &entry));
   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
 
-
   // Assert that the first 8 bytes of the file haven't been clobbered.
   uint8_t read_buffer[data_size];
   ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET));
@@ -487,10 +475,9 @@
 
   // Assert that the remainder of the file contains the incompressed data.
   std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
-  ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
-                                       entry.uncompressed_length));
-  ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(),
-                      kATxtContents.size()));
+  ASSERT_TRUE(
+      android::base::ReadFully(tmp_file.fd, uncompressed_data.data(), entry.uncompressed_length));
+  ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size()));
 
   // Assert that the total length of the file is sane
   ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
@@ -507,7 +494,7 @@
 
   // Memory map the file first and open the archive from the memory region.
   android::FileMap file_map;
-  file_map.create(zip_path.c_str(), fd, 0/*offset*/, sb.st_size, true);
+  file_map.create(zip_path.c_str(), fd, 0 /*offset*/, sb.st_size, true);
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveFromMemory(file_map.getDataPtr(), file_map.getDataLength(),
                                      zip_path.c_str(), &handle));
@@ -523,9 +510,8 @@
 }
 #endif
 
-static void ZipArchiveStreamTest(
-    ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
-    bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
+static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
+                                 bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
   ZipString name;
   SetZipString(&name, entry_name);
   ASSERT_EQ(0, FindEntry(handle, name, entry));
@@ -554,9 +540,9 @@
   ASSERT_EQ(total_size, read_data->size());
 }
 
-static void ZipArchiveStreamTestUsingContents(
-    const std::string& zip_file, const std::string& entry_name,
-    const std::vector<uint8_t>& contents, bool raw) {
+static void ZipArchiveStreamTestUsingContents(const std::string& zip_file,
+                                              const std::string& entry_name,
+                                              const std::vector<uint8_t>& contents, bool raw) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
 
@@ -570,7 +556,8 @@
   CloseArchive(handle);
 }
 
-static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file, const std::string& entry_name) {
+static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file,
+                                            const std::string& entry_name) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
 
@@ -632,13 +619,109 @@
   CloseArchive(handle);
 }
 
+// Generated using the following Java program:
+//     public static void main(String[] foo) throws Exception {
+//       FileOutputStream fos = new
+//       FileOutputStream("/tmp/data_descriptor.zip");
+//       ZipOutputStream zos = new ZipOutputStream(fos);
+//       ZipEntry ze = new ZipEntry("name");
+//       ze.setMethod(ZipEntry.DEFLATED);
+//       zos.putNextEntry(ze);
+//       zos.write("abdcdefghijk".getBytes());
+//       zos.closeEntry();
+//       zos.close();
+//     }
+//
+// cat /tmp/data_descriptor.zip | xxd -i
+//
+static const std::vector<uint8_t> kDataDescriptorZipFile{
+    0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61,
+    0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00,
+    //[sig---------------], [crc32---------------], [csize---------------], [size----------------]
+    0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+    0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a,
+    0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61,
+    0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00,
+    0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+// The offsets of the data descriptor in this file, so we can mess with
+// them later in the test.
+static constexpr uint32_t kDataDescriptorOffset = 48;
+static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8;
+static constexpr uint32_t kSizeOffset = kCSizeOffset + 4;
+
+static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data,
+                                 std::vector<uint8_t>* entry_out, int32_t* error_code_out) {
+  TemporaryFile tmp_file;
+  ASSERT_NE(-1, tmp_file.fd);
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle));
+
+  // This function expects a variant of kDataDescriptorZipFile, for look for
+  // an entry whose name is "name" and whose size is 12 (contents =
+  // "abdcdefghijk").
+  ZipEntry entry;
+  ZipString empty_name;
+  SetZipString(&empty_name, "name");
+
+  ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
+  ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
+
+  entry_out->resize(12);
+  (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, ValidDataDescriptors) {
+  std::vector<uint8_t> entry;
+  int32_t error_code = 0;
+  ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
+
+  ASSERT_EQ(0, error_code);
+  ASSERT_EQ(12u, entry.size());
+  ASSERT_EQ('a', entry[0]);
+  ASSERT_EQ('k', entry[11]);
+}
+
+TEST(ziparchive, InvalidDataDescriptors) {
+  std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
+  invalid_csize[kCSizeOffset] = 0xfe;
+
+  std::vector<uint8_t> entry;
+  int32_t error_code = 0;
+  ExtractEntryToMemory(invalid_csize, &entry, &error_code);
+
+  ASSERT_EQ(kInconsistentInformation, error_code);
+
+  std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
+  invalid_csize[kSizeOffset] = 0xfe;
+
+  error_code = 0;
+  entry.clear();
+  ExtractEntryToMemory(invalid_csize, &entry, &error_code);
+
+  ASSERT_EQ(kInconsistentInformation, error_code);
+}
+
+TEST(ziparchive, ErrorCodeString) {
+  ASSERT_STREQ("Success", ErrorCodeString(0));
+
+  // Out of bounds.
+  ASSERT_STREQ("Unknown return code", ErrorCodeString(1));
+  ASSERT_STREQ("Unknown return code", ErrorCodeString(-13));
+
+  ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
+}
+
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
 
-  static struct option options[] = {
-    { "test_data_dir", required_argument, nullptr, 't' },
-    { nullptr, 0, nullptr, 0 }
-  };
+  static struct option options[] = {{"test_data_dir", required_argument, nullptr, 't'},
+                                    {nullptr, 0, nullptr, 0}};
 
   while (true) {
     int option_index;
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 6d28bdb..6ad3366 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -16,11 +16,11 @@
 
 #include "ziparchive/zip_writer.h"
 
-#include <cstdio>
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <zlib.h>
-#define DEF_MEM_LEVEL 8                // normally in zutil.h?
+#include <cstdio>
+#define DEF_MEM_LEVEL 8  // normally in zutil.h?
 
 #include <memory>
 #include <vector>
@@ -33,13 +33,13 @@
 #include "zip_archive_common.h"
 
 #if !defined(powerof2)
-#define powerof2(x) ((((x)-1)&(x))==0)
+#define powerof2(x) ((((x)-1) & (x)) == 0)
 #endif
 
 /* Zip compression methods we support */
 enum {
-  kCompressStored     = 0,        // no compression
-  kCompressDeflated   = 8,        // standard deflate
+  kCompressStored = 0,    // no compression
+  kCompressDeflated = 8,  // standard deflate
 };
 
 // Size of the output buffer used for compression.
@@ -67,10 +67,7 @@
 static const int32_t kInvalidAlignment = -6;
 
 static const char* sErrorCodes[] = {
-    "Invalid state",
-    "IO error",
-    "Invalid entry name",
-    "Zlib error",
+    "Invalid state", "IO error", "Invalid entry name", "Zlib error",
 };
 
 const char* ZipWriter::ErrorCodeString(int32_t error_code) {
@@ -85,9 +82,13 @@
   delete stream;
 }
 
-ZipWriter::ZipWriter(FILE* f) : file_(f), seekable_(false), current_offset_(0),
-                                state_(State::kWritingZip), z_stream_(nullptr, DeleteZStream),
-                                buffer_(kBufSize) {
+ZipWriter::ZipWriter(FILE* f)
+    : file_(f),
+      seekable_(false),
+      current_offset_(0),
+      state_(State::kWritingZip),
+      z_stream_(nullptr, DeleteZStream),
+      buffer_(kBufSize) {
   // Check if the file is seekable (regular file). If fstat fails, that's fine, subsequent calls
   // will fail as well.
   struct stat file_stats;
@@ -96,13 +97,14 @@
   }
 }
 
-ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
-                                           seekable_(writer.seekable_),
-                                           current_offset_(writer.current_offset_),
-                                           state_(writer.state_),
-                                           files_(std::move(writer.files_)),
-                                           z_stream_(std::move(writer.z_stream_)),
-                                           buffer_(std::move(writer.buffer_)){
+ZipWriter::ZipWriter(ZipWriter&& writer)
+    : file_(writer.file_),
+      seekable_(writer.seekable_),
+      current_offset_(writer.current_offset_),
+      state_(writer.state_),
+      files_(std::move(writer.files_)),
+      z_stream_(std::move(writer.z_stream_)),
+      buffer_(std::move(writer.buffer_)) {
   writer.file_ = nullptr;
   writer.state_ = State::kError;
 }
@@ -154,10 +156,10 @@
 
   struct tm* ptm;
 #if !defined(_WIN32)
-    struct tm tm_result;
-    ptm = localtime_r(&when, &tm_result);
+  struct tm tm_result;
+  ptm = localtime_r(&when, &tm_result);
 #else
-    ptm = localtime(&when);
+  ptm = localtime(&when);
 #endif
 
   int year = ptm->tm_year;
@@ -193,8 +195,8 @@
   dst->extra_field_length = src.padding_length;
 }
 
-int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags,
-                                             time_t time, uint32_t alignment) {
+int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags, time_t time,
+                                             uint32_t alignment) {
   if (state_ != State::kWritingZip) {
     return kInvalidState;
   }
@@ -252,9 +254,8 @@
     return HandleError(kIoError);
   }
 
-  if (file_entry.padding_length != 0 &&
-      fwrite(zero_padding.data(), 1, file_entry.padding_length, file_)
-      != file_entry.padding_length) {
+  if (file_entry.padding_length != 0 && fwrite(zero_padding.data(), 1, file_entry.padding_length,
+                                               file_) != file_entry.padding_length) {
     return HandleError(kIoError);
   }
 
@@ -292,7 +293,7 @@
   CHECK(state_ == State::kWritingZip);
 
   // Initialize the z_stream for compression.
-  z_stream_ = std::unique_ptr<z_stream, void(*)(z_stream*)>(new z_stream(), DeleteZStream);
+  z_stream_ = std::unique_ptr<z_stream, void (*)(z_stream*)>(new z_stream(), DeleteZStream);
 
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wold-style-cast"
@@ -331,8 +332,8 @@
     return result;
   }
 
-  current_file_entry_.crc32 = crc32(current_file_entry_.crc32,
-                                    reinterpret_cast<const Bytef*>(data), len);
+  current_file_entry_.crc32 =
+      crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len);
   current_file_entry_.uncompressed_size += len;
   return kNoError;
 }
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 5b526a4..c284273 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include "ziparchive/zip_archive.h"
 #include "ziparchive/zip_writer.h"
+#include "ziparchive/zip_archive.h"
 
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
@@ -135,17 +135,6 @@
   CloseArchive(handle);
 }
 
-static void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) {
-  memset(tm, 0, sizeof(struct tm));
-  tm->tm_hour = (zip_time >> 11) & 0x1f;
-  tm->tm_min = (zip_time >> 5) & 0x3f;
-  tm->tm_sec = (zip_time & 0x1f) << 1;
-
-  tm->tm_year = ((zip_time >> 25) & 0x7f) + 80;
-  tm->tm_mon = ((zip_time >> 21) & 0xf) - 1;
-  tm->tm_mday = (zip_time >> 16) & 0x1f;
-}
-
 static struct tm MakeTm() {
   struct tm tm;
   memset(&tm, 0, sizeof(struct tm));
@@ -177,8 +166,7 @@
   ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
   EXPECT_EQ(0, data.offset & 0x03);
 
-  struct tm mod;
-  ConvertZipTimeToTm(data.mod_time, &mod);
+  struct tm mod = data.GetModificationTime();
   EXPECT_EQ(tm.tm_sec, mod.tm_sec);
   EXPECT_EQ(tm.tm_min, mod.tm_min);
   EXPECT_EQ(tm.tm_hour, mod.tm_hour);
@@ -228,8 +216,7 @@
   ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
   EXPECT_EQ(0, data.offset & 0xfff);
 
-  struct tm mod;
-  ConvertZipTimeToTm(data.mod_time, &mod);
+  struct tm mod = data.GetModificationTime();
   EXPECT_EQ(tm.tm_sec, mod.tm_sec);
   EXPECT_EQ(tm.tm_min, mod.tm_min);
   EXPECT_EQ(tm.tm_hour, mod.tm_hour);
@@ -374,7 +361,7 @@
 
   ASSERT_EQ(0, writer.StartEntry("large.txt", 0));
   std::vector<uint8_t> data;
-  data.resize(1024*1024, 0xef);
+  data.resize(1024 * 1024, 0xef);
   ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size()));
   ASSERT_EQ(0, writer.FinishEntry());
 
@@ -395,8 +382,9 @@
                                                             ZipArchiveHandle handle,
                                                             ZipEntry* zip_entry) {
   if (expected.size() != zip_entry->uncompressed_length) {
-    return ::testing::AssertionFailure() << "uncompressed entry size "
-        << zip_entry->uncompressed_length << " does not match expected size " << expected.size();
+    return ::testing::AssertionFailure()
+           << "uncompressed entry size " << zip_entry->uncompressed_length
+           << " does not match expected size " << expected.size();
   }
 
   std::string actual;
@@ -409,7 +397,7 @@
 
   if (expected != actual) {
     return ::testing::AssertionFailure() << "actual zip_entry data '" << actual
-        << "' does not match expected '" << expected << "'";
+                                         << "' does not match expected '" << expected << "'";
   }
   return ::testing::AssertionSuccess();
 }
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index e9ef9cc..f64196f 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -1638,7 +1638,7 @@
                 logcat_panic(context, HELP_FALSE, "read: unexpected length.\n");
                 break;
             }
-            logcat_panic(context, HELP_FALSE, "logcat read failure");
+            logcat_panic(context, HELP_FALSE, "logcat read failure\n");
             break;
         }
 
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index 06cc90d..07040b0 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -61,3 +61,4 @@
     user logd
     group log
     writepid /dev/cpuset/system-background/tasks
+    oom_score_adjust -600
diff --git a/logd/Android.bp b/logd/Android.bp
new file mode 100644
index 0000000..68b79d3
--- /dev/null
+++ b/logd/Android.bp
@@ -0,0 +1,78 @@
+// Copyright (C) 2017 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.
+
+// This is what we want to do:
+//  event_logtags = $(shell
+//    sed -n
+//        "s/^\([0-9]*\)[ \t]*$1[ \t].*/-D`echo $1 | tr a-z A-Z`_LOG_TAG=\1/p"
+//        $(LOCAL_PATH)/$2/event.logtags)
+//  event_flag := $(call event_logtags,auditd)
+//  event_flag += $(call event_logtags,logd)
+//  event_flag += $(call event_logtags,tag_def)
+// so make sure we do not regret hard-coding it as follows:
+event_flag = [
+    "-DAUDITD_LOG_TAG=1003",
+    "-DCHATTY_LOG_TAG=1004",
+    "-DTAG_DEF_LOG_TAG=1005",
+    "-DLIBLOG_LOG_TAG=1006"
+]
+
+cc_library_static {
+    name: "liblogd",
+
+    srcs: [
+        "LogCommand.cpp",
+        "CommandListener.cpp",
+        "LogListener.cpp",
+        "LogReader.cpp",
+        "FlushCommand.cpp",
+        "LogBuffer.cpp",
+        "LogBufferElement.cpp",
+        "LogBufferInterface.cpp",
+        "LogTimes.cpp",
+        "LogStatistics.cpp",
+        "LogWhiteBlackList.cpp",
+        "libaudit.c",
+        "LogAudit.cpp",
+        "LogKlog.cpp",
+        "LogTags.cpp",
+    ],
+    logtags: ["event.logtags"],
+
+    shared_libs: ["libbase"],
+
+    export_include_dirs: ["."],
+
+    cflags: ["-Werror"] + event_flag,
+}
+
+cc_binary {
+    name: "logd",
+    init_rc: ["logd.rc"],
+
+    srcs: ["main.cpp"],
+
+    static_libs: ["liblogd"],
+
+    shared_libs: [
+        "libsysutils",
+        "liblog",
+        "libcutils",
+        "libbase",
+        "libpackagelistparser",
+        "libcap",
+    ],
+
+    cflags: ["-Werror"],
+}
diff --git a/logd/Android.mk b/logd/Android.mk
index fb51992..1bca891 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -2,73 +2,6 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE:= liblogd
-
-LOCAL_SRC_FILES := \
-    LogCommand.cpp \
-    CommandListener.cpp \
-    LogListener.cpp \
-    LogReader.cpp \
-    FlushCommand.cpp \
-    LogBuffer.cpp \
-    LogBufferElement.cpp \
-    LogBufferInterface.cpp \
-    LogTimes.cpp \
-    LogStatistics.cpp \
-    LogWhiteBlackList.cpp \
-    libaudit.c \
-    LogAudit.cpp \
-    LogKlog.cpp \
-    LogTags.cpp \
-    event.logtags
-
-LOCAL_SHARED_LIBRARIES := \
-    libbase
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-
-# This is what we want to do:
-#  event_logtags = $(shell \
-#    sed -n \
-#        "s/^\([0-9]*\)[ \t]*$1[ \t].*/-D`echo $1 | tr a-z A-Z`_LOG_TAG=\1/p" \
-#        $(LOCAL_PATH)/$2/event.logtags)
-#  event_flag := $(call event_logtags,auditd)
-#  event_flag += $(call event_logtags,logd)
-#  event_flag += $(call event_logtags,tag_def)
-# so make sure we do not regret hard-coding it as follows:
-event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004 -DTAG_DEF_LOG_TAG=1005
-event_flag += -DLIBLOG_LOG_TAG=1006
-
-LOCAL_CFLAGS := -Werror $(event_flag)
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= logd
-
-LOCAL_INIT_RC := logd.rc
-
-LOCAL_SRC_FILES := \
-    main.cpp
-
-LOCAL_STATIC_LIBRARIES := \
-    liblogd
-
-LOCAL_SHARED_LIBRARIES := \
-    libsysutils \
-    liblog \
-    libcutils \
-    libbase \
-    libpackagelistparser \
-    libcap
-
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
 LOCAL_MODULE := logtagd.rc
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_CLASS := ETC
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index cd80212..4397b14 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -933,8 +933,12 @@
 }
 
 #ifdef __ANDROID__
-static inline int32_t get4LE(const char* src) {
-    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+static inline uint32_t get4LE(const uint8_t* src) {
+  return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+static inline uint32_t get4LE(const char* src) {
+  return get4LE(reinterpret_cast<const uint8_t*>(src));
 }
 #endif
 
diff --git a/platform_tools_tool_version.mk b/platform_tools_tool_version.mk
index 73229e6..eed2ab5 100644
--- a/platform_tools_tool_version.mk
+++ b/platform_tools_tool_version.mk
@@ -17,6 +17,6 @@
 # literal instead. Using 0 lets us easily distinguish non-canonical builds.
 platform_tools_version := $(shell sed \
     's/$${PLATFORM_SDK_VERSION}/0/ ; s/^Pkg.Revision=\(.*\)/\1/p ; d' \
-    $(ANDROID_BUILD_TOP)/development/sdk/plat_tools_source.prop_template \
+    development/sdk/plat_tools_source.prop_template \
   )
 tool_version := $(platform_tools_version)-$(BUILD_NUMBER_FROM_FILE)
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 540e976..9301743 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -7,6 +7,7 @@
 import /init.environ.rc
 import /init.usb.rc
 import /init.${ro.hardware}.rc
+import /vendor/etc/init/hw/init.${ro.hardware}.rc
 import /init.usb.configfs.rc
 import /init.${ro.zygote}.rc
 
@@ -26,6 +27,18 @@
     # Set the security context of /postinstall if present.
     restorecon /postinstall
 
+    # Mount cgroup mount point for cpu accounting
+    mount cgroup none /acct cpuacct
+    mkdir /acct/uid
+
+    # root memory control cgroup, used by lmkd
+    mkdir /dev/memcg 0700 root system
+    mount cgroup none /dev/memcg memory
+    # app mem cgroups, used by activity manager, lmkd and zygote
+    mkdir /dev/memcg/apps/ 0755 system system
+    # cgroup for system_server and surfaceflinger
+    mkdir /dev/memcg/system 0550 system system
+
     start ueventd
 
 on init
@@ -42,10 +55,6 @@
     # Link /vendor to /system/vendor for devices without a vendor partition.
     symlink /system/vendor /vendor
 
-    # Mount cgroup mount point for cpu accounting
-    mount cgroup none /acct cpuacct
-    mkdir /acct/uid
-
     # Create energy-aware scheduler tuning nodes
     mkdir /dev/stune
     mount cgroup none /dev/stune schedtune
@@ -98,12 +107,6 @@
     symlink /storage/self/primary /mnt/sdcard
     symlink /mnt/user/0/primary /mnt/runtime/default/self/primary
 
-    # root memory control cgroup, used by lmkd
-    mkdir /dev/memcg 0700 root system
-    mount cgroup none /dev/memcg memory
-    # app mem cgroups, used by activity manager, lmkd and zygote
-    mkdir /dev/memcg/apps/ 0755 system system
-
     write /proc/sys/kernel/panic_on_oops 1
     write /proc/sys/kernel/hung_task_timeout_secs 0
     write /proc/cpu/alignment 4
@@ -220,6 +223,8 @@
     mount pstore pstore /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
+    chmod 0440 /sys/fs/pstore/console-ramoops-0
     chown system log /sys/fs/pstore/pmsg-ramoops-0
     chmod 0440 /sys/fs/pstore/pmsg-ramoops-0
 
@@ -371,6 +376,20 @@
 
     # create basic filesystem structure
     mkdir /data/misc 01771 system misc
+    mkdir /data/misc/recovery 0770 system log
+    copy /data/misc/recovery/default.prop /data/misc/recovery/default.prop.1
+    chmod 0440 /data/misc/recovery/default.prop.1
+    chown system log /data/misc/recovery/default.prop.1
+    copy /default.prop /data/misc/recovery/default.prop
+    chmod 0440 /data/misc/recovery/default.prop
+    chown system log /data/misc/recovery/default.prop
+    mkdir /data/misc/recovery/proc 0770 system log
+    copy /data/misc/recovery/proc/version /data/misc/recovery/proc/version.1
+    chmod 0440 /data/misc/recovery/proc/version.1
+    chown system log /data/misc/recovery/proc/version.1
+    copy /proc/version /data/misc/recovery/proc/version
+    chmod 0440 /data/misc/recovery/proc/version
+    chown system log /data/misc/recovery/proc/version
     mkdir /data/misc/bluedroid 02770 bluetooth bluetooth
     # Fix the access permissions and group ownership for 'bt_config.conf'
     chmod 0660 /data/misc/bluedroid/bt_config.conf
@@ -676,12 +695,17 @@
 on property:security.perf_harden=1
     write /proc/sys/kernel/perf_event_paranoid 3
 
+# on shutdown
+# In device's init.rc, this trigger can be used to do device-specific actions
+# before shutdown. e.g disable watchdog and mask error handling
+
 ## Daemon processes to be run by init.
 ##
 service ueventd /sbin/ueventd
     class core
     critical
     seclabel u:r:ueventd:s0
+    shutdown critical
 
 service healthd /sbin/healthd
     class core
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 4f4fc5d..9620d63 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -3,7 +3,7 @@
     required: [
         "bzip2",
         "grep",
-        "gzip",
+        "grep_vendor",
         "mkshrc",
         "mkshrc_vendor",
         "reboot",
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 1c9fb20..8db8327 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -28,8 +28,8 @@
 }
 
 // We build BSD grep separately, so it can provide egrep and fgrep too.
-cc_binary {
-    name: "grep",
+cc_defaults {
+    name: "grep_common",
     srcs: [
         "upstream-netbsd/usr.bin/grep/fastgrep.c",
         "upstream-netbsd/usr.bin/grep/file.c",
@@ -40,5 +40,19 @@
     cflags: common_cflags,
     local_include_dirs: ["upstream-netbsd/include/"],
     symlinks: ["egrep", "fgrep"],
+}
 
+cc_binary {
+    name: "grep",
+    defaults: ["grep_common"],
+}
+
+// Build vendor grep.
+// TODO: Add vendor_available to "grep" module and remove "grep_vendor" module
+//       when vendor_available is fully supported.
+cc_binary {
+    name: "grep_vendor",
+    stem: "grep",
+    vendor: true,
+    defaults: ["grep_common"],
 }
diff --git a/toolbox/upstream-netbsd/include/sys/mtio.h b/toolbox/upstream-netbsd/include/sys/mtio.h
deleted file mode 100644
index 8fb5655..0000000
--- a/toolbox/upstream-netbsd/include/sys/mtio.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <linux/mtio.h>
diff --git a/tzdatacheck/Android.bp b/tzdatacheck/Android.bp
deleted file mode 100644
index 00ad141..0000000
--- a/tzdatacheck/Android.bp
+++ /dev/null
@@ -1,14 +0,0 @@
-// ========================================================
-// Executable
-// ========================================================
-cc_binary {
-    name: "tzdatacheck",
-    host_supported: true,
-    srcs: ["tzdatacheck.cpp"],
-    shared_libs: [
-        "libbase",
-        "libcutils",
-        "liblog",
-    ],
-    cflags: ["-Werror"],
-}
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
deleted file mode 100644
index 8fcd17f..0000000
--- a/tzdatacheck/tzdatacheck.cpp
+++ /dev/null
@@ -1,601 +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 <ctype.h>
-#include <errno.h>
-#include <ftw.h>
-#include <libgen.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <iostream>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "android-base/logging.h"
-
-// The name of the directory that holds a staged time zone update distro. If this exists it should
-// replace the one in CURRENT_DIR_NAME.
-// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
-static const char* STAGED_DIR_NAME = "/staged";
-
-// The name of the directory that holds the (optional) installed time zone update distro.
-// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
-static const char* CURRENT_DIR_NAME = "/current";
-
-// The name of a file in the staged dir that indicates the staged operation is an "uninstall".
-// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
-static const char* UNINSTALL_TOMBSTONE_FILE_NAME = "/STAGED_UNINSTALL_TOMBSTONE";
-
-// The name of the file containing the distro version information.
-// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
-static const char* DISTRO_VERSION_FILENAME = "/distro_version";
-
-// distro_version is an ASCII file consisting of 17 bytes in the form: AAA.BBB|CCCCC|DDD
-// AAA.BBB is the major/minor version of the distro format (e.g. 001.001),
-// CCCCC is the rules version (e.g. 2016g)
-// DDD is the android revision for this rules version to allow for distro corrections (e.g. 001)
-// We only need the first 13 to determine if it is suitable for the device.
-static const int DISTRO_VERSION_LENGTH = 13;
-
-// The major version of the distro format supported by this code as a null-terminated char[].
-// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
-static const char SUPPORTED_DISTRO_MAJOR_VERSION[] = "001";
-
-// The length of the distro format major version excluding the \0
-static const size_t SUPPORTED_DISTRO_MAJOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MAJOR_VERSION) - 1;
-
-// The minor version of the distro format supported by this code as a null-terminated char[].
-// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
-static const char SUPPORTED_DISTRO_MINOR_VERSION[] = "001";
-
-// The length of the distro format minor version excluding the \0
-static const size_t SUPPORTED_DISTRO_MINOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MINOR_VERSION) - 1;
-
-// The length of the distro format version. e.g. 001.001
-static const size_t SUPPORTED_DISTRO_VERSION_LEN =
-        SUPPORTED_DISTRO_MAJOR_VERSION_LEN + SUPPORTED_DISTRO_MINOR_VERSION_LEN + 1;
-
-// The length of the IANA rules version bytes. e.g. 2016a
-static const size_t RULES_VERSION_LEN = 5;
-
-// Distro version bytes are: AAA.BBB|CCCCC - the rules version is CCCCC
-static const size_t DISTRO_VERSION_RULES_IDX = 8;
-
-// See also libcore.tzdata.shared2.TimeZoneDistro.
-static const char* TZDATA_FILENAME = "/tzdata";
-
-// tzdata file header (as much as we need for the version):
-// byte[11] tzdata_version  -- e.g. "tzdata2012f"
-static const int TZ_HEADER_LENGTH = 11;
-
-static const char TZ_DATA_HEADER_PREFIX[] = "tzdata";
-static const size_t TZ_DATA_HEADER_PREFIX_LEN = sizeof(TZ_DATA_HEADER_PREFIX) - 1; // exclude \0
-
-static void usage() {
-    std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
-            "\n"
-            "Checks whether any timezone update distro in DATA_TZ_DIR is compatible with the\n"
-            "current Android release and better than or the same as base system timezone rules in\n"
-            "SYSTEM_TZ_DIR. If the timezone rules in SYSTEM_TZ_DIR are a higher version than the\n"
-            "one in DATA_TZ_DIR the DATA_TZ_DIR is renamed and then deleted.\n";
-    exit(1);
-}
-
-/*
- * Opens a file and fills buffer with the first byteCount bytes from the file.
- * If the file does not exist or cannot be opened or is too short then false is returned.
- * If the bytes were read successfully then true is returned.
- */
-static bool readBytes(const std::string& fileName, char* buffer, size_t byteCount) {
-    FILE* file = fopen(fileName.c_str(), "r");
-    if (file == nullptr) {
-        if (errno != ENOENT) {
-            PLOG(WARNING) << "Error opening file " << fileName;
-        }
-        return false;
-    }
-    size_t bytesRead = fread(buffer, 1, byteCount, file);
-    fclose(file);
-    if (bytesRead != byteCount) {
-        LOG(WARNING) << fileName << " is too small. " << byteCount << " bytes required";
-        return false;
-    }
-    return true;
-}
-
-/*
- * Checks the contents of headerBytes. Returns true if it is valid (starts with "tzdata"), false
- * otherwise.
- */
-static bool checkValidTzDataHeader(const std::string& fileName, const char* headerBytes) {
-    if (strncmp("tzdata", headerBytes, 6) != 0) {
-        LOG(WARNING) << fileName << " does not start with the expected bytes (tzdata)";
-        return false;
-    }
-    return true;
-}
-
-static bool checkDigits(const char* buffer, const size_t count, size_t* i) {
-    for (size_t j = 0; j < count; j++) {
-      char toCheck = buffer[(*i)++];
-      if (!isdigit(toCheck)) {
-        return false;
-      }
-    }
-    return true;
-}
-
-static bool checkValidDistroVersion(const char* buffer) {
-    // See DISTRO_VERSION_LENGTH comments above for a description of the format.
-    size_t i = 0;
-    if (!checkDigits(buffer, 3, &i)) {
-      return false;
-    }
-    if (buffer[i++] != '.') {
-      return false;
-    }
-    if (!checkDigits(buffer, 3, &i)) {
-      return false;
-    }
-    if (buffer[i++] != '|') {
-      return false;
-    }
-    if (!checkDigits(buffer, 4, &i)) {
-      return false;
-    }
-    // Ignore the last character. It is assumed to be a letter but we don't check because it's not
-    // obvious what would happen at 'z'.
-    return true;
-}
-
-/* Return the parent directory of dirName. */
-static std::string getParentDir(const std::string& dirName) {
-    std::unique_ptr<char> mutable_dirname(strdup(dirName.c_str()));
-    return dirname(mutable_dirname.get());
-}
-
-/* Deletes a single file, symlink or directory. Called from nftw(). */
-static int deleteFn(const char* fpath, const struct stat*, int typeflag, struct FTW*) {
-    LOG(DEBUG) << "Inspecting " << fpath;
-    switch (typeflag) {
-    case FTW_F:
-    case FTW_SL:
-        LOG(DEBUG) << "Unlinking " << fpath;
-        if (unlink(fpath)) {
-            PLOG(WARNING) << "Failed to unlink file/symlink " << fpath;
-        }
-        break;
-    case FTW_D:
-    case FTW_DP:
-        LOG(DEBUG) << "Removing dir " << fpath;
-        if (rmdir(fpath)) {
-            PLOG(WARNING) << "Failed to remove dir " << fpath;
-        }
-        break;
-    default:
-        LOG(WARNING) << "Unsupported file type " << fpath << ": " << typeflag;
-        break;
-    }
-    return 0;
-}
-
-enum PathStatus { ERR, NONE, IS_DIR, IS_REG, UNKNOWN };
-
-static PathStatus checkPath(const std::string& path) {
-    struct stat buf;
-    if (stat(path.c_str(), &buf) != 0) {
-        if (errno != ENOENT) {
-            PLOG(WARNING) << "Unable to stat " << path;
-            return ERR;
-        }
-        return NONE;
-    }
-    return S_ISDIR(buf.st_mode) ? IS_DIR : S_ISREG(buf.st_mode) ? IS_REG : UNKNOWN;
-}
-
-/*
- * Deletes fileToDelete and returns true if it is successful. If fileToDelete is not a file or
- * cannot be accessed this method returns false.
- */
-static bool deleteFile(const std::string& fileToDelete) {
-    // Check whether the file exists.
-    PathStatus pathStatus = checkPath(fileToDelete);
-    if (pathStatus == NONE) {
-        LOG(INFO) << "Path " << fileToDelete << " does not exist";
-        return true;
-    }
-    if (pathStatus != IS_REG) {
-        LOG(WARNING) << "Path " << fileToDelete << " failed to stat() or is not a file.";
-        return false;
-    }
-
-    // Attempt the deletion.
-    int rc = unlink(fileToDelete.c_str());
-    if (rc != 0) {
-        PLOG(WARNING) << "unlink() failed for " << fileToDelete;
-    }
-    return rc == 0;
-}
-
-/*
- * Deletes dirToDelete and returns true if it is successful in removing or moving the directory out
- * of the way. If dirToDelete does not exist this function does nothing and returns true. If
- * dirToDelete is not a directory or cannot be accessed this method returns false.
- *
- * During deletion, this function first renames the directory to a temporary name. If the temporary
- * directory cannot be created, or the directory cannot be renamed, false is returned. After the
- * rename, deletion of files and subdirs beneath the directory is performed on a "best effort"
- * basis. Symlinks beneath the directory are not followed.
- */
-static bool deleteDir(const std::string& dirToDelete) {
-    // Check whether the dir exists.
-    int pathStatus = checkPath(dirToDelete);
-    if (pathStatus == NONE) {
-        LOG(INFO) << "Path " << dirToDelete << " does not exist";
-        return true;
-    }
-    if (pathStatus != IS_DIR) {
-        LOG(WARNING) << "Path " << dirToDelete << " failed to stat() or is not a directory.";
-        return false;
-    }
-
-    // First, rename dirToDelete.
-
-    std::string tempDirNameTemplate = getParentDir(dirToDelete);
-    tempDirNameTemplate += "/tempXXXXXX";
-
-    // Create an empty directory with the temporary name. For this we need a non-const char*.
-    std::vector<char> tempDirName(tempDirNameTemplate.length() + 1);
-    strcpy(&tempDirName[0], tempDirNameTemplate.c_str());
-    if (mkdtemp(&tempDirName[0]) == nullptr) {
-        PLOG(WARNING) << "Unable to create a temporary directory: " << tempDirNameTemplate;
-        return false;
-    }
-
-    // Rename dirToDelete to tempDirName (replacing the empty tempDirName directory created above).
-    int rc = rename(dirToDelete.c_str(), &tempDirName[0]);
-    if (rc == -1) {
-        PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to "
-                << &tempDirName[0];
-        return false;
-    }
-
-    // Recursively delete contents of tempDirName.
-
-    rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */,
-            FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
-    if (rc == -1) {
-        LOG(INFO) << "Could not delete directory: " << &tempDirName[0];
-    }
-    return true;
-}
-
-/*
- * Deletes the ConfigInstaller metadata directory.
- * TODO(nfuller). http://b/31008728 Remove this when ConfigInstaller is no longer used.
- */
-static void deleteConfigUpdaterMetadataDir(const char* dataZoneInfoDir) {
-    // Delete the update metadata
-    std::string dataUpdatesDirName(dataZoneInfoDir);
-    dataUpdatesDirName += "/updates";
-    LOG(INFO) << "Removing: " << dataUpdatesDirName;
-    if (!deleteDir(dataUpdatesDirName)) {
-        LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
-                << " was not successful";
-    }
-}
-
-/*
- * Deletes the timezone update distro directory.
- */
-static void deleteUpdateDistroDir(const std::string& distroDirName) {
-    LOG(INFO) << "Removing: " << distroDirName;
-    if (!deleteDir(distroDirName)) {
-        LOG(WARNING) << "Deletion of distro dir " << distroDirName << " was not successful";
-    }
-}
-
-static void handleStagedUninstall(const std::string& dataStagedDirName,
-                                  const std::string& dataCurrentDirName,
-                                  const PathStatus dataCurrentDirStatus) {
-    LOG(INFO) << "Staged operation is an uninstall.";
-
-    // Delete the current install directory.
-    switch (dataCurrentDirStatus) {
-        case NONE:
-            // This is unexpected: No uninstall should be staged if there is nothing to
-            // uninstall. Carry on anyway.
-            LOG(WARNING) << "No current install to delete.";
-            break;
-        case IS_DIR:
-            // This is normal. Delete the current install dir.
-            if (!deleteDir(dataCurrentDirName)) {
-                LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
-                             << " was not successful";
-                // If this happens we don't know whether we were able to delete or not. We don't
-                // delete the staged operation so it will be retried next boot unless overridden.
-                return;
-            }
-            break;
-        case IS_REG:
-        default:
-            // This is unexpected: We can try to delete the unexpected file and carry on.
-            LOG(WARNING) << "Current distro dir " << dataCurrentDirName
-                         << " is not actually a directory. Attempting deletion.";
-            if (!deleteFile(dataCurrentDirName)) {
-                LOG(WARNING) << "Could not delete " << dataCurrentDirName;
-                return;
-            }
-            break;
-    }
-
-    // Delete the staged uninstall dir.
-    if (!deleteDir(dataStagedDirName)) {
-        LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
-                     << " was not successful";
-        // If this happens we don't know whether we were able to delete the staged operation
-        // or not.
-        return;
-    }
-    LOG(INFO) << "Staged uninstall complete.";
-}
-
-static void handleStagedInstall(const std::string& dataStagedDirName,
-                                const std::string& dataCurrentDirName,
-                                const PathStatus dataCurrentDirStatus) {
-    LOG(INFO) << "Staged operation is an install.";
-
-    switch (dataCurrentDirStatus) {
-        case NONE:
-            // This is expected: This is the first install.
-            LOG(INFO) << "No current install to replace.";
-            break;
-        case IS_DIR:
-            // This is expected: We are replacing an existing install.
-            // Delete the current dir so we can replace it.
-            if (!deleteDir(dataCurrentDirName)) {
-                LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
-                             << " was not successful";
-                // If this happens, we cannot proceed.
-                return;
-            }
-            break;
-        case IS_REG:
-        default:
-            // This is unexpected: We can try to delete the unexpected file and carry on.
-            LOG(WARNING) << "Current distro dir " << dataCurrentDirName
-                         << " is not actually a directory. Attempting deletion.";
-            if (!deleteFile(dataCurrentDirName)) {
-                LOG(WARNING) << "Could not delete " << dataCurrentDirName;
-                return;
-            }
-            break;
-    }
-
-    // Move the staged dir so it is the new current dir, completing the install.
-    LOG(INFO) << "Moving " << dataStagedDirName << " to " << dataCurrentDirName;
-    int rc = rename(dataStagedDirName.c_str(), dataCurrentDirName.c_str());
-    if (rc == -1) {
-        PLOG(WARNING) << "Unable to rename directory from " << dataStagedDirName << " to "
-                      << &dataCurrentDirName[0];
-        return;
-    }
-
-    LOG(INFO) << "Staged install complete.";
-}
-/*
- * Process a staged operation if there is one.
- */
-static void processStagedOperation(const std::string& dataStagedDirName,
-                                   const std::string& dataCurrentDirName) {
-    PathStatus dataStagedDirStatus = checkPath(dataStagedDirName);
-
-    // Exit early for the common case.
-    if (dataStagedDirStatus == NONE) {
-        LOG(DEBUG) << "No staged time zone operation.";
-        return;
-    }
-
-    // Check known directory names are in a good starting state.
-    if (dataStagedDirStatus != IS_DIR) {
-        LOG(WARNING) << "Staged distro dir " << dataStagedDirName
-                     << " could not be accessed or is not a directory."
-                     << " stagedDirStatus=" << dataStagedDirStatus;
-        return;
-    }
-
-    // dataStagedDirStatus == IS_DIR.
-
-    // Work out whether there is anything currently installed.
-    PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
-    if (dataCurrentDirStatus == ERR) {
-        LOG(WARNING) << "Current install dir " << dataCurrentDirName << " could not be accessed"
-                     << " dataCurrentDirStatus=" << dataCurrentDirStatus;
-        return;
-    }
-
-    // We must perform the staged operation.
-
-    // Check to see if the staged directory contains an uninstall or an install operation.
-    std::string uninstallTombStoneFile(dataStagedDirName);
-    uninstallTombStoneFile += UNINSTALL_TOMBSTONE_FILE_NAME;
-    int uninstallTombStoneFileStatus = checkPath(uninstallTombStoneFile);
-    if (uninstallTombStoneFileStatus != IS_REG && uninstallTombStoneFileStatus != NONE) {
-        // Error case.
-        LOG(WARNING) << "Unable to determine if the staged operation is an uninstall.";
-        return;
-    }
-    if (uninstallTombStoneFileStatus == IS_REG) {
-        handleStagedUninstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
-    } else {
-        // uninstallTombStoneFileStatus == NONE meaning this is a staged install.
-        handleStagedInstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
-    }
-}
-
-/*
- * After a platform update it is likely that timezone data found on the system partition will be
- * newer than the version found in the data partition. This tool detects this case and removes the
- * version in /data.
- *
- * Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The
- * paths for the metadata and current timezone data must match.
- *
- * Typically on device the two args will be:
- *   /system/usr/share/zoneinfo /data/misc/zoneinfo
- *
- * See usage() for usage notes.
- */
-int main(int argc, char* argv[]) {
-    if (argc != 3) {
-        usage();
-        return 1;
-    }
-
-    const char* systemZoneInfoDir = argv[1];
-    const char* dataZoneInfoDir = argv[2];
-
-    std::string dataStagedDirName(dataZoneInfoDir);
-    dataStagedDirName += STAGED_DIR_NAME;
-
-    std::string dataCurrentDirName(dataZoneInfoDir);
-    dataCurrentDirName += CURRENT_DIR_NAME;
-
-    // Check for an process any staged operation.
-    // If the staged operation could not be handled we still have to validate the current installed
-    // directory so we do not check for errors and do not quit early.
-    processStagedOperation(dataStagedDirName, dataCurrentDirName);
-
-    // Check the distro directory exists. If it does not, exit quickly: nothing to do.
-    PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
-    if (dataCurrentDirStatus == NONE) {
-        LOG(INFO) << "timezone distro dir " << dataCurrentDirName
-                << " does not exist. No action required.";
-        return 0;
-    }
-
-    // If the distro directory path is not a directory or we can't stat() the path, exit with a
-    // warning: either there's a problem accessing storage or the world is not as it should be;
-    // nothing to do.
-    if (dataCurrentDirStatus != IS_DIR) {
-        LOG(WARNING) << "Current distro dir " << dataCurrentDirName
-                << " could not be accessed or is not a directory. result=" << dataCurrentDirStatus;
-        return 2;
-    }
-
-    // Check the installed distro version.
-    std::string distroVersionFileName(dataCurrentDirName);
-    distroVersionFileName += DISTRO_VERSION_FILENAME;
-    std::vector<char> distroVersion;
-    distroVersion.reserve(DISTRO_VERSION_LENGTH);
-    bool distroVersionReadOk =
-            readBytes(distroVersionFileName, distroVersion.data(), DISTRO_VERSION_LENGTH);
-    if (!distroVersionReadOk) {
-        LOG(WARNING) << "distro version file " << distroVersionFileName
-                << " does not exist or is too short. Deleting distro dir.";
-        // Implies the contents of the data partition is corrupt in some way. Try to clean up.
-        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
-        deleteUpdateDistroDir(dataCurrentDirName);
-        return 3;
-    }
-
-    if (!checkValidDistroVersion(distroVersion.data())) {
-        LOG(WARNING) << "distro version file " << distroVersionFileName
-                << " is not valid. Deleting distro dir.";
-        // Implies the contents of the data partition is corrupt in some way. Try to clean up.
-        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
-        deleteUpdateDistroDir(dataCurrentDirName);
-        return 4;
-    }
-
-    std::string actualDistroVersion =
-            std::string(distroVersion.data(), SUPPORTED_DISTRO_VERSION_LEN);
-    // Check the first 3 bytes of the distro version: these are the major version (e.g. 001).
-    // It must match the one we support exactly to be ok.
-    if (strncmp(
-            &distroVersion[0],
-            SUPPORTED_DISTRO_MAJOR_VERSION,
-            SUPPORTED_DISTRO_MAJOR_VERSION_LEN) != 0) {
-
-        LOG(INFO) << "distro version file " << distroVersionFileName
-                << " major version is not the required version " << SUPPORTED_DISTRO_MAJOR_VERSION
-                << ", was \"" << actualDistroVersion << "\". Deleting distro dir.";
-        // This implies there has been an OTA and the installed distro is not compatible with the
-        // new version of Android. Remove the installed distro.
-        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
-        deleteUpdateDistroDir(dataCurrentDirName);
-        return 5;
-    }
-
-    // Check the last 3 bytes of the distro version: these are the minor version (e.g. 001).
-    // If the version in the distro is < the minor version required by this device it cannot be
-    // used.
-    if (strncmp(
-            &distroVersion[4],
-            SUPPORTED_DISTRO_MINOR_VERSION,
-            SUPPORTED_DISTRO_MINOR_VERSION_LEN) < 0) {
-
-        LOG(INFO) << "distro version file " << distroVersionFileName
-                << " minor version is not the required version " << SUPPORTED_DISTRO_MINOR_VERSION
-                << ", was \"" << actualDistroVersion << "\". Deleting distro dir.";
-        // This implies there has been an OTA and the installed distro is not compatible with the
-        // new version of Android. Remove the installed distro.
-        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
-        deleteUpdateDistroDir(dataCurrentDirName);
-        return 5;
-    }
-
-    // Read the system rules version out of the /system tzdata file.
-    std::string systemTzDataFileName(systemZoneInfoDir);
-    systemTzDataFileName += TZDATA_FILENAME;
-    std::vector<char> systemTzDataHeader;
-    systemTzDataHeader.reserve(TZ_HEADER_LENGTH);
-    bool systemFileExists =
-            readBytes(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
-    if (!systemFileExists) {
-        // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
-        LOG(WARNING) << systemTzDataFileName << " does not exist or could not be opened";
-        return 6;
-    }
-    if (!checkValidTzDataHeader(systemTzDataFileName, systemTzDataHeader.data())) {
-        // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
-        LOG(WARNING) << systemTzDataFileName << " does not have a valid header.";
-        return 7;
-    }
-
-    // Compare the distro rules version against the system rules version.
-    if (strncmp(
-            &systemTzDataHeader[TZ_DATA_HEADER_PREFIX_LEN],
-            &distroVersion[DISTRO_VERSION_RULES_IDX],
-            RULES_VERSION_LEN) <= 0) {
-        LOG(INFO) << "Found an installed distro but it is valid. No action taken.";
-        // Implies there is an installed update, but it is good.
-        return 0;
-    }
-
-    // Implies there has been an OTA and the system version of the timezone rules is now newer
-    // than the version installed in /data. Remove the installed distro.
-    LOG(INFO) << "timezone distro in " << dataCurrentDirName << " is older than data in "
-            << systemTzDataFileName << "; fixing...";
-
-    deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
-    deleteUpdateDistroDir(dataCurrentDirName);
-    return 0;
-}