Merge "Revert "Add functions for testability to the EventLog APIs""
diff --git a/adb/adb.h b/adb/adb.h
index a4d233e..3651413 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -67,9 +67,8 @@
     uint32_t magic;       /* command ^ 0xffffffff             */
 };
 
-struct apacket
-{
-    apacket *next;
+struct apacket {
+    apacket* next;
 
     size_t len;
     char* ptr;
@@ -85,13 +84,11 @@
 ** this should be used to cleanup objects that depend on the
 ** transport (e.g. remote sockets, listeners, etc...)
 */
-struct  adisconnect
-{
-    void        (*func)(void*  opaque, atransport*  t);
-    void*         opaque;
+struct adisconnect {
+    void (*func)(void* opaque, atransport* t);
+    void* opaque;
 };
 
-
 // A transport object models the connection to a remote device or emulator there
 // is one transport per connected device/emulator. A "local transport" connects
 // through TCP (for the emulator), while a "usb transport" through USB (for real
@@ -121,15 +118,14 @@
     kCsUnauthorized,
 };
 
-
-void print_packet(const char *label, apacket *p);
+void print_packet(const char* label, apacket* p);
 
 // These use the system (v)fprintf, not the adb prefixed ones defined in sysdeps.h, so they
 // shouldn't be tagged with ADB_FORMAT_ARCHETYPE.
 void fatal(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
 void fatal_errno(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
 
-void handle_packet(apacket *p, atransport *t);
+void handle_packet(apacket* p, atransport* t);
 
 int launch_server(const std::string& socket_spec);
 int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply_fd);
@@ -138,7 +134,7 @@
 #if ADB_HOST
 int get_available_local_transport_index();
 #endif
-int  init_socket_transport(atransport *t, int s, int port, int local);
+int init_socket_transport(atransport* t, int s, int port, int local);
 void init_usb_transport(atransport* t, usb_handle* usb);
 
 std::string getEmulatorSerialString(int console_port);
@@ -153,77 +149,78 @@
 #endif
 
 #if !ADB_HOST
-int       init_jdwp(void);
-asocket*  create_jdwp_service_socket();
-asocket*  create_jdwp_tracker_service_socket();
-int       create_jdwp_connection_fd(int  jdwp_pid);
+int init_jdwp(void);
+asocket* create_jdwp_service_socket();
+asocket* create_jdwp_tracker_service_socket();
+int create_jdwp_connection_fd(int jdwp_pid);
 #endif
 
 int handle_forward_request(const char* service, TransportType type, const char* serial,
                            TransportId transport_id, int reply_fd);
 
 #if !ADB_HOST
-void framebuffer_service(int fd, void *cookie);
+void framebuffer_service(int fd, void* cookie);
 void set_verity_enabled_state_service(int fd, void* cookie);
 #endif
 
 /* packet allocator */
-apacket *get_apacket(void);
-void put_apacket(apacket *p);
+apacket* get_apacket(void);
+void put_apacket(apacket* p);
 
 // Define it if you want to dump packets.
 #define DEBUG_PACKETS 0
 
 #if !DEBUG_PACKETS
-#define print_packet(tag,p) do {} while (0)
+#define print_packet(tag, p) \
+    do {                     \
+    } while (0)
 #endif
 
 #if ADB_HOST_ON_TARGET
 /* adb and adbd are coexisting on the target, so use 5038 for adb
  * to avoid conflicting with adbd's usage of 5037
  */
-#  define DEFAULT_ADB_PORT 5038
+#define DEFAULT_ADB_PORT 5038
 #else
-#  define DEFAULT_ADB_PORT 5037
+#define DEFAULT_ADB_PORT 5037
 #endif
 
 #define DEFAULT_ADB_LOCAL_TRANSPORT_PORT 5555
 
-#define ADB_CLASS              0xff
-#define ADB_SUBCLASS           0x42
-#define ADB_PROTOCOL           0x1
-
+#define ADB_CLASS 0xff
+#define ADB_SUBCLASS 0x42
+#define ADB_PROTOCOL 0x1
 
 void local_init(int port);
 bool local_connect(int port);
-int  local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error);
+int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error);
 
-ConnectionState connection_state(atransport *t);
+ConnectionState connection_state(atransport* t);
 
 extern const char* adb_device_banner;
 
 #if !ADB_HOST
 extern int SHELL_EXIT_NOTIFY_FD;
-#endif // !ADB_HOST
+#endif  // !ADB_HOST
 
-#define CHUNK_SIZE (64*1024)
+#define CHUNK_SIZE (64 * 1024)
 
 #if !ADB_HOST
-#define USB_FFS_ADB_PATH  "/dev/usb-ffs/adb/"
-#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH#x
+#define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/"
+#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH #x
 
-#define USB_FFS_ADB_EP0   USB_FFS_ADB_EP(ep0)
-#define USB_FFS_ADB_OUT   USB_FFS_ADB_EP(ep1)
-#define USB_FFS_ADB_IN    USB_FFS_ADB_EP(ep2)
+#define USB_FFS_ADB_EP0 USB_FFS_ADB_EP(ep0)
+#define USB_FFS_ADB_OUT USB_FFS_ADB_EP(ep1)
+#define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2)
 #endif
 
 int handle_host_request(const char* service, TransportType type, const char* serial,
                         TransportId transport_id, int reply_fd, asocket* s);
 
-void handle_online(atransport *t);
-void handle_offline(atransport *t);
+void handle_online(atransport* t);
+void handle_offline(atransport* t);
 
-void send_connect(atransport *t);
+void send_connect(atransport* t);
 
 void parse_banner(const std::string&, atransport* t);
 
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 30cb29b..a142384 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -172,7 +172,7 @@
     auto pred = [](const std::unique_ptr<alistener>& listener) {
         return listener->local_name == "*smartsocket*";
     };
-    listener_list.erase(std::remove_if(listener_list.begin(), listener_list.end(), pred));
+    listener_list.remove_if(pred);
 }
 
 InstallStatus install_listener(const std::string& local_name, const char* connect_to,
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index a7df0ed..1f376a4 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -253,7 +253,7 @@
                             continue;
                         }
                             /* aproto 01 needs 0 termination */
-                        if (interface->bInterfaceProtocol == 0x01) {
+                        if (interface->bInterfaceProtocol == ADB_PROTOCOL) {
                             max_packet_size = ep1->wMaxPacketSize;
                             zero_mask = ep1->wMaxPacketSize - 1;
                         }
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index 4e1480f..2e999ee 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -174,7 +174,7 @@
         kr = (*iface)->GetInterfaceClass(iface, &if_class);
         kr = (*iface)->GetInterfaceSubClass(iface, &subclass);
         kr = (*iface)->GetInterfaceProtocol(iface, &protocol);
-        if(if_class != ADB_CLASS || subclass != ADB_SUBCLASS || protocol != ADB_PROTOCOL) {
+        if (!is_adb_interface(if_class, subclass, protocol)) {
             // Ignore non-ADB devices.
             LOG(DEBUG) << "Ignoring interface with incorrect class/subclass/protocol - " << if_class
                        << ", " << subclass << ", " << protocol;
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 1620e6e..61981b1 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -18,8 +18,10 @@
 
 #include "sysdeps.h"
 
+// clang-format off
 #include <winsock2.h>  // winsock.h *must* be included before windows.h.
 #include <windows.h>
+// clang-format on
 #include <usb100.h>
 #include <winerror.h>
 
@@ -47,29 +49,29 @@
   ability to break a thread out of pipe IO.
 */
 struct usb_handle {
-  /// Previous entry in the list of opened usb handles
-  usb_handle *prev;
+    /// Previous entry in the list of opened usb handles
+    usb_handle* prev;
 
-  /// Next entry in the list of opened usb handles
-  usb_handle *next;
+    /// Next entry in the list of opened usb handles
+    usb_handle* next;
 
-  /// Handle to USB interface
-  ADBAPIHANDLE  adb_interface;
+    /// Handle to USB interface
+    ADBAPIHANDLE adb_interface;
 
-  /// Handle to USB read pipe (endpoint)
-  ADBAPIHANDLE  adb_read_pipe;
+    /// Handle to USB read pipe (endpoint)
+    ADBAPIHANDLE adb_read_pipe;
 
-  /// Handle to USB write pipe (endpoint)
-  ADBAPIHANDLE  adb_write_pipe;
+    /// Handle to USB write pipe (endpoint)
+    ADBAPIHANDLE adb_write_pipe;
 
-  /// Interface name
-  wchar_t*      interface_name;
+    /// Interface name
+    wchar_t* interface_name;
 
-  /// Maximum packet size.
-  unsigned max_packet_size;
+    /// Maximum packet size.
+    unsigned max_packet_size;
 
-  /// Mask for determining when to use zero length packets
-  unsigned zero_mask;
+    /// Mask for determining when to use zero length packets
+    unsigned zero_mask;
 };
 
 /// Class ID assigned to the device by androidusb.sys
@@ -77,8 +79,7 @@
 
 /// List of opened usb handles
 static usb_handle handle_list = {
-  .prev = &handle_list,
-  .next = &handle_list,
+    .prev = &handle_list, .next = &handle_list,
 };
 
 /// Locker for the list of opened usb handles
@@ -118,7 +119,7 @@
 int usb_write(usb_handle* handle, const void* data, int len);
 
 /// Reads data using the opened usb handle
-int usb_read(usb_handle *handle, void* data, int len);
+int usb_read(usb_handle* handle, void* data, int len);
 
 /// Cleans up opened usb handle
 void usb_cleanup_handle(usb_handle* handle);
@@ -130,401 +131,374 @@
 int usb_close(usb_handle* handle);
 
 int known_device_locked(const wchar_t* dev_name) {
-  usb_handle* usb;
+    usb_handle* usb;
 
-  if (NULL != dev_name) {
-    // Iterate through the list looking for the name match.
-    for(usb = handle_list.next; usb != &handle_list; usb = usb->next) {
-      // In Windows names are not case sensetive!
-      if((NULL != usb->interface_name) &&
-         (0 == wcsicmp(usb->interface_name, dev_name))) {
-        return 1;
-      }
+    if (NULL != dev_name) {
+        // Iterate through the list looking for the name match.
+        for (usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+            // In Windows names are not case sensetive!
+            if ((NULL != usb->interface_name) && (0 == wcsicmp(usb->interface_name, dev_name))) {
+                return 1;
+            }
+        }
     }
-  }
 
-  return 0;
+    return 0;
 }
 
 int known_device(const wchar_t* dev_name) {
-  int ret = 0;
+    int ret = 0;
 
-  if (NULL != dev_name) {
-    std::lock_guard<std::mutex> lock(usb_lock);
-    ret = known_device_locked(dev_name);
-  }
+    if (NULL != dev_name) {
+        std::lock_guard<std::mutex> lock(usb_lock);
+        ret = known_device_locked(dev_name);
+    }
 
-  return ret;
+    return ret;
 }
 
 int register_new_device(usb_handle* handle) {
-  if (NULL == handle)
-    return 0;
+    if (NULL == handle) return 0;
 
-  std::lock_guard<std::mutex> lock(usb_lock);
+    std::lock_guard<std::mutex> lock(usb_lock);
 
-  // Check if device is already in the list
-  if (known_device_locked(handle->interface_name)) {
-    return 0;
-  }
+    // Check if device is already in the list
+    if (known_device_locked(handle->interface_name)) {
+        return 0;
+    }
 
-  // Not in the list. Add this handle to the list.
-  handle->next = &handle_list;
-  handle->prev = handle_list.prev;
-  handle->prev->next = handle;
-  handle->next->prev = handle;
+    // Not in the list. Add this handle to the list.
+    handle->next = &handle_list;
+    handle->prev = handle_list.prev;
+    handle->prev->next = handle;
+    handle->next->prev = handle;
 
-  return 1;
+    return 1;
 }
 
 void device_poll_thread() {
-  adb_thread_setname("Device Poll");
-  D("Created device thread");
+    adb_thread_setname("Device Poll");
+    D("Created device thread");
 
-  while (true) {
-    find_devices();
-    std::this_thread::sleep_for(1s);
-  }
+    while (true) {
+        find_devices();
+        std::this_thread::sleep_for(1s);
+    }
 }
 
-static LRESULT CALLBACK _power_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam,
-                                           LPARAM lParam) {
-  switch (uMsg) {
-  case WM_POWERBROADCAST:
-    switch (wParam) {
-    case PBT_APMRESUMEAUTOMATIC:
-      // Resuming from sleep or hibernation, so kick all existing USB devices
-      // and then allow the device_poll_thread to redetect USB devices from
-      // scratch. If we don't do this, existing USB devices will never respond
-      // to us because they'll be waiting for the connect/auth handshake.
-      D("Received (WM_POWERBROADCAST, PBT_APMRESUMEAUTOMATIC) notification, "
-        "so kicking all USB devices\n");
-      kick_devices();
-      return TRUE;
+static LRESULT CALLBACK _power_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uMsg) {
+        case WM_POWERBROADCAST:
+            switch (wParam) {
+                case PBT_APMRESUMEAUTOMATIC:
+                    // Resuming from sleep or hibernation, so kick all existing USB devices
+                    // and then allow the device_poll_thread to redetect USB devices from
+                    // scratch. If we don't do this, existing USB devices will never respond
+                    // to us because they'll be waiting for the connect/auth handshake.
+                    D("Received (WM_POWERBROADCAST, PBT_APMRESUMEAUTOMATIC) notification, "
+                      "so kicking all USB devices\n");
+                    kick_devices();
+                    return TRUE;
+            }
     }
-  }
-  return DefWindowProcW(hwnd, uMsg, wParam, lParam);
+    return DefWindowProcW(hwnd, uMsg, wParam, lParam);
 }
 
 static void _power_notification_thread() {
-  // This uses a thread with its own window message pump to get power
-  // notifications. If adb runs from a non-interactive service account, this
-  // might not work (not sure). If that happens to not work, we could use
-  // heavyweight WMI APIs to get power notifications. But for the common case
-  // of a developer's interactive session, a window message pump is more
-  // appropriate.
-  D("Created power notification thread");
-  adb_thread_setname("Power Notifier");
+    // This uses a thread with its own window message pump to get power
+    // notifications. If adb runs from a non-interactive service account, this
+    // might not work (not sure). If that happens to not work, we could use
+    // heavyweight WMI APIs to get power notifications. But for the common case
+    // of a developer's interactive session, a window message pump is more
+    // appropriate.
+    D("Created power notification thread");
+    adb_thread_setname("Power Notifier");
 
-  // Window class names are process specific.
-  static const WCHAR kPowerNotificationWindowClassName[] =
-    L"PowerNotificationWindow";
+    // Window class names are process specific.
+    static const WCHAR kPowerNotificationWindowClassName[] = L"PowerNotificationWindow";
 
-  // Get the HINSTANCE corresponding to the module that _power_window_proc
-  // is in (the main module).
-  const HINSTANCE instance = GetModuleHandleW(NULL);
-  if (!instance) {
-    // This is such a common API call that this should never fail.
-    fatal("GetModuleHandleW failed: %s",
-          android::base::SystemErrorCodeToString(GetLastError()).c_str());
-  }
+    // Get the HINSTANCE corresponding to the module that _power_window_proc
+    // is in (the main module).
+    const HINSTANCE instance = GetModuleHandleW(NULL);
+    if (!instance) {
+        // This is such a common API call that this should never fail.
+        fatal("GetModuleHandleW failed: %s",
+              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+    }
 
-  WNDCLASSEXW wndclass;
-  memset(&wndclass, 0, sizeof(wndclass));
-  wndclass.cbSize = sizeof(wndclass);
-  wndclass.lpfnWndProc = _power_window_proc;
-  wndclass.hInstance = instance;
-  wndclass.lpszClassName = kPowerNotificationWindowClassName;
-  if (!RegisterClassExW(&wndclass)) {
-    fatal("RegisterClassExW failed: %s",
-          android::base::SystemErrorCodeToString(GetLastError()).c_str());
-  }
+    WNDCLASSEXW wndclass;
+    memset(&wndclass, 0, sizeof(wndclass));
+    wndclass.cbSize = sizeof(wndclass);
+    wndclass.lpfnWndProc = _power_window_proc;
+    wndclass.hInstance = instance;
+    wndclass.lpszClassName = kPowerNotificationWindowClassName;
+    if (!RegisterClassExW(&wndclass)) {
+        fatal("RegisterClassExW failed: %s",
+              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+    }
 
-  if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
-                       L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0,
-                       NULL, NULL, instance, NULL)) {
-    fatal("CreateWindowExW failed: %s",
-          android::base::SystemErrorCodeToString(GetLastError()).c_str());
-  }
+    if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
+                         L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0, NULL, NULL,
+                         instance, NULL)) {
+        fatal("CreateWindowExW failed: %s",
+              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+    }
 
-  MSG msg;
-  while (GetMessageW(&msg, NULL, 0, 0)) {
-    TranslateMessage(&msg);
-    DispatchMessageW(&msg);
-  }
+    MSG msg;
+    while (GetMessageW(&msg, NULL, 0, 0)) {
+        TranslateMessage(&msg);
+        DispatchMessageW(&msg);
+    }
 
-  // GetMessageW() will return false if a quit message is posted. We don't
-  // do that, but it might be possible for that to occur when logging off or
-  // shutting down. Not a big deal since the whole process will be going away
-  // soon anyway.
-  D("Power notification thread exiting");
+    // GetMessageW() will return false if a quit message is posted. We don't
+    // do that, but it might be possible for that to occur when logging off or
+    // shutting down. Not a big deal since the whole process will be going away
+    // soon anyway.
+    D("Power notification thread exiting");
 }
 
 void usb_init() {
-  std::thread(device_poll_thread).detach();
-  std::thread(_power_notification_thread).detach();
+    std::thread(device_poll_thread).detach();
+    std::thread(_power_notification_thread).detach();
 }
 
 void usb_cleanup() {}
 
 usb_handle* do_usb_open(const wchar_t* interface_name) {
-  unsigned long name_len = 0;
+    unsigned long name_len = 0;
 
-  // Allocate our handle
-  usb_handle* ret = (usb_handle*)calloc(1, sizeof(usb_handle));
-  if (NULL == ret) {
-    D("Could not allocate %u bytes for usb_handle: %s", sizeof(usb_handle),
-      strerror(errno));
-    goto fail;
-  }
+    // Allocate our handle
+    usb_handle* ret = (usb_handle*)calloc(1, sizeof(usb_handle));
+    if (NULL == ret) {
+        D("Could not allocate %u bytes for usb_handle: %s", sizeof(usb_handle), strerror(errno));
+        goto fail;
+    }
 
-  // Set linkers back to the handle
-  ret->next = ret;
-  ret->prev = ret;
+    // Set linkers back to the handle
+    ret->next = ret;
+    ret->prev = ret;
 
-  // Create interface.
-  ret->adb_interface = AdbCreateInterfaceByName(interface_name);
-  if (NULL == ret->adb_interface) {
-    D("AdbCreateInterfaceByName failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    goto fail;
-  }
+    // Create interface.
+    ret->adb_interface = AdbCreateInterfaceByName(interface_name);
+    if (NULL == ret->adb_interface) {
+        D("AdbCreateInterfaceByName failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        goto fail;
+    }
 
-  // Open read pipe (endpoint)
-  ret->adb_read_pipe =
-    AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
-                                   AdbOpenAccessTypeReadWrite,
-                                   AdbOpenSharingModeReadWrite);
-  if (NULL == ret->adb_read_pipe) {
-    D("AdbOpenDefaultBulkReadEndpoint failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    goto fail;
-  }
+    // Open read pipe (endpoint)
+    ret->adb_read_pipe = AdbOpenDefaultBulkReadEndpoint(
+        ret->adb_interface, AdbOpenAccessTypeReadWrite, AdbOpenSharingModeReadWrite);
+    if (NULL == ret->adb_read_pipe) {
+        D("AdbOpenDefaultBulkReadEndpoint failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        goto fail;
+    }
 
-  // Open write pipe (endpoint)
-  ret->adb_write_pipe =
-    AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
-                                    AdbOpenAccessTypeReadWrite,
-                                    AdbOpenSharingModeReadWrite);
-  if (NULL == ret->adb_write_pipe) {
-    D("AdbOpenDefaultBulkWriteEndpoint failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    goto fail;
-  }
+    // Open write pipe (endpoint)
+    ret->adb_write_pipe = AdbOpenDefaultBulkWriteEndpoint(
+        ret->adb_interface, AdbOpenAccessTypeReadWrite, AdbOpenSharingModeReadWrite);
+    if (NULL == ret->adb_write_pipe) {
+        D("AdbOpenDefaultBulkWriteEndpoint failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        goto fail;
+    }
 
-  // Save interface name
-  // First get expected name length
-  AdbGetInterfaceName(ret->adb_interface,
-                      NULL,
-                      &name_len,
-                      false);
-  if (0 == name_len) {
-    D("AdbGetInterfaceName returned name length of zero: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    goto fail;
-  }
+    // Save interface name
+    // First get expected name length
+    AdbGetInterfaceName(ret->adb_interface, NULL, &name_len, false);
+    if (0 == name_len) {
+        D("AdbGetInterfaceName returned name length of zero: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        goto fail;
+    }
 
-  ret->interface_name = (wchar_t*)malloc(name_len * sizeof(ret->interface_name[0]));
-  if (NULL == ret->interface_name) {
-    D("Could not allocate %lu characters for interface_name: %s", name_len, strerror(errno));
-    goto fail;
-  }
+    ret->interface_name = (wchar_t*)malloc(name_len * sizeof(ret->interface_name[0]));
+    if (NULL == ret->interface_name) {
+        D("Could not allocate %lu characters for interface_name: %s", name_len, strerror(errno));
+        goto fail;
+    }
 
-  // Now save the name
-  if (!AdbGetInterfaceName(ret->adb_interface,
-                           ret->interface_name,
-                           &name_len,
-                           false)) {
-    D("AdbGetInterfaceName failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    goto fail;
-  }
+    // Now save the name
+    if (!AdbGetInterfaceName(ret->adb_interface, ret->interface_name, &name_len, false)) {
+        D("AdbGetInterfaceName failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        goto fail;
+    }
 
-  // We're done at this point
-  return ret;
+    // We're done at this point
+    return ret;
 
 fail:
-  if (NULL != ret) {
-    usb_cleanup_handle(ret);
-    free(ret);
-  }
+    if (NULL != ret) {
+        usb_cleanup_handle(ret);
+        free(ret);
+    }
 
-  return NULL;
+    return NULL;
 }
 
 int usb_write(usb_handle* handle, const void* data, int len) {
-  unsigned long time_out = 5000;
-  unsigned long written = 0;
-  int err = 0;
+    unsigned long time_out = 5000;
+    unsigned long written = 0;
+    int err = 0;
 
-  D("usb_write %d", len);
-  if (NULL == handle) {
-    D("usb_write was passed NULL handle");
-    err = EINVAL;
-    goto fail;
-  }
-
-  // Perform write
-  if (!AdbWriteEndpointSync(handle->adb_write_pipe,
-                            (void*)data,
-                            (unsigned long)len,
-                            &written,
-                            time_out)) {
-    D("AdbWriteEndpointSync failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    err = EIO;
-    goto fail;
-  }
-
-  // Make sure that we've written what we were asked to write
-  D("usb_write got: %ld, expected: %d", written, len);
-  if (written != (unsigned long)len) {
-    // If this occurs, this code should be changed to repeatedly call
-    // AdbWriteEndpointSync() until all bytes are written.
-    D("AdbWriteEndpointSync was supposed to write %d, but only wrote %ld",
-      len, written);
-    err = EIO;
-    goto fail;
-  }
-
-  if (handle->zero_mask && (len & handle->zero_mask) == 0) {
-    // Send a zero length packet
-    if (!AdbWriteEndpointSync(handle->adb_write_pipe,
-                              (void*)data,
-                              0,
-                              &written,
-                              time_out)) {
-      D("AdbWriteEndpointSync of zero length packet failed: %s",
-        android::base::SystemErrorCodeToString(GetLastError()).c_str());
-      err = EIO;
-      goto fail;
+    D("usb_write %d", len);
+    if (NULL == handle) {
+        D("usb_write was passed NULL handle");
+        err = EINVAL;
+        goto fail;
     }
-  }
 
-  return 0;
+    // Perform write
+    if (!AdbWriteEndpointSync(handle->adb_write_pipe, (void*)data, (unsigned long)len, &written,
+                              time_out)) {
+        D("AdbWriteEndpointSync failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        err = EIO;
+        goto fail;
+    }
+
+    // Make sure that we've written what we were asked to write
+    D("usb_write got: %ld, expected: %d", written, len);
+    if (written != (unsigned long)len) {
+        // If this occurs, this code should be changed to repeatedly call
+        // AdbWriteEndpointSync() until all bytes are written.
+        D("AdbWriteEndpointSync was supposed to write %d, but only wrote %ld", len, written);
+        err = EIO;
+        goto fail;
+    }
+
+    if (handle->zero_mask && (len & handle->zero_mask) == 0) {
+        // Send a zero length packet
+        if (!AdbWriteEndpointSync(handle->adb_write_pipe, (void*)data, 0, &written, time_out)) {
+            D("AdbWriteEndpointSync of zero length packet failed: %s",
+              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+            err = EIO;
+            goto fail;
+        }
+    }
+
+    return 0;
 
 fail:
-  // Any failure should cause us to kick the device instead of leaving it a
-  // zombie state with potential to hang.
-  if (NULL != handle) {
-    D("Kicking device due to error in usb_write");
-    usb_kick(handle);
-  }
+    // Any failure should cause us to kick the device instead of leaving it a
+    // zombie state with potential to hang.
+    if (NULL != handle) {
+        D("Kicking device due to error in usb_write");
+        usb_kick(handle);
+    }
 
-  D("usb_write failed");
-  errno = err;
-  return -1;
+    D("usb_write failed");
+    errno = err;
+    return -1;
 }
 
-int usb_read(usb_handle *handle, void* data, int len) {
-  unsigned long time_out = 0;
-  unsigned long read = 0;
-  int err = 0;
-  int orig_len = len;
+int usb_read(usb_handle* handle, void* data, int len) {
+    unsigned long time_out = 0;
+    unsigned long read = 0;
+    int err = 0;
+    int orig_len = len;
 
-  D("usb_read %d", len);
-  if (NULL == handle) {
-    D("usb_read was passed NULL handle");
-    err = EINVAL;
-    goto fail;
-  }
-
-  while (len == orig_len) {
-    if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read, time_out)) {
-      D("AdbReadEndpointSync failed: %s",
-        android::base::SystemErrorCodeToString(GetLastError()).c_str());
-      err = EIO;
-      goto fail;
+    D("usb_read %d", len);
+    if (NULL == handle) {
+        D("usb_read was passed NULL handle");
+        err = EINVAL;
+        goto fail;
     }
-    D("usb_read got: %ld, expected: %d", read, len);
 
-    data = (char*)data + read;
-    len -= read;
-  }
+    while (len == orig_len) {
+        if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read, time_out)) {
+            D("AdbReadEndpointSync failed: %s",
+              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+            err = EIO;
+            goto fail;
+        }
+        D("usb_read got: %ld, expected: %d", read, len);
 
-  return orig_len - len;
+        data = (char*)data + read;
+        len -= read;
+    }
+
+    return orig_len - len;
 
 fail:
-  // Any failure should cause us to kick the device instead of leaving it a
-  // zombie state with potential to hang.
-  if (NULL != handle) {
-    D("Kicking device due to error in usb_read");
-    usb_kick(handle);
-  }
+    // Any failure should cause us to kick the device instead of leaving it a
+    // zombie state with potential to hang.
+    if (NULL != handle) {
+        D("Kicking device due to error in usb_read");
+        usb_kick(handle);
+    }
 
-  D("usb_read failed");
-  errno = err;
-  return -1;
+    D("usb_read failed");
+    errno = err;
+    return -1;
 }
 
 // Wrapper around AdbCloseHandle() that logs diagnostics.
 static void _adb_close_handle(ADBAPIHANDLE adb_handle) {
-  if (!AdbCloseHandle(adb_handle)) {
-    D("AdbCloseHandle(%p) failed: %s", adb_handle,
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-  }
+    if (!AdbCloseHandle(adb_handle)) {
+        D("AdbCloseHandle(%p) failed: %s", adb_handle,
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+    }
 }
 
 void usb_cleanup_handle(usb_handle* handle) {
-  D("usb_cleanup_handle");
-  if (NULL != handle) {
-    if (NULL != handle->interface_name)
-      free(handle->interface_name);
-    // AdbCloseHandle(pipe) will break any threads out of pending IO calls and
-    // wait until the pipe no longer uses the interface. Then we can
-    // AdbCloseHandle() the interface.
-    if (NULL != handle->adb_write_pipe)
-      _adb_close_handle(handle->adb_write_pipe);
-    if (NULL != handle->adb_read_pipe)
-      _adb_close_handle(handle->adb_read_pipe);
-    if (NULL != handle->adb_interface)
-      _adb_close_handle(handle->adb_interface);
+    D("usb_cleanup_handle");
+    if (NULL != handle) {
+        if (NULL != handle->interface_name) free(handle->interface_name);
+        // AdbCloseHandle(pipe) will break any threads out of pending IO calls and
+        // wait until the pipe no longer uses the interface. Then we can
+        // AdbCloseHandle() the interface.
+        if (NULL != handle->adb_write_pipe) _adb_close_handle(handle->adb_write_pipe);
+        if (NULL != handle->adb_read_pipe) _adb_close_handle(handle->adb_read_pipe);
+        if (NULL != handle->adb_interface) _adb_close_handle(handle->adb_interface);
 
-    handle->interface_name = NULL;
-    handle->adb_write_pipe = NULL;
-    handle->adb_read_pipe = NULL;
-    handle->adb_interface = NULL;
-  }
+        handle->interface_name = NULL;
+        handle->adb_write_pipe = NULL;
+        handle->adb_read_pipe = NULL;
+        handle->adb_interface = NULL;
+    }
 }
 
 static void usb_kick_locked(usb_handle* handle) {
-  // The reason the lock must be acquired before calling this function is in
-  // case multiple threads are trying to kick the same device at the same time.
-  usb_cleanup_handle(handle);
+    // The reason the lock must be acquired before calling this function is in
+    // case multiple threads are trying to kick the same device at the same time.
+    usb_cleanup_handle(handle);
 }
 
 void usb_kick(usb_handle* handle) {
-  D("usb_kick");
-  if (NULL != handle) {
-    std::lock_guard<std::mutex> lock(usb_lock);
-    usb_kick_locked(handle);
-  } else {
-    errno = EINVAL;
-  }
+    D("usb_kick");
+    if (NULL != handle) {
+        std::lock_guard<std::mutex> lock(usb_lock);
+        usb_kick_locked(handle);
+    } else {
+        errno = EINVAL;
+    }
 }
 
 int usb_close(usb_handle* handle) {
-  D("usb_close");
+    D("usb_close");
 
-  if (NULL != handle) {
-    // Remove handle from the list
-    {
-      std::lock_guard<std::mutex> lock(usb_lock);
+    if (NULL != handle) {
+        // Remove handle from the list
+        {
+            std::lock_guard<std::mutex> lock(usb_lock);
 
-      if ((handle->next != handle) && (handle->prev != handle)) {
-        handle->next->prev = handle->prev;
-        handle->prev->next = handle->next;
-        handle->prev = handle;
-        handle->next = handle;
-      }
+            if ((handle->next != handle) && (handle->prev != handle)) {
+                handle->next->prev = handle->prev;
+                handle->prev->next = handle->next;
+                handle->prev = handle;
+                handle->next = handle;
+            }
+        }
+
+        // Cleanup handle
+        usb_cleanup_handle(handle);
+        free(handle);
     }
 
-    // Cleanup handle
-    usb_cleanup_handle(handle);
-    free(handle);
-  }
-
-  return 0;
+    return 0;
 }
 
 size_t usb_get_max_packet_size(usb_handle* handle) {
@@ -532,131 +506,124 @@
 }
 
 int recognized_device(usb_handle* handle) {
-  if (NULL == handle)
-    return 0;
+    if (NULL == handle) return 0;
 
-  // Check vendor and product id first
-  USB_DEVICE_DESCRIPTOR device_desc;
+    // Check vendor and product id first
+    USB_DEVICE_DESCRIPTOR device_desc;
 
-  if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
-                                 &device_desc)) {
-    D("AdbGetUsbDeviceDescriptor failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    return 0;
-  }
+    if (!AdbGetUsbDeviceDescriptor(handle->adb_interface, &device_desc)) {
+        D("AdbGetUsbDeviceDescriptor failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        return 0;
+    }
 
-  // Then check interface properties
-  USB_INTERFACE_DESCRIPTOR interf_desc;
+    // Then check interface properties
+    USB_INTERFACE_DESCRIPTOR interf_desc;
 
-  if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
-                                    &interf_desc)) {
-    D("AdbGetUsbInterfaceDescriptor failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    return 0;
-  }
+    if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface, &interf_desc)) {
+        D("AdbGetUsbInterfaceDescriptor failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        return 0;
+    }
 
-  // Must have two endpoints
-  if (2 != interf_desc.bNumEndpoints) {
-    return 0;
-  }
+    // Must have two endpoints
+    if (2 != interf_desc.bNumEndpoints) {
+        return 0;
+    }
 
-  if (is_adb_interface(interf_desc.bInterfaceClass, interf_desc.bInterfaceSubClass,
-                       interf_desc.bInterfaceProtocol)) {
-    if (interf_desc.bInterfaceProtocol == 0x01) {
-      AdbEndpointInformation endpoint_info;
-      // assuming zero is a valid bulk endpoint ID
-      if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
+    if (!is_adb_interface(interf_desc.bInterfaceClass, interf_desc.bInterfaceSubClass,
+                          interf_desc.bInterfaceProtocol)) {
+        return 0;
+    }
+
+    AdbEndpointInformation endpoint_info;
+    // assuming zero is a valid bulk endpoint ID
+    if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
         handle->max_packet_size = endpoint_info.max_packet_size;
         handle->zero_mask = endpoint_info.max_packet_size - 1;
         D("device zero_mask: 0x%x", handle->zero_mask);
-      } else {
+    } else {
         D("AdbGetEndpointInformation failed: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
-      }
     }
 
     return 1;
-  }
-
-  return 0;
 }
 
 void find_devices() {
-  usb_handle* handle = NULL;
-  char entry_buffer[2048];
-  AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
-  unsigned long entry_buffer_size = sizeof(entry_buffer);
+    usb_handle* handle = NULL;
+    char entry_buffer[2048];
+    AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
+    unsigned long entry_buffer_size = sizeof(entry_buffer);
 
-  // Enumerate all present and active interfaces.
-  ADBAPIHANDLE enum_handle =
-    AdbEnumInterfaces(usb_class_id, true, true, true);
+    // Enumerate all present and active interfaces.
+    ADBAPIHANDLE enum_handle = AdbEnumInterfaces(usb_class_id, true, true, true);
 
-  if (NULL == enum_handle) {
-    D("AdbEnumInterfaces failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    return;
-  }
-
-  while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
-    // Lets see if we already have this device in the list
-    if (!known_device(next_interface->device_name)) {
-      // This seems to be a new device. Open it!
-      handle = do_usb_open(next_interface->device_name);
-      if (NULL != handle) {
-        // Lets see if this interface (device) belongs to us
-        if (recognized_device(handle)) {
-          D("adding a new device %ls", next_interface->device_name);
-
-          // We don't request a wchar_t string from AdbGetSerialNumber() because of a bug in
-          // adb_winusb_interface.cpp:CopyMemory(buffer, ser_num->bString, bytes_written) where the
-          // last parameter should be (str_len * sizeof(wchar_t)). The bug reads 2 bytes past the
-          // end of a stack buffer in the best case, and in the unlikely case of a long serial
-          // number, it will read 2 bytes past the end of a heap allocation. This doesn't affect the
-          // resulting string, but we should avoid the bad reads in the first place.
-          char serial_number[512];
-          unsigned long serial_number_len = sizeof(serial_number);
-          if (AdbGetSerialNumber(handle->adb_interface,
-                                serial_number,
-                                &serial_number_len,
-                                true)) {
-            // Lets make sure that we don't duplicate this device
-            if (register_new_device(handle)) {
-              register_usb_transport(handle, serial_number, NULL, 1);
-            } else {
-              D("register_new_device failed for %ls", next_interface->device_name);
-              usb_cleanup_handle(handle);
-              free(handle);
-            }
-          } else {
-            D("cannot get serial number: %s",
-              android::base::SystemErrorCodeToString(GetLastError()).c_str());
-            usb_cleanup_handle(handle);
-            free(handle);
-          }
-        } else {
-          usb_cleanup_handle(handle);
-          free(handle);
-        }
-      }
+    if (NULL == enum_handle) {
+        D("AdbEnumInterfaces failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        return;
     }
 
-    entry_buffer_size = sizeof(entry_buffer);
-  }
+    while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
+        // Lets see if we already have this device in the list
+        if (!known_device(next_interface->device_name)) {
+            // This seems to be a new device. Open it!
+            handle = do_usb_open(next_interface->device_name);
+            if (NULL != handle) {
+                // Lets see if this interface (device) belongs to us
+                if (recognized_device(handle)) {
+                    D("adding a new device %ls", next_interface->device_name);
 
-  if (GetLastError() != ERROR_NO_MORE_ITEMS) {
-    // Only ERROR_NO_MORE_ITEMS is expected at the end of enumeration.
-    D("AdbNextInterface failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-  }
+                    // We don't request a wchar_t string from AdbGetSerialNumber() because of a bug
+                    // in adb_winusb_interface.cpp:CopyMemory(buffer, ser_num->bString,
+                    // bytes_written) where the last parameter should be (str_len *
+                    // sizeof(wchar_t)). The bug reads 2 bytes past the end of a stack buffer in the
+                    // best case, and in the unlikely case of a long serial number, it will read 2
+                    // bytes past the end of a heap allocation. This doesn't affect the resulting
+                    // string, but we should avoid the bad reads in the first place.
+                    char serial_number[512];
+                    unsigned long serial_number_len = sizeof(serial_number);
+                    if (AdbGetSerialNumber(handle->adb_interface, serial_number, &serial_number_len,
+                                           true)) {
+                        // Lets make sure that we don't duplicate this device
+                        if (register_new_device(handle)) {
+                            register_usb_transport(handle, serial_number, NULL, 1);
+                        } else {
+                            D("register_new_device failed for %ls", next_interface->device_name);
+                            usb_cleanup_handle(handle);
+                            free(handle);
+                        }
+                    } else {
+                        D("cannot get serial number: %s",
+                          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+                        usb_cleanup_handle(handle);
+                        free(handle);
+                    }
+                } else {
+                    usb_cleanup_handle(handle);
+                    free(handle);
+                }
+            }
+        }
 
-  _adb_close_handle(enum_handle);
+        entry_buffer_size = sizeof(entry_buffer);
+    }
+
+    if (GetLastError() != ERROR_NO_MORE_ITEMS) {
+        // Only ERROR_NO_MORE_ITEMS is expected at the end of enumeration.
+        D("AdbNextInterface failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+    }
+
+    _adb_close_handle(enum_handle);
 }
 
 static void kick_devices() {
-  // Need to acquire lock to safely walk the list which might be modified
-  // by another thread.
-  std::lock_guard<std::mutex> lock(usb_lock);
-  for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) {
-    usb_kick_locked(usb);
-  }
+    // Need to acquire lock to safely walk the list which might be modified
+    // by another thread.
+    std::lock_guard<std::mutex> lock(usb_lock);
+    for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+        usb_kick_locked(usb);
+    }
 }
diff --git a/adb/test_device.py b/adb/test_device.py
index ddceda9..4cf2206 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -34,11 +34,8 @@
 import time
 import unittest
 
-import mock
-
 import adb
 
-
 def requires_root(func):
     def wrapper(self, *args):
         if self.device.get_prop('ro.debuggable') != '1':
@@ -76,59 +73,6 @@
     return wrapper
 
 
-class GetDeviceTest(unittest.TestCase):
-    def setUp(self):
-        self.android_serial = os.getenv('ANDROID_SERIAL')
-        if 'ANDROID_SERIAL' in os.environ:
-            del os.environ['ANDROID_SERIAL']
-
-    def tearDown(self):
-        if self.android_serial is not None:
-            os.environ['ANDROID_SERIAL'] = self.android_serial
-        else:
-            if 'ANDROID_SERIAL' in os.environ:
-                del os.environ['ANDROID_SERIAL']
-
-    @mock.patch('adb.device.get_devices')
-    def test_explicit(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo', 'bar']
-        device = adb.get_device('foo')
-        self.assertEqual(device.serial, 'foo')
-
-    @mock.patch('adb.device.get_devices')
-    def test_from_env(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo', 'bar']
-        os.environ['ANDROID_SERIAL'] = 'foo'
-        device = adb.get_device()
-        self.assertEqual(device.serial, 'foo')
-
-    @mock.patch('adb.device.get_devices')
-    def test_arg_beats_env(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo', 'bar']
-        os.environ['ANDROID_SERIAL'] = 'bar'
-        device = adb.get_device('foo')
-        self.assertEqual(device.serial, 'foo')
-
-    @mock.patch('adb.device.get_devices')
-    def test_no_such_device(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo', 'bar']
-        self.assertRaises(adb.DeviceNotFoundError, adb.get_device, ['baz'])
-
-        os.environ['ANDROID_SERIAL'] = 'baz'
-        self.assertRaises(adb.DeviceNotFoundError, adb.get_device)
-
-    @mock.patch('adb.device.get_devices')
-    def test_unique_device(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo']
-        device = adb.get_device()
-        self.assertEqual(device.serial, 'foo')
-
-    @mock.patch('adb.device.get_devices')
-    def test_no_unique_device(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo', 'bar']
-        self.assertRaises(adb.NoUniqueDeviceError, adb.get_device)
-
-
 class DeviceTest(unittest.TestCase):
     def setUp(self):
         self.device = adb.get_device()
diff --git a/base/Android.bp b/base/Android.bp
index 0fd00ea..7ff02a0 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -14,11 +14,10 @@
 // limitations under the License.
 //
 
-libbase_cppflags = [
-    "-Wall",
-    "-Wextra",
-    "-Werror",
-]
+cc_defaults {
+    name: "libbase_defaults",
+    cflags: ["-Wall", "-Werror", "-Wextra"],
+}
 
 cc_library_headers {
     name: "libbase_headers",
@@ -38,6 +37,7 @@
 
 cc_library {
     name: "libbase",
+    defaults: ["libbase_defaults"],
     vendor_available: true,
     host_supported: true,
     vndk: {
@@ -59,21 +59,24 @@
     ],
     export_header_lib_headers: ["libbase_headers"],
 
-    cppflags: libbase_cppflags,
     shared_libs: ["liblog"],
     target: {
         android: {
             srcs: [
-                "errors_unix.cpp",
                 "properties.cpp",
-                "chrono_utils.cpp",
             ],
-            cppflags: ["-Wexit-time-destructors"],
             sanitize: {
                 misc_undefined: ["integer"],
             },
 
         },
+        linux: {
+            srcs: [
+                "chrono_utils.cpp",
+                "errors_unix.cpp",
+            ],
+            cppflags: ["-Wexit-time-destructors"],
+        },
         darwin: {
             srcs: [
                 "chrono_utils.cpp",
@@ -82,20 +85,8 @@
             cppflags: ["-Wexit-time-destructors"],
         },
         linux_bionic: {
-            srcs: [
-                "chrono_utils.cpp",
-                "errors_unix.cpp",
-            ],
-            cppflags: ["-Wexit-time-destructors"],
             enabled: true,
         },
-        linux_glibc: {
-            srcs: [
-                "chrono_utils.cpp",
-                "errors_unix.cpp",
-            ],
-            cppflags: ["-Wexit-time-destructors"],
-        },
         windows: {
             srcs: [
                 "errors_windows.cpp",
@@ -110,6 +101,7 @@
 // ------------------------------------------------------------------------------
 cc_test {
     name: "libbase_test",
+    defaults: ["libbase_defaults"],
     host_supported: true,
     srcs: [
         "endian_test.cpp",
@@ -127,15 +119,12 @@
     ],
     target: {
         android: {
-            srcs: [
-                "chrono_utils_test.cpp",
-                "properties_test.cpp"
-            ],
+            srcs: ["properties_test.cpp"],
             sanitize: {
                 misc_undefined: ["integer"],
             },
         },
-        linux_glibc: {
+        linux: {
             srcs: ["chrono_utils_test.cpp"],
         },
         windows: {
@@ -144,7 +133,6 @@
         },
     },
     local_include_dirs: ["."],
-    cppflags: libbase_cppflags,
     shared_libs: ["libbase"],
     compile_multilib: "both",
     multilib: {
diff --git a/base/logging.cpp b/base/logging.cpp
index 6357b4b..75078e5 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -80,7 +80,9 @@
 #if defined(__BIONIC__)
   return gettid();
 #elif defined(__APPLE__)
-  return syscall(SYS_thread_selfid);
+  uint64_t tid;
+  pthread_threadid_np(NULL, &tid);
+  return tid;
 #elif defined(__linux__)
   return syscall(__NR_gettid);
 #elif defined(_WIN32)
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 7ed5b2b..121197c 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -51,6 +51,14 @@
   ASSERT_EQ("bar", parts[2]);
 }
 
+TEST(strings, split_with_trailing_empty_part) {
+  std::vector<std::string> parts = android::base::Split("foo,bar,", ",");
+  ASSERT_EQ(3U, parts.size());
+  ASSERT_EQ("foo", parts[0]);
+  ASSERT_EQ("bar", parts[1]);
+  ASSERT_EQ("", parts[2]);
+}
+
 TEST(strings, split_null_char) {
   std::vector<std::string> parts =
       android::base::Split(std::string("foo\0bar", 7), std::string("\0", 1));
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 209e81b..c1d5430 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -194,10 +194,12 @@
               sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
         if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]
         then
+          sleep 1
           break
         fi
         if [ "${vals}" = "`echo logbootcomplete=1 ; echo boot_completed=1`" ]
         then
+          sleep 1
           break
         fi
       fi
@@ -239,7 +241,8 @@
 
 [ "USAGE: EXPECT_PROPERTY <prop> <value> [--allow_failure]
 
-Returns true if current return (regex) value is true and the result matches" ]
+Returns true (0) if current return (regex) value is true and the result matches
+and the incoming return value is true as well (wired-or)" ]
 EXPECT_PROPERTY() {
   save_ret=${?}
   property="${1}"
@@ -287,6 +290,7 @@
 bootstat: Battery level at shutdown 100%
 bootstat: Battery level at startup 100%
 init    : Parsing file /system/etc/init/bootstat.rc...
+init    : Parsing file /system/etc/init/bootstat-debug.rc...
 init    : processing action (persist.test.boot.reason=*) from (/system/etc/init/bootstat-debug.rc:
 init    : Command 'setprop ro.boot.bootreason \${persist.test.boot.reason}' action=persist.test.boot.reason=* (/system/etc/init/bootstat-debug.rc:
 init    : processing action (post-fs-data) from (/system/etc/init/bootstat.rc
@@ -566,6 +570,8 @@
   esac
   adb reboot ${TEST}
   wait_for_screen
+  bootloader_reason=`validate_property ro.boot.bootreason`
+  EXPECT_PROPERTY ro.boot.bootreason ${bootloader_reason}
   EXPECT_PROPERTY sys.boot.reason ${reason}
   EXPECT_PROPERTY persist.sys.boot.reason ${reason}
   report_bootstat_logs ${reason}
@@ -623,8 +629,13 @@
     adb reboot-bootloader
   fi
   fastboot format userdata >&2
+  save_ret=${?}
+  if [ 0 != ${save_ret} ]; then
+    echo "ERROR: fastboot can not format userdata" >&2
+  fi
   fastboot reboot >&2
   wait_for_screen
+  ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
   EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
   EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs reboot,factory_reset bootloader \
@@ -868,17 +879,30 @@
 - NB: should report reboot,its_just_so_hard
 - NB: expect log \"... I bootstat: Unknown boot reason: reboot,its_just_so_hard\"" ]
 test_Its_Just_So_Hard_reboot() {
-  duration_test
+  if isDebuggable; then       # see below
+    duration_test
+  else
+    duration_test `expr ${DURATION_DEFAULT} + ${DURATION_DEFAULT}`
+  fi
   adb shell 'reboot "Its Just So Hard"'
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
   EXPECT_PROPERTY persist.sys.boot.reason "reboot,Its Just So Hard"
-  adb shell su root setprop persist.sys.boot.reason reboot,its_just_so_hard
-  if checkDebugBuild; then
-    flag=""
+  # Do not leave this test with an illegal value in persist.sys.boot.reason
+  save_ret=${?}           # hold on to error code from above two lines
+  if isDebuggable; then   # can do this easy, or we can do this hard.
+    adb shell su root setprop persist.sys.boot.reason reboot,its_just_so_hard
+    ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
   else
-    flag="--allow_failure"
+    report_bootstat_logs reboot,its_just_so_hard  # report what we have so far
+    # user build mitigation
+    adb shell reboot its_just_so_hard
+    wait_for_screen
+    ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
+    EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
   fi
+  # Ensure persist.sys.boot.reason now valid, failure here acts as a signal
+  # that we could choke up following tests.  For example test_properties.
   EXPECT_PROPERTY persist.sys.boot.reason reboot,its_just_so_hard ${flag}
   report_bootstat_logs reboot,its_just_so_hard
 }
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index f8eaa1c..2d34e2d 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -212,6 +212,22 @@
     {"hard,hw_reset", 72},
     {"shutdown,suspend", 73},    // Suspend to RAM
     {"shutdown,hibernate", 74},  // Suspend to DISK
+    {"power_on_key", 75},
+    {"reboot_by_key", 76},
+    {"wdt_by_pass_pwk", 77},
+    {"reboot_longkey", 78},
+    {"powerkey", 79},
+    {"usb", 80},
+    {"wdt", 81},
+    {"tool_by_pass_pwk", 82},
+    {"2sec_reboot", 83},
+    {"reboot,by_key", 84},
+    {"reboot,longkey", 85},
+    {"reboot,2sec", 86},
+    {"shutdown,thermal,battery", 87},
+    {"reboot,its_just_so_hard", 88},  // produced by boot_reason_test
+    {"reboot,Its Just So Hard", 89},  // produced by boot_reason_test
+    {"usb", 90},
 };
 
 // Converts a string value representing the reason the system booted to an
@@ -313,7 +329,107 @@
   return android::base::ReadFileToString("/sys/fs/pstore/console-ramoops", &console);
 }
 
-bool addKernelPanicSubReason(const std::string& console, std::string& ret) {
+// Implement a variant of std::string::rfind that is resilient to errors in
+// the data stream being inspected.
+class pstoreConsole {
+ private:
+  const size_t kBitErrorRate = 8;  // number of bits per error
+  const std::string& console;
+
+  // Number of bits that differ between the two arguments l and r.
+  // Returns zero if the values for l and r are identical.
+  size_t numError(uint8_t l, uint8_t r) const { return std::bitset<8>(l ^ r).count(); }
+
+  // A string comparison function, reports the number of errors discovered
+  // in the match to a maximum of the bitLength / kBitErrorRate, at that
+  // point returning npos to indicate match is too poor.
+  //
+  // Since called in rfind which works backwards, expect cache locality will
+  // help if we check in reverse here as well for performance.
+  //
+  // Assumption: l (from console.c_str() + pos) is long enough to house
+  //             _r.length(), checked in rfind caller below.
+  //
+  size_t numError(size_t pos, const std::string& _r) const {
+    const char* l = console.c_str() + pos;
+    const char* r = _r.c_str();
+    size_t n = _r.length();
+    const uint8_t* le = reinterpret_cast<const uint8_t*>(l) + n;
+    const uint8_t* re = reinterpret_cast<const uint8_t*>(r) + n;
+    size_t count = 0;
+    n = 0;
+    do {
+      // individual character bit error rate > threshold + slop
+      size_t num = numError(*--le, *--re);
+      if (num > ((8 + kBitErrorRate) / kBitErrorRate)) return std::string::npos;
+      // total bit error rate > threshold + slop
+      count += num;
+      ++n;
+      if (count > ((n * 8 + kBitErrorRate - (n > 2)) / kBitErrorRate)) {
+        return std::string::npos;
+      }
+    } while (le != reinterpret_cast<const uint8_t*>(l));
+    return count;
+  }
+
+ public:
+  explicit pstoreConsole(const std::string& console) : console(console) {}
+  // scope of argument must be equal to or greater than scope of pstoreConsole
+  explicit pstoreConsole(const std::string&& console) = delete;
+  explicit pstoreConsole(std::string&& console) = delete;
+
+  // Our implementation of rfind, use exact match first, then resort to fuzzy.
+  size_t rfind(const std::string& needle) const {
+    size_t pos = console.rfind(needle);  // exact match?
+    if (pos != std::string::npos) return pos;
+
+    // Check to make sure needle fits in console string.
+    pos = console.length();
+    if (needle.length() > pos) return std::string::npos;
+    pos -= needle.length();
+    // fuzzy match to maximum kBitErrorRate
+    for (;;) {
+      if (numError(pos, needle) != std::string::npos) return pos;
+      if (pos == 0) break;
+      --pos;
+    }
+    return std::string::npos;
+  }
+
+  // Our implementation of find, use only fuzzy match.
+  size_t find(const std::string& needle, size_t start = 0) const {
+    // Check to make sure needle fits in console string.
+    if (needle.length() > console.length()) return std::string::npos;
+    const size_t last_pos = console.length() - needle.length();
+    // fuzzy match to maximum kBitErrorRate
+    for (size_t pos = start; pos <= last_pos; ++pos) {
+      if (numError(pos, needle) != std::string::npos) return pos;
+    }
+    return std::string::npos;
+  }
+};
+
+// If bit error match to needle, correct it.
+// Return true if any corrections were discovered and applied.
+bool correctForBer(std::string& reason, const std::string& needle) {
+  bool corrected = false;
+  if (reason.length() < needle.length()) return corrected;
+  const pstoreConsole console(reason);
+  const size_t last_pos = reason.length() - needle.length();
+  for (size_t pos = 0; pos <= last_pos; pos += needle.length()) {
+    pos = console.find(needle, pos);
+    if (pos == std::string::npos) break;
+
+    // exact match has no malice
+    if (needle == reason.substr(pos, needle.length())) continue;
+
+    corrected = true;
+    reason = reason.substr(0, pos) + needle + reason.substr(pos + needle.length());
+  }
+  return corrected;
+}
+
+bool addKernelPanicSubReason(const pstoreConsole& console, std::string& ret) {
   // Check for kernel panic types to refine information
   if (console.rfind("SysRq : Trigger a crash") != std::string::npos) {
     // Can not happen, except on userdebug, during testing/debugging.
@@ -332,16 +448,28 @@
   return false;
 }
 
+bool addKernelPanicSubReason(const std::string& content, std::string& ret) {
+  return addKernelPanicSubReason(pstoreConsole(content), ret);
+}
+
 // std::transform Helper callback functions:
 // Converts a string value representing the reason the system booted to a
 // string complying with Android system standard reason.
 char tounderline(char c) {
   return ::isblank(c) ? '_' : c;
 }
+
 char toprintable(char c) {
   return ::isprint(c) ? c : '?';
 }
 
+// Cleanup boot_reason regarding acceptable character set
+void transformReason(std::string& reason) {
+  std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);
+  std::transform(reason.begin(), reason.end(), reason.begin(), tounderline);
+  std::transform(reason.begin(), reason.end(), reason.begin(), toprintable);
+}
+
 const char system_reboot_reason_property[] = "sys.boot.reason";
 const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
 const char bootloader_reboot_reason_property[] = "ro.boot.bootreason";
@@ -355,10 +483,7 @@
   // If sys.boot.reason == ro.boot.bootreason, let's re-evaluate
   if (reason == ret) ret = "";
 
-  // Cleanup boot_reason regarding acceptable character set
-  std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);
-  std::transform(reason.begin(), reason.end(), reason.begin(), tounderline);
-  std::transform(reason.begin(), reason.end(), reason.begin(), toprintable);
+  transformReason(reason);
 
   // Is the current system boot reason sys.boot.reason valid?
   if (!isKnownRebootReason(ret)) ret = "";
@@ -395,6 +520,7 @@
         {"shutdown,thermal", "thermal"},
         {"warm,s3_wakeup", "s3_wakeup"},
         {"hard,hw_reset", "hw_reset"},
+        {"reboot,2sec", "2sec_reboot"},
         {"bootloader", ""},
     };
 
@@ -430,9 +556,10 @@
     // Check to see if last klog has some refinement hints.
     std::string content;
     if (readPstoreConsole(content)) {
+      const pstoreConsole console(content);
       // The toybox reboot command used directly (unlikely)? But also
       // catches init's response to Android's more controlled reboot command.
-      if (content.rfind("reboot: Power down") != std::string::npos) {
+      if (console.rfind("reboot: Power down") != std::string::npos) {
         ret = "shutdown";  // Still too blunt, but more accurate.
         // ToDo: init should record the shutdown reason to kernel messages ala:
         //           init: shutdown system with command 'last_reboot_reason'
@@ -441,32 +568,46 @@
       }
 
       static const char cmd[] = "reboot: Restarting system with command '";
-      size_t pos = content.rfind(cmd);
+      size_t pos = console.rfind(cmd);
       if (pos != std::string::npos) {
         pos += strlen(cmd);
         std::string subReason(content.substr(pos, max_reason_length));
+        // Correct against any known strings that Bit Error Match
+        for (const auto& s : knownReasons) {
+          correctForBer(subReason, s);
+        }
+        for (const auto& m : kBootReasonMap) {
+          if (m.first.length() <= strlen("cold")) continue;  // too short?
+          if (correctForBer(subReason, m.first + "'")) continue;
+          if (m.first.length() <= strlen("reboot,cold")) continue;  // short?
+          if (!android::base::StartsWith(m.first, "reboot,")) continue;
+          correctForBer(subReason, m.first.substr(strlen("reboot,")) + "'");
+        }
         for (pos = 0; pos < subReason.length(); ++pos) {
-          char c = tounderline(subReason[pos]);
-          if (!::isprint(c) || (c == '\'')) {
+          char c = subReason[pos];
+          // #, &, %, / are common single bit error for ' that we can block
+          if (!::isprint(c) || (c == '\'') || (c == '#') || (c == '&') || (c == '%') || (c == '/')) {
             subReason.erase(pos);
             break;
           }
-          subReason[pos] = ::tolower(c);
         }
+        transformReason(subReason);
         if (subReason != "") {  // Will not land "reboot" as that is too blunt.
           if (isKernelRebootReason(subReason)) {
             ret = "reboot," + subReason;  // User space can't talk kernel reasons.
-          } else {
+          } else if (isKnownRebootReason(subReason)) {
             ret = subReason;
+          } else {
+            ret = "reboot," + subReason;  // legitimize unknown reasons
           }
         }
       }
 
       // Check for kernel panics, allowed to override reboot command.
-      if (!addKernelPanicSubReason(content, ret) &&
+      if (!addKernelPanicSubReason(console, ret) &&
           // check for long-press power down
-          ((content.rfind("Power held for ") != std::string::npos) ||
-           (content.rfind("charger: [") != std::string::npos))) {
+          ((console.rfind("Power held for ") != std::string::npos) ||
+           (console.rfind("charger: [") != std::string::npos))) {
         ret = "cold";
       }
     }
@@ -482,14 +623,33 @@
       // Really a hail-mary pass to find it in last klog content ...
       static const int battery_dead_threshold = 2;  // percent
       static const char battery[] = "healthd: battery l=";
-      size_t pos = content.rfind(battery);  // last one
+      const pstoreConsole console(content);
+      size_t pos = console.rfind(battery);  // last one
       std::string digits;
       if (pos != std::string::npos) {
-        digits = content.substr(pos + strlen(battery));
+        digits = content.substr(pos + strlen(battery), strlen("100 "));
+        // correct common errors
+        correctForBer(digits, "100 ");
+        if (digits[0] == '!') digits[0] = '1';
+        if (digits[1] == '!') digits[1] = '1';
       }
-      char* endptr = NULL;
-      unsigned long long level = strtoull(digits.c_str(), &endptr, 10);
-      if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) {
+      const char* endptr = digits.c_str();
+      unsigned level = 0;
+      while (::isdigit(*endptr)) {
+        level *= 10;
+        level += *endptr++ - '0';
+        // make sure no leading zeros, except zero itself, and range check.
+        if ((level == 0) || (level > 100)) break;
+      }
+      // example bit error rate issues for 10%
+      //   'l=10 ' no bits in error
+      //   'l=00 ' single bit error (fails above)
+      //   'l=1  ' single bit error
+      //   'l=0  ' double bit error
+      // There are others, not typically critical because of 2%
+      // battery_dead_threshold. KISS check, make sure second
+      // character after digit sequence is not a space.
+      if ((level <= 100) && (endptr != digits.c_str()) && (endptr[0] == ' ') && (endptr[1] != ' ')) {
         LOG(INFO) << "Battery level at shutdown " << level << "%";
         if (level <= battery_dead_threshold) {
           ret = "shutdown,battery";
@@ -508,7 +668,6 @@
           android::base::ReadFdToString(fileno(fp), &content);
         }
         android_logcat_pclose(&ctx, fp);
-        android_logcat_destroy(&ctx);
         static const char logcat_battery[] = "W/healthd (    0): battery l=";
         const char* match = logcat_battery;
 
@@ -530,10 +689,16 @@
 
         pos = content.find(match);  // The first one it finds.
         if (pos != std::string::npos) {
-          digits = content.substr(pos + strlen(match));
+          digits = content.substr(pos + strlen(match), strlen("100 "));
         }
-        endptr = NULL;
-        level = strtoull(digits.c_str(), &endptr, 10);
+        endptr = digits.c_str();
+        level = 0;
+        while (::isdigit(*endptr)) {
+          level *= 10;
+          level += *endptr++ - '0';
+          // make sure no leading zeros, except zero itself, and range check.
+          if ((level == 0) || (level > 100)) break;
+        }
         if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) {
           LOG(INFO) << "Battery level at startup " << level << "%";
           if (level <= battery_dead_threshold) {
@@ -550,10 +715,7 @@
       // Content buffer no longer will have console data. Beware if more
       // checks added below, that depend on parsing console content.
       content = GetProperty(last_reboot_reason_property);
-      // Cleanup last_boot_reason regarding acceptable character set
-      std::transform(content.begin(), content.end(), content.begin(), ::tolower);
-      std::transform(content.begin(), content.end(), content.begin(), tounderline);
-      std::transform(content.begin(), content.end(), content.begin(), toprintable);
+      transformReason(content);
 
       // Anything in last is better than 'super-blunt' reboot or shutdown.
       if ((ret == "") || (ret == "reboot") || (ret == "shutdown") || !isBluntRebootReason(content)) {
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 2b5f4f6..17a9f3a 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -193,7 +193,6 @@
         "libdebuggerd/test/elf_fake.cpp",
         "libdebuggerd/test/log_fake.cpp",
         "libdebuggerd/test/open_files_list_test.cpp",
-        "libdebuggerd/test/property_fake.cpp",
         "libdebuggerd/test/ptrace_fake.cpp",
         "libdebuggerd/test/tombstone_test.cpp",
     ],
diff --git a/debuggerd/NOTICE b/debuggerd/NOTICE
deleted file mode 100644
index c5b1efa..0000000
--- a/debuggerd/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
-   Copyright (c) 2005-2008, 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.
-
-   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.
-
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 6ef3ed6..827420e 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -348,11 +348,6 @@
       LOG(FATAL) << "failed to create backtrace map";
     }
   }
-  std::unique_ptr<BacktraceMap> backtrace_map_new;
-  backtrace_map_new.reset(BacktraceMap::CreateNew(main_tid));
-  if (!backtrace_map_new) {
-    LOG(FATAL) << "failed to create backtrace map new";
-  }
 
   // Collect the list of open files.
   OpenFilesList open_files;
@@ -432,9 +427,8 @@
     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(), backtrace_map_new.get(), &open_files,
-                      target, main_tid, process_name, threads, abort_address,
-                      fatal_signal ? &amfd_data : nullptr);
+    engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid,
+                      process_name, threads, abort_address, fatal_signal ? &amfd_data : nullptr);
   }
 
   // We don't actually need to PTRACE_DETACH, as long as our tracees aren't in
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index 67b4ab7..7bec470 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -1,7 +1,7 @@
 cc_defaults {
     name: "crasher-defaults",
 
-    cppflags: [
+    cflags: [
         "-W",
         "-Wall",
         "-Wextra",
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 45e768d..8d0c98b 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -16,6 +16,7 @@
 
 #include <err.h>
 #include <fcntl.h>
+#include <stdlib.h>
 #include <sys/capability.h>
 #include <sys/prctl.h>
 #include <sys/ptrace.h>
@@ -298,6 +299,26 @@
   ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
 }
 
+TEST_F(CrasherTest, LD_PRELOAD) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    setenv("LD_PRELOAD", "nonexistent.so", 1);
+    *reinterpret_cast<volatile char*>(0xdead) = '1';
+  });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
+}
+
 TEST_F(CrasherTest, abort) {
   int intercept_result;
   unique_fd output_fd;
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index d41dc67..bd202ff 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -330,8 +330,8 @@
     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, debuggerd_dump_type,
-          nullptr);
+    execle(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
+           nullptr, nullptr);
 
     fatal_errno("exec failed");
   } else {
@@ -429,7 +429,12 @@
     abort_message = g_callbacks.get_abort_message();
   }
 
-  if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
+  // If sival_int is ~0, it means that the fallback handler has been called
+  // once before and this function is being called again to dump the stack
+  // of a specific thread. It is possible that the prctl call might return 1,
+  // then return 0 in subsequent calls, so check the sival_int to determine if
+  // the fallback handler should be called first.
+  if (info->si_value.sival_int == ~0 || prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
     // This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely,
     // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing
     // ANR trace.
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 45740df..79743b6 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -35,10 +35,10 @@
 int open_tombstone(std::string* path);
 
 /* Creates a tombstone file and writes the crash dump to it. */
-void engrave_tombstone(int tombstone_fd, BacktraceMap* map, BacktraceMap* map_new,
-                       const OpenFilesList* open_files, pid_t pid, pid_t tid,
-                       const std::string& process_name, const std::map<pid_t, std::string>& threads,
-                       uintptr_t abort_msg_address, std::string* amfd_data);
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files,
+                       pid_t pid, pid_t tid, const std::string& process_name,
+                       const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address,
+                       std::string* amfd_data);
 
 void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
                                 ucontext_t* ucontext);
diff --git a/debuggerd/libdebuggerd/test/property_fake.cpp b/debuggerd/libdebuggerd/test/property_fake.cpp
deleted file mode 100644
index 02069f1..0000000
--- a/debuggerd/libdebuggerd/test/property_fake.cpp
+++ /dev/null
@@ -1,45 +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 <string.h>
-
-#include <string>
-#include <unordered_map>
-
-#include <sys/system_properties.h>
-
-std::unordered_map<std::string, std::string> g_properties;
-
-extern "C" int property_set(const char* name, const char* value) {
-  if (g_properties.count(name) != 0) {
-    g_properties.erase(name);
-  }
-  g_properties[name] = value;
-  return 0;
-}
-
-extern "C" int property_get(const char* key, char* value, const char* default_value) {
-  if (g_properties.count(key) == 0) {
-    if (default_value == nullptr) {
-      return 0;
-    }
-    strncpy(value, default_value, PROP_VALUE_MAX-1);
-  } else {
-    strncpy(value, g_properties[key].c_str(), PROP_VALUE_MAX-1);
-  }
-  value[PROP_VALUE_MAX-1] = '\0';
-  return strlen(value);
-}
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 934ceba..59a43b7 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -19,8 +19,9 @@
 #include <memory>
 #include <string>
 
-#include <gtest/gtest.h>
 #include <android-base/file.h>
+#include <android-base/properties.h>
+#include <gtest/gtest.h>
 
 #include "libdebuggerd/utility.h"
 
@@ -639,7 +640,10 @@
 TEST_F(TombstoneTest, dump_header_info) {
   dump_header_info(&log_);
 
-  std::string expected = "Build fingerprint: 'unknown'\nRevision: 'unknown'\n";
+  std::string expected = android::base::StringPrintf(
+      "Build fingerprint: '%s'\nRevision: '%s'\n",
+      android::base::GetProperty("ro.build.fingerprint", "unknown").c_str(),
+      android::base::GetProperty("ro.revision", "unknown").c_str());
   expected += android::base::StringPrintf("ABI: '%s'\n", ABI_STRING);
   ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
 }
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 6fb29a9..725c42c 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -496,55 +496,8 @@
   _LOG(log, logtype::REGISTERS, "    register dumping unimplemented on this architecture");
 }
 
-static bool verify_backtraces_equal(Backtrace* back1, Backtrace* back2) {
-  if (back1->NumFrames() != back2->NumFrames()) {
-    return false;
-  }
-  std::string back1_str;
-  std::string back2_str;
-  for (size_t i = 0; i < back1->NumFrames(); i++) {
-    back1_str += back1->FormatFrameData(i);
-    back2_str += back2->FormatFrameData(i);
-  }
-  return back1_str == back2_str;
-}
-
-static void log_mismatch_data(log_t* log, Backtrace* backtrace) {
-  _LOG(log, logtype::THREAD, "MISMATCH: This unwind is different.\n");
-  if (backtrace->NumFrames() == 0) {
-    _LOG(log, logtype::THREAD, "MISMATCH: No frames in new backtrace.\n");
-    return;
-  }
-  _LOG(log, logtype::THREAD, "MISMATCH: Backtrace from new unwinder.\n");
-  for (size_t i = 0; i < backtrace->NumFrames(); i++) {
-    _LOG(log, logtype::THREAD, "MISMATCH: %s\n", backtrace->FormatFrameData(i).c_str());
-  }
-
-  // Get the stack trace up to 8192 bytes.
-  std::vector<uint64_t> buffer(8192 / sizeof(uint64_t));
-  size_t bytes =
-      backtrace->Read(backtrace->GetFrame(0)->sp, reinterpret_cast<uint8_t*>(buffer.data()),
-                      buffer.size() * sizeof(uint64_t));
-  std::string log_data;
-  for (size_t i = 0; i < bytes / sizeof(uint64_t); i++) {
-    if ((i % 4) == 0) {
-      if (!log_data.empty()) {
-        _LOG(log, logtype::THREAD, "MISMATCH: stack_data%s\n", log_data.c_str());
-        log_data = "";
-      }
-    }
-    log_data += android::base::StringPrintf(" 0x%016" PRIx64, buffer[i]);
-  }
-
-  if (!log_data.empty()) {
-    _LOG(log, logtype::THREAD, "MISMATCH: data%s\n", log_data.c_str());
-  }
-
-  // If there is any leftover (bytes % sizeof(uint64_t) != 0, ignore it for now.
-}
-
-static bool dump_thread(log_t* log, pid_t pid, pid_t tid, const std::string& process_name,
-                        const std::string& thread_name, BacktraceMap* map, BacktraceMap* map_new,
+static void dump_thread(log_t* log, pid_t pid, pid_t tid, const std::string& process_name,
+                        const std::string& thread_name, BacktraceMap* map,
                         uintptr_t abort_msg_address, bool primary_thread) {
   log->current_tid = tid;
   if (!primary_thread) {
@@ -558,18 +511,7 @@
     dump_abort_message(backtrace.get(), log, abort_msg_address);
   }
   dump_registers(log, tid);
-  bool matches = true;
   if (backtrace->Unwind(0)) {
-    // Use the new method and verify it is the same as old.
-    std::unique_ptr<Backtrace> backtrace_new(Backtrace::CreateNew(pid, tid, map_new));
-    if (!backtrace_new->Unwind(0)) {
-      _LOG(log, logtype::THREAD, "Failed to unwind with new unwinder: %s\n",
-           backtrace_new->GetErrorString(backtrace_new->GetError()).c_str());
-      matches = false;
-    } else if (!verify_backtraces_equal(backtrace.get(), backtrace_new.get())) {
-      log_mismatch_data(log, backtrace_new.get());
-      matches = false;
-    }
     dump_backtrace_and_stack(backtrace.get(), log);
   } else {
     ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
@@ -583,8 +525,6 @@
   }
 
   log->current_tid = log->crashed_tid;
-
-  return matches;
 }
 
 // Reads the contents of the specified log device, filters out the entries
@@ -718,18 +658,16 @@
 }
 
 // Dumps all information about the specified pid to the tombstone.
-static void dump_crash(log_t* log, BacktraceMap* map, BacktraceMap* map_new,
-                       const OpenFilesList* open_files, pid_t pid, pid_t tid,
-                       const std::string& process_name, const std::map<pid_t, std::string>& threads,
-                       uintptr_t abort_msg_address) {
+static void dump_crash(log_t* log, BacktraceMap* map, const OpenFilesList* open_files, pid_t pid,
+                       pid_t tid, const std::string& process_name,
+                       const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address) {
   // don't copy log messages to tombstone unless this is a dev device
   bool want_logs = GetBoolProperty("ro.debuggable", false);
 
   _LOG(log, logtype::HEADER,
        "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
   dump_header_info(log);
-  bool new_unwind_matches = dump_thread(log, pid, tid, process_name, threads.find(tid)->second, map,
-                                        map_new, abort_msg_address, true);
+  dump_thread(log, pid, tid, process_name, threads.find(tid)->second, map, abort_msg_address, true);
   if (want_logs) {
     dump_logs(log, pid, 5);
   }
@@ -739,9 +677,7 @@
     const std::string& thread_name = it.second;
 
     if (thread_tid != tid) {
-      bool match =
-          dump_thread(log, pid, thread_tid, process_name, thread_name, map, map_new, 0, false);
-      new_unwind_matches = new_unwind_matches && match;
+      dump_thread(log, pid, thread_tid, process_name, thread_name, map, 0, false);
     }
   }
 
@@ -753,26 +689,18 @@
   if (want_logs) {
     dump_logs(log, pid, 0);
   }
-  if (!new_unwind_matches) {
-    _LOG(log, logtype::THREAD, "MISMATCH: New and old unwinder do not agree.\n");
-    _LOG(log, logtype::THREAD, "MISMATCH: If you see this please file a bug in:\n");
-    _LOG(log, logtype::THREAD,
-         "MISMATCH: Android > Android OS & Apps > Runtime > native > tools "
-         "(debuggerd/gdb/init/simpleperf/strace/valgrind)\n");
-    _LOG(log, logtype::THREAD, "MISMATCH: and attach this tombstone.\n");
-  }
 }
 
-void engrave_tombstone(int tombstone_fd, BacktraceMap* map, BacktraceMap* map_new,
-                       const OpenFilesList* open_files, pid_t pid, pid_t tid,
-                       const std::string& process_name, const std::map<pid_t, std::string>& threads,
-                       uintptr_t abort_msg_address, std::string* amfd_data) {
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files,
+                       pid_t pid, pid_t tid, const std::string& process_name,
+                       const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address,
+                       std::string* amfd_data) {
   log_t log;
   log.current_tid = tid;
   log.crashed_tid = tid;
   log.tfd = tombstone_fd;
   log.amfd_data = amfd_data;
-  dump_crash(&log, map, map_new, open_files, pid, tid, process_name, threads, abort_msg_address);
+  dump_crash(&log, map, open_files, pid, tid, process_name, threads, abort_msg_address);
 }
 
 void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
diff --git a/debuggerd/signal_sender.cpp b/debuggerd/signal_sender.cpp
deleted file mode 100644
index 42a8e77..0000000
--- a/debuggerd/signal_sender.cpp
+++ /dev/null
@@ -1,183 +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.
- */
-
-#define LOG_TAG "debuggerd-signal"
-
-#include <errno.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include "signal_sender.h"
-
-static int signal_fd = -1;
-static pid_t signal_pid;
-struct signal_message {
-  pid_t pid;
-  pid_t tid;
-  int signal;
-};
-
-static void set_signal_sender_process_name() {
-#if defined(__LP64__)
-  static constexpr char long_process_name[] = "debuggerd64:signaller";
-  static constexpr char short_process_name[] = "debuggerd64:sig";
-  static_assert(sizeof(long_process_name) <= sizeof("/system/bin/debuggerd64"), "");
-#else
-  static constexpr char long_process_name[] = "debuggerd:signaller";
-  static constexpr char short_process_name[] = "debuggerd:sig";
-  static_assert(sizeof(long_process_name) <= sizeof("/system/bin/debuggerd"), "");
-#endif
-
-  // pthread_setname_np has a maximum length of 16 chars, including null terminator.
-  static_assert(sizeof(short_process_name) <= 16, "");
-  pthread_setname_np(pthread_self(), short_process_name);
-
-  char* progname = const_cast<char*>(getprogname());
-  if (strlen(progname) <= strlen(long_process_name)) {
-    ALOGE("debuggerd: unexpected progname %s", progname);
-    return;
-  }
-
-  memset(progname, 0, strlen(progname));
-  strcpy(progname, long_process_name);
-}
-
-// Fork a process to send signals for the worker processes to use after they've dropped privileges.
-bool start_signal_sender() {
-  if (signal_pid != 0) {
-    ALOGE("debuggerd: attempted to start signal sender multiple times");
-    return false;
-  }
-
-  int sfd[2];
-  if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sfd) != 0) {
-    ALOGE("debuggerd: failed to create socketpair for signal sender: %s", strerror(errno));
-    return false;
-  }
-
-  pid_t parent = getpid();
-  pid_t fork_pid = fork();
-  if (fork_pid == -1) {
-    ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno));
-    return false;
-  } else if (fork_pid == 0) {
-    close(sfd[1]);
-
-    set_signal_sender_process_name();
-
-    while (true) {
-      signal_message msg;
-      int rc = TEMP_FAILURE_RETRY(read(sfd[0], &msg, sizeof(msg)));
-      if (rc < 0) {
-        ALOGE("debuggerd: signal sender failed to read from socket");
-        break;
-      } else if (rc != sizeof(msg)) {
-        ALOGE("debuggerd: signal sender read unexpected number of bytes: %d", rc);
-        break;
-      }
-
-      // Report success after sending a signal
-      int err = 0;
-      if (msg.tid > 0) {
-        if (syscall(SYS_tgkill, msg.pid, msg.tid, msg.signal) != 0) {
-          err = errno;
-        }
-      } else {
-        if (kill(msg.pid, msg.signal) != 0) {
-          err = errno;
-        }
-      }
-
-      if (TEMP_FAILURE_RETRY(write(sfd[0], &err, sizeof(err))) < 0) {
-        ALOGE("debuggerd: signal sender failed to write: %s", strerror(errno));
-      }
-    }
-
-    // Our parent proably died, but if not, kill them.
-    if (getppid() == parent) {
-      kill(parent, SIGKILL);
-    }
-    _exit(1);
-  } else {
-    close(sfd[0]);
-    signal_fd = sfd[1];
-    signal_pid = fork_pid;
-    return true;
-  }
-}
-
-bool stop_signal_sender() {
-  if (signal_pid <= 0) {
-    return false;
-  }
-
-  if (kill(signal_pid, SIGKILL) != 0) {
-    ALOGE("debuggerd: failed to kill signal sender: %s", strerror(errno));
-    return false;
-  }
-
-  close(signal_fd);
-  signal_fd = -1;
-
-  int status;
-  waitpid(signal_pid, &status, 0);
-  signal_pid = 0;
-
-  return true;
-}
-
-bool send_signal(pid_t pid, pid_t tid, int signal) {
-  if (signal_fd == -1) {
-    ALOGE("debuggerd: attempted to send signal before signal sender was started");
-    errno = EHOSTUNREACH;
-    return false;
-  }
-
-  signal_message msg = {.pid = pid, .tid = tid, .signal = signal };
-  if (TEMP_FAILURE_RETRY(write(signal_fd, &msg, sizeof(msg))) < 0) {
-    ALOGE("debuggerd: failed to send message to signal sender: %s", strerror(errno));
-    errno = EHOSTUNREACH;
-    return false;
-  }
-
-  int response;
-  ssize_t rc = TEMP_FAILURE_RETRY(read(signal_fd, &response, sizeof(response)));
-  if (rc == 0) {
-    ALOGE("debuggerd: received EOF from signal sender");
-    errno = EHOSTUNREACH;
-    return false;
-  } else if (rc < 0) {
-    ALOGE("debuggerd: failed to receive response from signal sender: %s", strerror(errno));
-    errno = EHOSTUNREACH;
-    return false;
-  }
-
-  if (response == 0) {
-    return true;
-  }
-
-  errno = response;
-  return false;
-}
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
index c93e2ab..46a6f76 100644
--- a/demangle/DemangleTest.cpp
+++ b/demangle/DemangleTest.cpp
@@ -334,6 +334,29 @@
   // Template within templates.
   ASSERT_EQ("one::two<three<char, int>>", demangler.Parse("_ZN3one3twoIN5threeIciEEEE"));
   ASSERT_EQ("one::two<three<char, four<int>>>", demangler.Parse("_ZN3one3twoIN5threeIcN4fourIiEEEEEE"));
+
+  ASSERT_EQ("one<char>", demangler.Parse("_Z3oneIcE"));
+  ASSERT_EQ("one<void>", demangler.Parse("_Z3oneIvE"));
+  ASSERT_EQ("one<void*>", demangler.Parse("_Z3oneIPvE"));
+  ASSERT_EQ("one<void const>", demangler.Parse("_Z3oneIKvE"));
+  ASSERT_EQ("one<char, int, bool>", demangler.Parse("_Z3oneIcibE"));
+  ASSERT_EQ("one(two<three>)", demangler.Parse("_Z3one3twoIN5threeEE"));
+  ASSERT_EQ("one<char, int, two::three>", demangler.Parse("_Z3oneIciN3two5threeEE"));
+  // Template within templates.
+  ASSERT_EQ("one(two<three<char, int>>)", demangler.Parse("_Z3one3twoIN5threeIciEEE"));
+  ASSERT_EQ("one(two<three<char, four<int>>>)",
+            demangler.Parse("_Z3one3twoIN5threeIcN4fourIiEEEEE"));
+}
+
+TEST(DemangleTest, TemplateFunctionWithReturnType) {
+  Demangler demangler;
+
+  ASSERT_EQ("char one<int>(char)", demangler.Parse("_Z3oneIiEcc"));
+  ASSERT_EQ("void one<int>()", demangler.Parse("_Z3oneIiEvv"));
+  ASSERT_EQ("char one<int>()", demangler.Parse("_Z3oneIiEcv"));
+  ASSERT_EQ("char one<int>(void, void)", demangler.Parse("_Z3oneIiEcvv"));
+  ASSERT_EQ("char one<int>()", demangler.Parse("_ZN3oneIiEEcv"));
+  ASSERT_EQ("char one<int>(void, void)", demangler.Parse("_ZN3oneIiEEcvv"));
 }
 
 TEST(DemangleTest, TemplateArguments) {
@@ -410,6 +433,28 @@
             demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS3_"));
 }
 
+TEST(DemangleTest, TemplateSubstitution) {
+  Demangler demangler;
+
+  ASSERT_EQ("void one<int, double>(int)", demangler.Parse("_ZN3oneIidEEvT_"));
+  ASSERT_EQ("void one<int, double>(double)", demangler.Parse("_ZN3oneIidEEvT0_"));
+  ASSERT_EQ("void one<int, double, char, void>(char)", demangler.Parse("_ZN3oneIidcvEEvT1_"));
+
+  ASSERT_EQ("void one<int, double>(int)", demangler.Parse("_Z3oneIidEvT_"));
+  ASSERT_EQ("void one<int, double>(double)", demangler.Parse("_Z3oneIidEvT0_"));
+  ASSERT_EQ("void one<int, double, char, void>(char)", demangler.Parse("_Z3oneIidcvEvT1_"));
+
+  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(l)",
+            demangler.Parse("_ZN3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEEvT10_"));
+  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(m)",
+            demangler.Parse("_ZN3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEEvT11_"));
+
+  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(l)",
+            demangler.Parse("_Z3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEvT10_"));
+  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(m)",
+            demangler.Parse("_Z3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEvT11_"));
+}
+
 TEST(DemangleTest, StringTooLong) {
   Demangler demangler;
 
@@ -434,6 +479,34 @@
   ASSERT_EQ("one<true>", demangler.Parse("_ZN3oneILb1EEE"));
   ASSERT_EQ("one<false>", demangler.Parse("_ZN3oneILb0EEE"));
   ASSERT_EQ("one<false, true>", demangler.Parse("_ZN3oneILb0ELb1EEE"));
+
+  ASSERT_EQ("one<true>", demangler.Parse("_Z3oneILb1EE"));
+  ASSERT_EQ("one<false>", demangler.Parse("_Z3oneILb0EE"));
+  ASSERT_EQ("one<false, true>", demangler.Parse("_Z3oneILb0ELb1EE"));
+
+  ASSERT_EQ("one(two<three<four>, false, true>)",
+            demangler.Parse("_ZN3oneE3twoI5threeI4fourELb0ELb1EE"));
+}
+
+TEST(DemangleTest, non_virtual_thunk) {
+  Demangler demangler;
+
+  ASSERT_EQ("non-virtual thunk to one", demangler.Parse("_ZThn0_N3oneE"));
+  ASSERT_EQ("non-virtual thunk to two", demangler.Parse("_ZThn0_3two"));
+  ASSERT_EQ("non-virtual thunk to three", demangler.Parse("_ZTh0_5three"));
+  ASSERT_EQ("non-virtual thunk to four", demangler.Parse("_ZTh_4four"));
+  ASSERT_EQ("non-virtual thunk to five", demangler.Parse("_ZTh0123456789_4five"));
+  ASSERT_EQ("non-virtual thunk to six", demangler.Parse("_ZThn0123456789_3six"));
+
+  ASSERT_EQ("_ZThn0N3oneE", demangler.Parse("_ZThn0N3oneE"));
+  ASSERT_EQ("_ZThn03two", demangler.Parse("_ZThn03two"));
+  ASSERT_EQ("_ZTh05three", demangler.Parse("_ZTh05three"));
+  ASSERT_EQ("_ZTh4four", demangler.Parse("_ZTh4four"));
+  ASSERT_EQ("_ZTh01234567894five", demangler.Parse("_ZTh01234567894five"));
+  ASSERT_EQ("_ZThn01234567893six", demangler.Parse("_ZThn01234567893six"));
+  ASSERT_EQ("_ZT_N3oneE", demangler.Parse("_ZT_N3oneE"));
+  ASSERT_EQ("_ZT0_N3oneE", demangler.Parse("_ZT0_N3oneE"));
+  ASSERT_EQ("_ZTH_N3oneE", demangler.Parse("_ZTH_N3oneE"));
 }
 
 TEST(DemangleTest, demangle) {
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
index f148b21..af2816c 100644
--- a/demangle/Demangler.cpp
+++ b/demangle/Demangler.cpp
@@ -347,6 +347,33 @@
   return name + 1;
 }
 
+const char* Demangler::ParseT(const char* name) {
+  if (template_saves_.empty()) {
+    return nullptr;
+  }
+
+  if (*name == '_') {
+    last_save_name_ = false;
+    AppendCurrent(template_saves_[0]);
+    return name + 1;
+  }
+
+  // Need to get the total number.
+  char* end;
+  unsigned long int index = strtoul(name, &end, 10) + 1;
+  if (name == end || *end != '_') {
+    return nullptr;
+  }
+
+  if (index >= template_saves_.size()) {
+    return nullptr;
+  }
+
+  last_save_name_ = false;
+  AppendCurrent(template_saves_[index]);
+  return end + 1;
+}
+
 const char* Demangler::ParseFunctionName(const char* name) {
   if (*name == 'E') {
     if (parse_funcs_.empty()) {
@@ -361,7 +388,7 @@
       saves_.pop_back();
     }
 
-    function_name_ = cur_state_.str;
+    function_name_ += cur_state_.str;
     while (!cur_state_.suffixes.empty()) {
       function_suffix_ += cur_state_.suffixes.back();
       cur_state_.suffixes.pop_back();
@@ -371,9 +398,28 @@
     return name + 1;
   }
 
+  if (*name == 'I') {
+    state_stack_.push(cur_state_);
+    cur_state_.Clear();
+
+    parse_funcs_.push_back(parse_func_);
+    parse_func_ = &Demangler::ParseFunctionNameTemplate;
+    return name + 1;
+  }
+
   return ParseComplexString(name);
 }
 
+const char* Demangler::ParseFunctionNameTemplate(const char* name) {
+  if (*name == 'E' && name[1] == 'E') {
+    // Only consider this a template with saves if it is right before
+    // the end of the name.
+    template_found_ = true;
+    template_saves_ = cur_state_.args;
+  }
+  return ParseTemplateArgumentsComplex(name);
+}
+
 const char* Demangler::ParseComplexArgument(const char* name) {
   if (*name == 'E') {
     if (parse_funcs_.empty()) {
@@ -690,6 +736,7 @@
     }
     parse_func_ = parse_funcs_.back();
     parse_funcs_.pop_back();
+
     FinalizeTemplate();
     Save(cur_state_.str, false);
     return name + 1;
@@ -699,6 +746,7 @@
     parse_func_ = &Demangler::ParseTemplateLiteral;
     return name + 1;
   }
+
   return ParseArguments(name);
 }
 
@@ -713,13 +761,57 @@
     AppendArgument(cur_state_.str);
     cur_state_.str.clear();
     return name + 1;
+  } else if (*name == 'L') {
+    // Literal value for a template.
+    parse_funcs_.push_back(parse_func_);
+    parse_func_ = &Demangler::ParseTemplateLiteral;
+    return name + 1;
   }
+
   return ParseArguments(name);
 }
 
+const char* Demangler::ParseFunctionTemplateArguments(const char* name) {
+  if (*name == 'E') {
+    parse_func_ = parse_funcs_.back();
+    parse_funcs_.pop_back();
+
+    function_name_ += '<' + GetArgumentsString() + '>';
+    template_found_ = true;
+    template_saves_ = cur_state_.args;
+    cur_state_.Clear();
+    return name + 1;
+  }
+  return ParseTemplateArgumentsComplex(name);
+}
+
 const char* Demangler::FindFunctionName(const char* name) {
+  if (*name == 'T') {
+    // non-virtual thunk, verify that it matches one of these patterns:
+    //   Thn[0-9]+_
+    //   Th[0-9]+_
+    //   Thn_
+    //   Th_
+    name++;
+    if (*name != 'h') {
+      return nullptr;
+    }
+    name++;
+    if (*name == 'n') {
+      name++;
+    }
+    while (std::isdigit(*name)) {
+      name++;
+    }
+    if (*name != '_') {
+      return nullptr;
+    }
+    function_name_ = "non-virtual thunk to ";
+    return name + 1;
+  }
+
   if (*name == 'N') {
-    parse_funcs_.push_back(&Demangler::ParseArguments);
+    parse_funcs_.push_back(&Demangler::ParseArgumentsAtTopLevel);
     parse_func_ = &Demangler::ParseFunctionName;
     return name + 1;
   }
@@ -732,11 +824,35 @@
     name = AppendOperatorString(name);
     function_name_ = cur_state_.str;
   }
-  parse_func_ = &Demangler::ParseArguments;
   cur_state_.Clear();
+
+  // Check for a template argument, which will still be part of the function
+  // name.
+  if (name != nullptr && *name == 'I') {
+    parse_funcs_.push_back(&Demangler::ParseArgumentsAtTopLevel);
+    parse_func_ = &Demangler::ParseFunctionTemplateArguments;
+    return name + 1;
+  }
+  parse_func_ = &Demangler::ParseArgumentsAtTopLevel;
   return name;
 }
 
+const char* Demangler::ParseArgumentsAtTopLevel(const char* name) {
+  // At the top level is the only place where T is allowed.
+  if (*name == 'T') {
+    name++;
+    name = ParseT(name);
+    if (name == nullptr) {
+      return nullptr;
+    }
+    AppendArgument(cur_state_.str);
+    cur_state_.str.clear();
+    return name;
+  }
+
+  return Demangler::ParseArguments(name);
+}
+
 std::string Demangler::Parse(const char* name, size_t max_length) {
   if (name[0] == '\0' || name[0] != '_' || name[1] == '\0' || name[1] != 'Z') {
     // Name is not mangled.
@@ -757,6 +873,21 @@
     return name;
   }
 
+  std::string return_type;
+  if (template_found_) {
+    // Only a single argument with a template is not allowed.
+    if (cur_state_.args.size() == 1) {
+      return name;
+    }
+
+    // If there are at least two arguments, this template has a return type.
+    if (cur_state_.args.size() > 1) {
+      // The first argument will be the return value.
+      return_type = cur_state_.args[0] + ' ';
+      cur_state_.args.erase(cur_state_.args.begin());
+    }
+  }
+
   std::string arg_str;
   if (cur_state_.args.size() == 1 && cur_state_.args[0] == "void") {
     // If the only argument is void, then don't print any args.
@@ -767,7 +898,7 @@
       arg_str = '(' + arg_str + ')';
     }
   }
-  return function_name_ + arg_str + function_suffix_;
+  return return_type + function_name_ + arg_str + function_suffix_;
 }
 
 std::string demangle(const char* name) {
diff --git a/demangle/Demangler.h b/demangle/Demangler.h
index f76def6..3b7d44e 100644
--- a/demangle/Demangler.h
+++ b/demangle/Demangler.h
@@ -39,6 +39,7 @@
   std::string GetArgumentsString();
   void FinalizeTemplate();
   const char* ParseS(const char* name);
+  const char* ParseT(const char* name);
   const char* AppendOperatorString(const char* name);
   void Save(const std::string& str, bool is_name);
 
@@ -50,17 +51,21 @@
     first_save_.clear();
     cur_state_.Clear();
     saves_.clear();
+    template_saves_.clear();
     while (!state_stack_.empty()) {
       state_stack_.pop();
     }
     last_save_name_ = false;
+    template_found_ = false;
   }
 
   using parse_func_type = const char* (Demangler::*)(const char*);
   parse_func_type parse_func_;
   std::vector<parse_func_type> parse_funcs_;
   std::vector<std::string> saves_;
+  std::vector<std::string> template_saves_;
   bool last_save_name_;
+  bool template_found_;
 
   std::string function_name_;
   std::string function_suffix_;
@@ -89,12 +94,15 @@
   // Parsing functions.
   const char* ParseComplexString(const char* name);
   const char* ParseComplexArgument(const char* name);
+  const char* ParseArgumentsAtTopLevel(const char* name);
   const char* ParseArguments(const char* name);
   const char* ParseTemplateArguments(const char* name);
   const char* ParseTemplateArgumentsComplex(const char* name);
   const char* ParseTemplateLiteral(const char* name);
   const char* ParseFunctionArgument(const char* name);
   const char* ParseFunctionName(const char* name);
+  const char* ParseFunctionNameTemplate(const char* name);
+  const char* ParseFunctionTemplateArguments(const char* name);
   const char* FindFunctionName(const char* name);
   const char* Fail(const char*) { return nullptr; }
 
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index dd8bad9..7723ec6 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -23,7 +23,6 @@
 LOCAL_C_INCLUDES := \
   $(LOCAL_PATH)/../adb \
   $(LOCAL_PATH)/../mkbootimg \
-  $(LOCAL_PATH)/../../extras/f2fs_utils \
 
 LOCAL_SRC_FILES := \
     bootimg_utils.cpp \
@@ -58,7 +57,6 @@
 
 LOCAL_STATIC_LIBRARIES := \
     libziparchive \
-    libext4_utils \
     libsparse \
     libutils \
     liblog \
@@ -68,13 +66,7 @@
     libcutils \
     libgtest_host \
 
-# libf2fs_dlutils_host will dlopen("libf2fs_fmt_host_dyn")
 LOCAL_CFLAGS_linux := -DUSE_F2FS
-LOCAL_LDFLAGS_linux := -ldl -rdynamic -Wl,-rpath,.
-LOCAL_REQUIRED_MODULES_linux := libf2fs_fmt_host_dyn
-# The following libf2fs_* are from system/extras/f2fs_utils,
-# and do not use code in external/f2fs-tools.
-LOCAL_STATIC_LIBRARIES_linux += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host
 
 LOCAL_CXX_STL := libc++_static
 
@@ -88,9 +80,6 @@
 my_dist_files := $(LOCAL_BUILT_MODULE)
 my_dist_files += $(HOST_OUT_EXECUTABLES)/mke2fs$(HOST_EXECUTABLE_SUFFIX)
 my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid$(HOST_EXECUTABLE_SUFFIX)
-ifeq ($(HOST_OS),linux)
-my_dist_files += $(HOST_LIBRARY_PATH)/libf2fs_fmt_host_dyn$(HOST_SHLIB_SUFFIX)
-endif
 $(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
 ifdef HOST_CROSS_OS
 # Archive fastboot.exe for win_sdk build.
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 5f2267c..6175f59 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -444,13 +444,13 @@
                                  const char* cmdline) {
     int64_t ksize;
     void* kdata = load_file(kernel.c_str(), &ksize);
-    if (kdata == nullptr) die("cannot load '%s': %s\n", kernel.c_str(), strerror(errno));
+    if (kdata == nullptr) die("cannot load '%s': %s", kernel.c_str(), strerror(errno));
 
     // Is this actually a boot image?
     if (!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
         if (cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
 
-        if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk\n");
+        if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk");
 
         *sz = ksize;
         return kdata;
@@ -460,14 +460,14 @@
     int64_t rsize = 0;
     if (!ramdisk.empty()) {
         rdata = load_file(ramdisk.c_str(), &rsize);
-        if (rdata == nullptr) die("cannot load '%s': %s\n", ramdisk.c_str(), strerror(errno));
+        if (rdata == nullptr) die("cannot load '%s': %s", ramdisk.c_str(), strerror(errno));
     }
 
     void* sdata = nullptr;
     int64_t ssize = 0;
     if (!second_stage.empty()) {
         sdata = load_file(second_stage.c_str(), &ssize);
-        if (sdata == nullptr) die("cannot load '%s': %s\n", second_stage.c_str(), strerror(errno));
+        if (sdata == nullptr) die("cannot load '%s': %s", second_stage.c_str(), strerror(errno));
     }
 
     fprintf(stderr,"creating boot image...\n");
@@ -476,7 +476,7 @@
                       rdata, rsize, ramdisk_offset,
                       sdata, ssize, second_offset,
                       page_size, base_addr, tags_offset, &bsize);
-    if (bdata == nullptr) die("failed to create boot.img\n");
+    if (bdata == nullptr) die("failed to create boot.img");
 
     if (cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
     fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", bsize);
@@ -485,29 +485,22 @@
     return bdata;
 }
 
-static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, int64_t* sz) {
+static void* unzip_to_memory(ZipArchiveHandle zip, const char* entry_name, int64_t* sz) {
     ZipString zip_entry_name(entry_name);
     ZipEntry zip_entry;
     if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name);
-        return 0;
+        return nullptr;
     }
 
     *sz = zip_entry.uncompressed_length;
 
-    fprintf(stderr, "extracting %s (%" PRId64 " MB)...\n", entry_name, *sz / 1024 / 1024);
+    fprintf(stderr, "extracting %s (%" PRId64 " MB) to RAM...\n", entry_name, *sz / 1024 / 1024);
     uint8_t* data = reinterpret_cast<uint8_t*>(malloc(zip_entry.uncompressed_length));
-    if (data == nullptr) {
-        fprintf(stderr, "failed to allocate %" PRId64 " bytes for '%s'\n", *sz, entry_name);
-        return 0;
-    }
+    if (data == nullptr) die("failed to allocate %" PRId64 " bytes for '%s'", *sz, entry_name);
 
     int error = ExtractToMemory(zip, &zip_entry, data, zip_entry.uncompressed_length);
-    if (error != 0) {
-        fprintf(stderr, "failed to extract '%s': %s\n", entry_name, ErrorCodeString(error));
-        free(data);
-        return 0;
-    }
+    if (error != 0) die("failed to extract '%s': %s", entry_name, ErrorCodeString(error));
 
     return data;
 }
@@ -524,14 +517,12 @@
     char temp_path[PATH_MAX];
     DWORD nchars = GetTempPath(sizeof(temp_path), temp_path);
     if (nchars == 0 || nchars >= sizeof(temp_path)) {
-        fprintf(stderr, "GetTempPath failed, error %ld\n", GetLastError());
-        return nullptr;
+        die("GetTempPath failed, error %ld", GetLastError());
     }
 
     char filename[PATH_MAX];
     if (GetTempFileName(temp_path, "fastboot", 0, filename) == 0) {
-        fprintf(stderr, "GetTempFileName failed, error %ld\n", GetLastError());
-        return nullptr;
+        die("GetTempFileName failed, error %ld", GetLastError());
     }
 
     return fopen(filename, "w+bTD");
@@ -540,8 +531,7 @@
 #define tmpfile win32_tmpfile
 
 static std::string make_temporary_directory() {
-    fprintf(stderr, "make_temporary_directory not supported under Windows, sorry!");
-    return "";
+    die("make_temporary_directory not supported under Windows, sorry!");
 }
 
 static int make_temporary_fd() {
@@ -613,9 +603,7 @@
 static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
     unique_fd fd(make_temporary_fd());
     if (fd == -1) {
-        fprintf(stderr, "failed to create temporary file for '%s': %s\n",
-                entry_name, strerror(errno));
-        return -1;
+        die("failed to create temporary file for '%s': %s", entry_name, strerror(errno));
     }
 
     ZipString zip_entry_name(entry_name);
@@ -625,16 +613,20 @@
         return -1;
     }
 
-    fprintf(stderr, "extracting %s (%" PRIu32 " MB)...\n", entry_name,
+    fprintf(stderr, "extracting %s (%" PRIu32 " MB) to disk...", entry_name,
             zip_entry.uncompressed_length / 1024 / 1024);
+    double start = now();
     int error = ExtractEntryToFile(zip, &zip_entry, fd);
     if (error != 0) {
-        fprintf(stderr, "failed to extract '%s': %s\n", entry_name, ErrorCodeString(error));
-        return -1;
+        die("\nfailed to extract '%s': %s", entry_name, ErrorCodeString(error));
     }
 
-    lseek(fd, 0, SEEK_SET);
-    // TODO: We're leaking 'fp' here.
+    if (lseek(fd, 0, SEEK_SET) != 0) {
+        die("\nlseek on extracted file '%s' failed: %s", entry_name, strerror(errno));
+    }
+
+    fprintf(stderr, " took %.3fs\n", now() - start);
+
     return fd.release();
 }
 
@@ -738,27 +730,18 @@
     fb_queue_notice("--------------------------------------------");
 }
 
-static struct sparse_file **load_sparse_files(int fd, int max_size)
-{
+static struct sparse_file** load_sparse_files(int fd, int max_size) {
     struct sparse_file* s = sparse_file_import_auto(fd, false, true);
-    if (!s) {
-        die("cannot sparse read file\n");
-    }
+    if (!s) die("cannot sparse read file");
 
     int files = sparse_file_resparse(s, max_size, nullptr, 0);
-    if (files < 0) {
-        die("Failed to resparse\n");
-    }
+    if (files < 0) die("Failed to resparse");
 
     sparse_file** out_s = reinterpret_cast<sparse_file**>(calloc(sizeof(struct sparse_file *), files + 1));
-    if (!out_s) {
-        die("Failed to allocate sparse file array\n");
-    }
+    if (!out_s) die("Failed to allocate sparse file array");
 
     files = sparse_file_resparse(s, max_size, out_s, files);
-    if (files < 0) {
-        die("Failed to resparse\n");
-    }
+    if (files < 0) die("Failed to resparse");
 
     return out_s;
 }
@@ -1017,18 +1000,18 @@
             if (count > 0) {
                 return "a";
             } else {
-                die("No known slots.");
+                die("No known slots");
             }
         }
     }
 
     int count = get_slot_count(transport);
-    if (count == 0) die("Device does not support slots.\n");
+    if (count == 0) die("Device does not support slots");
 
     if (slot == "other") {
         std::string other = get_other_slot(transport, count);
         if (other == "") {
-           die("No known slots.");
+           die("No known slots");
         }
         return other;
     }
@@ -1060,7 +1043,7 @@
         if (slot == "") {
             current_slot = get_current_slot(transport);
             if (current_slot == "") {
-                die("Failed to identify current slot.\n");
+                die("Failed to identify current slot");
             }
             func(part + "_" + current_slot);
         } else {
@@ -1086,7 +1069,7 @@
 
     if (slot == "all") {
         if (!fb_getvar(transport, "has-slot:" + part, &has_slot)) {
-            die("Could not check if partition %s has slot.", part.c_str());
+            die("Could not check if partition %s has slot %s", part.c_str(), slot.c_str());
         }
         if (has_slot == "yes") {
             for (int i=0; i < get_slot_count(transport); i++) {
@@ -1111,7 +1094,7 @@
 
 static void do_update_signature(ZipArchiveHandle zip, const char* filename) {
     int64_t sz;
-    void* data = unzip_file(zip, filename, &sz);
+    void* data = unzip_to_memory(zip, filename, &sz);
     if (data == nullptr) return;
     fb_queue_download("signature", data, sz);
     fb_queue_command("signature", "installing signature");
@@ -1146,14 +1129,12 @@
     ZipArchiveHandle zip;
     int error = OpenArchive(filename, &zip);
     if (error != 0) {
-        CloseArchive(zip);
         die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
     }
 
     int64_t sz;
-    void* data = unzip_file(zip, "android-info.txt", &sz);
+    void* data = unzip_to_memory(zip, "android-info.txt", &sz);
     if (data == nullptr) {
-        CloseArchive(zip);
         die("update package '%s' has no android-info.txt", filename);
     }
 
@@ -1186,17 +1167,17 @@
         int fd = unzip_to_file(zip, images[i].img_name);
         if (fd == -1) {
             if (images[i].is_optional) {
-                continue;
+                continue; // An optional file is missing, so ignore it.
             }
-            CloseArchive(zip);
-            exit(1); // unzip_to_file already explained why.
+            die("non-optional file %s missing", images[i].img_name);
         }
+
         fastboot_buffer buf;
         if (!load_buf_fd(transport, fd, &buf)) {
             die("cannot load %s from flash: %s", images[i].img_name, strerror(errno));
         }
 
-        auto update = [&](const std::string &partition) {
+        auto update = [&](const std::string& partition) {
             do_update_signature(zip, images[i].sig_name);
             if (erase_first && needs_erase(transport, partition.c_str())) {
                 fb_queue_erase(partition.c_str());
@@ -1210,12 +1191,13 @@
         do_for_partitions(transport, images[i].part_name, slot, update, false);
     }
 
-    CloseArchive(zip);
     if (slot_override == "all") {
         set_active(transport, "a");
     } else {
         set_active(transport, slot_override);
     }
+
+    CloseArchive(zip);
 }
 
 static void do_send_signature(const std::string& fn) {
@@ -1274,7 +1256,7 @@
         fastboot_buffer buf;
         if (!load_buf(transport, fname.c_str(), &buf)) {
             if (images[i].is_optional) continue;
-            die("could not load '%s': %s\n", images[i].img_name, strerror(errno));
+            die("could not load '%s': %s", images[i].img_name, strerror(errno));
         }
 
         auto flashall = [&](const std::string &partition) {
@@ -1375,7 +1357,7 @@
 
 static unsigned fb_get_flash_block_size(Transport* transport, std::string name) {
     std::string sizeString;
-    if (!fb_getvar(transport, name.c_str(), &sizeString)) {
+    if (!fb_getvar(transport, name.c_str(), &sizeString) || sizeString.empty()) {
         /* This device does not report flash block sizes, so return 0 */
         return 0;
     }
@@ -1386,9 +1368,8 @@
         fprintf(stderr, "Couldn't parse %s '%s'.\n", name.c_str(), sizeString.c_str());
         return 0;
     }
-    if (size < 4096 || (size & (size - 1)) != 0) {
-        fprintf(stderr, "Invalid %s %u: must be a power of 2 and at least 4096.\n",
-                name.c_str(), size);
+    if ((size & (size - 1)) != 0) {
+        fprintf(stderr, "Invalid %s %u: must be a power of 2.\n", name.c_str(), size);
         return 0;
     }
     return size;
@@ -1463,7 +1444,7 @@
 
     if (fs_generator_generate(gen, output.path, size, initial_dir,
             eraseBlkSize, logicalBlkSize)) {
-        die("Cannot generate image for %s\n", partition);
+        die("Cannot generate image for %s", partition);
         return;
     }
 
@@ -1583,9 +1564,7 @@
             break;
         case 'S':
             sparse_limit = parse_num(optarg);
-            if (sparse_limit < 0) {
-                    die("invalid sparse limit");
-            }
+            if (sparse_limit < 0) die("invalid sparse limit");
             break;
         case 'u':
             erase_first = false;
@@ -1718,7 +1697,7 @@
             std::string filename = next_arg(&args);
             data = load_file(filename.c_str(), &sz);
             if (data == nullptr) die("could not load '%s': %s", filename.c_str(), strerror(errno));
-            if (sz != 256) die("signature must be 256 bytes");
+            if (sz != 256) die("signature must be 256 bytes (got %" PRId64 ")", sz);
             fb_queue_download("signature", data, sz);
             fb_queue_command("signature", "installing signature");
         } else if (command == "reboot") {
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 709f061..8877b09 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -1,7 +1,6 @@
 #include "fs.h"
 
 #include "fastboot.h"
-#include "make_f2fs.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -23,8 +22,6 @@
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
-#include <ext4_utils/make_ext4fs.h>
-#include <sparse/sparse.h>
 
 using android::base::StringPrintf;
 using android::base::unique_fd;
@@ -120,6 +117,8 @@
         int raid_stripe_width = eraseBlkSize / block_size;
         // stride should be the max of 8kb and logical block size
         if (logicalBlkSize != 0 && logicalBlkSize < 8192) raid_stride = 8192 / block_size;
+        // stripe width should be >= stride
+        if (raid_stripe_width < raid_stride) raid_stripe_width = raid_stride;
         ext_attr += StringPrintf(",stride=%d,stripe-width=%d", raid_stride, raid_stripe_width);
     }
     mke2fs_args.push_back("-E");
@@ -159,16 +158,32 @@
 static int generate_f2fs_image(const char* fileName, long long partSize, const std::string& initial_dir,
                                unsigned /* unused */, unsigned /* unused */)
 {
+    const std::string exec_dir = android::base::GetExecutableDirectory();
+    const std::string mkf2fs_path = exec_dir + "/make_f2fs";
+    std::vector<const char*> mkf2fs_args = {mkf2fs_path.c_str()};
+
+    mkf2fs_args.push_back("-S");
+    std::string size_str = std::to_string(partSize);
+    mkf2fs_args.push_back(size_str.c_str());
+    mkf2fs_args.push_back("-f");
+    mkf2fs_args.push_back("-O");
+    mkf2fs_args.push_back("encrypt");
+    mkf2fs_args.push_back("-O");
+    mkf2fs_args.push_back("quota");
+    mkf2fs_args.push_back(fileName);
+    mkf2fs_args.push_back(nullptr);
+
+    int ret = exec_e2fs_cmd(mkf2fs_args[0], const_cast<char**>(mkf2fs_args.data()));
+    if (ret != 0) {
+        fprintf(stderr, "mkf2fs failed: %d\n", ret);
+        return -1;
+    }
+
     if (!initial_dir.empty()) {
         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);
+    return 0;
 }
 #endif
 
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 5a6298e..ed165ed 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -20,7 +20,11 @@
         misc_undefined: ["integer"],
     },
     local_include_dirs: ["include/"],
-    cppflags: ["-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-variable",
+    ],
 }
 
 cc_library_static {
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 85a593f..cbd8ffa 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -115,7 +115,9 @@
 
     std::string size_str = std::to_string(dev_sz / 4096);
     const char* const args[] = {
-        "/system/bin/make_f2fs", "-f", "-O", "encrypt", fs_blkdev, size_str.c_str(), nullptr};
+        "/system/bin/make_f2fs", "-d1", "-f",
+        "-O", "encrypt", "-O", "quota",
+        fs_blkdev, size_str.c_str(), nullptr};
 
     return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), NULL, true,
                                    LOG_KLOG, true, nullptr, nullptr, 0);
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 92c6ee8..2c18a6d 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -700,25 +700,44 @@
 }
 
 /*
- * tries to load default fstab.<hardware> file from /odm/etc, /vendor/etc
- * or /. loads the first one found and also combines fstab entries passed
- * in from device tree.
+ * Identify path to fstab file. Lookup is based on pattern
+ * fstab.<hardware>, fstab.<hardware.platform> in folders
+   /odm/etc, vendor/etc, or /.
+ */
+static std::string get_fstab_path()
+{
+    for (const char* prop : {"hardware", "hardware.platform"}) {
+        std::string hw;
+
+        if (!fs_mgr_get_boot_config(prop, &hw)) continue;
+
+        for (const char* prefix : {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab."}) {
+            std::string fstab_path = prefix + hw;
+            if (access(fstab_path.c_str(), F_OK) == 0) {
+                return fstab_path;
+            }
+        }
+    }
+
+    return std::string();
+}
+
+/*
+ * loads the fstab file and combines with fstab entries passed in from device tree.
  */
 struct fstab *fs_mgr_read_fstab_default()
 {
-    std::string hw;
     std::string default_fstab;
 
     // Use different fstab paths for normal boot and recovery boot, respectively
     if (access("/sbin/recovery", F_OK) == 0) {
         default_fstab = "/etc/recovery.fstab";
-    } else if (fs_mgr_get_boot_config("hardware", &hw)) {  // normal boot
-        for (const char *prefix : {"/odm/etc/fstab.","/vendor/etc/fstab.", "/fstab."}) {
-            default_fstab = prefix + hw;
-            if (access(default_fstab.c_str(), F_OK) == 0) break;
-        }
-    } else {
-        LWARNING << __FUNCTION__ << "(): failed to find device hardware name";
+    } else {  // normal boot
+        default_fstab = get_fstab_path();
+    }
+
+    if (default_fstab.empty()) {
+        LWARNING << __FUNCTION__ << "(): failed to find device default fstab";
     }
 
     // combines fstab entries passed in from device tree with
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 33fd562..0a113b4 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -40,7 +40,7 @@
             char *tmp;
             if (ab_suffix.empty()) {
                 ab_suffix = fs_mgr_get_slot_suffix();
-                // Returns false as non A/B devices should not have MF_SLOTSELECT.
+                // Return false if failed to get ab_suffix when MF_SLOTSELECT is specified.
                 if (ab_suffix.empty()) return false;
             }
             if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device, ab_suffix.c_str()) > 0) {
diff --git a/healthd/Android.bp b/healthd/Android.bp
index d348866..ed1413e 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -53,7 +53,10 @@
     init_rc: ["android.hardware.health@2.0-service.rc"],
     vendor: true,
     relative_install_path: "hw",
-    srcs: ["HealthService.cpp"],
+    srcs: [
+        "HealthServiceCommon.cpp",
+        "HealthServiceDefault.cpp",
+    ],
 
     cflags: ["-DHEALTH_INSTANCE_NAME=\"default\""],
 
@@ -74,3 +77,33 @@
         "android.hardware.health@2.0",
     ],
 }
+
+cc_binary {
+    name: "healthd",
+    srcs: [
+        "HealthServiceCommon.cpp",
+        "HealthServiceHealthd.cpp",
+    ],
+    local_include_dirs: ["include"],
+
+    cflags: ["-DHEALTH_INSTANCE_NAME=\"backup\""],
+
+    static_libs: [
+        "android.hardware.health@2.0-impl",
+        "android.hardware.health@1.0-convert",
+        "libbatterymonitor",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libutils",
+        "android.hardware.health@1.0",
+        "android.hardware.health@2.0",
+    ],
+
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 8c3dcfd..c1a82c2 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -3,27 +3,6 @@
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
-    healthd_mode_android.cpp \
-    BatteryPropertiesRegistrar.cpp
-
-LOCAL_MODULE := libhealthd_android
-LOCAL_EXPORT_C_INCLUDE_DIRS := \
-    $(LOCAL_PATH) \
-    $(LOCAL_PATH)/include
-
-LOCAL_STATIC_LIBRARIES := \
-    libbatterymonitor \
-    libbatteryservice \
-    libutils \
-    libbase \
-    libcutils \
-    liblog \
-    libc \
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
 
 LOCAL_MODULE := libhealthd_draw
 
@@ -174,41 +153,3 @@
 _add-charger-image :=
 _img_modules :=
 endif # LOCAL_CHARGER_NO_UI
-
-### healthd ###
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    healthd_common.cpp \
-    healthd.cpp \
-
-LOCAL_MODULE := healthd
-LOCAL_MODULE_TAGS := optional
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
-ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_FAST),)
-LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_FAST=$(BOARD_PERIODIC_CHORES_INTERVAL_FAST)
-endif
-ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_SLOW),)
-LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_SLOW=$(BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
-endif
-
-LOCAL_STATIC_LIBRARIES := \
-    libhealthd_android \
-    libbatterymonitor \
-    libbatteryservice \
-    android.hardware.health@1.0-convert \
-
-LOCAL_SHARED_LIBRARIES := \
-    libbinder \
-    libbase \
-    libutils \
-    libcutils \
-    liblog \
-    libm \
-    libc \
-    libhidlbase \
-    libhidltransport \
-    android.hardware.health@1.0 \
-
-include $(BUILD_EXECUTABLE)
diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
deleted file mode 100644
index e51a06d..0000000
--- a/healthd/BatteryPropertiesRegistrar.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "BatteryPropertiesRegistrar.h"
-#include <batteryservice/BatteryService.h>
-#include <batteryservice/IBatteryPropertiesListener.h>
-#include <batteryservice/IBatteryPropertiesRegistrar.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/PermissionCache.h>
-#include <private/android_filesystem_config.h>
-#include <utils/Errors.h>
-#include <utils/Mutex.h>
-#include <utils/String16.h>
-
-#include <healthd/healthd.h>
-
-namespace android {
-
-void BatteryPropertiesRegistrar::publish(
-    const sp<BatteryPropertiesRegistrar>& service) {
-    defaultServiceManager()->addService(String16("batteryproperties"), service);
-}
-
-void BatteryPropertiesRegistrar::notifyListeners(const struct BatteryProperties& 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);
-    }
-}
-
-void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesListener>& listener) {
-    {
-        if (listener == NULL)
-            return;
-        Mutex::Autolock _l(mRegistrationLock);
-        // check whether this is a duplicate
-        for (size_t i = 0; i < mListeners.size(); i++) {
-            if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) {
-                return;
-            }
-        }
-
-        mListeners.add(listener);
-        IInterface::asBinder(listener)->linkToDeath(this);
-    }
-    healthd_battery_update();
-}
-
-void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesListener>& listener) {
-    if (listener == NULL)
-        return;
-    Mutex::Autolock _l(mRegistrationLock);
-    for (size_t i = 0; i < mListeners.size(); i++) {
-        if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) {
-            IInterface::asBinder(mListeners[i])->unlinkToDeath(this);
-            mListeners.removeAt(i);
-            break;
-        }
-    }
-}
-
-status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) {
-    return healthd_get_property(id, val);
-}
-
-void BatteryPropertiesRegistrar::scheduleUpdate() {
-    healthd_battery_update();
-}
-
-status_t BatteryPropertiesRegistrar::dump(int fd, const Vector<String16>& /*args*/) {
-    IPCThreadState* self = IPCThreadState::self();
-    const int pid = self->getCallingPid();
-    const int uid = self->getCallingUid();
-    if ((uid != AID_SHELL) &&
-        !PermissionCache::checkPermission(
-                String16("android.permission.DUMP"), pid, uid))
-        return PERMISSION_DENIED;
-
-    healthd_dump_battery_state(fd);
-    return OK;
-}
-
-void BatteryPropertiesRegistrar::binderDied(const wp<IBinder>& who) {
-    Mutex::Autolock _l(mRegistrationLock);
-
-    for (size_t i = 0; i < mListeners.size(); i++) {
-        if (IInterface::asBinder(mListeners[i]) == who) {
-            mListeners.removeAt(i);
-            break;
-        }
-    }
-}
-
-}  // namespace android
diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h
deleted file mode 100644
index 14e9145..0000000
--- a/healthd/BatteryPropertiesRegistrar.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
-#define HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
-
-#include <binder/IBinder.h>
-#include <utils/Mutex.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
-#include <batteryservice/BatteryService.h>
-#include <batteryservice/IBatteryPropertiesListener.h>
-#include <batteryservice/IBatteryPropertiesRegistrar.h>
-
-namespace android {
-
-class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar,
-                                   public IBinder::DeathRecipient {
-public:
-    void publish(const sp<BatteryPropertiesRegistrar>& service);
-    void notifyListeners(const struct BatteryProperties& props);
-    void scheduleUpdate();
-
-private:
-    Mutex mRegistrationLock;
-    Vector<sp<IBatteryPropertiesListener> > mListeners;
-
-    void registerListener(const sp<IBatteryPropertiesListener>& listener);
-    void unregisterListener(const sp<IBatteryPropertiesListener>& listener);
-    status_t getProperty(int id, struct BatteryProperty *val);
-    status_t dump(int fd, const Vector<String16>& args);
-    void binderDied(const wp<IBinder>& who);
-};
-
-};  // namespace android
-
-#endif // HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
diff --git a/healthd/Health.cpp b/healthd/Health.cpp
index ec05398..d271811 100644
--- a/healthd/Health.cpp
+++ b/healthd/Health.cpp
@@ -13,6 +13,8 @@
 namespace V2_0 {
 namespace implementation {
 
+sp<Health> Health::instance_;
+
 Health::Health(struct healthd_config* c) {
     battery_monitor_ = std::make_unique<BatteryMonitor>();
     battery_monitor_->init(c);
@@ -120,7 +122,7 @@
     }
 
     // Retrieve all information and call healthd_mode_ops->battery_update, which calls
-    // updateAndNotify.
+    // notifyListeners.
     bool chargerOnline = battery_monitor_->update();
 
     // adjust uevent / wakealarm periods
@@ -129,19 +131,10 @@
     return Result::SUCCESS;
 }
 
-void Health::updateAndNotify(HealthInfo* info) {
-    // update 2.0 specific fields
-    struct BatteryProperty prop;
-    if (battery_monitor_->getProperty(BATTERY_PROP_CURRENT_AVG, &prop) == OK)
-        info->batteryCurrentAverage = static_cast<int32_t>(prop.valueInt64);
-    if (battery_monitor_->getProperty(BATTERY_PROP_CAPACITY, &prop) == OK)
-        info->batteryCapacity = static_cast<int32_t>(prop.valueInt64);
-    if (battery_monitor_->getProperty(BATTERY_PROP_ENERGY_COUNTER, &prop) == OK)
-        info->energyCounter = prop.valueInt64;
-
+void Health::notifyListeners(const HealthInfo& info) {
     std::lock_guard<std::mutex> _lock(callbacks_lock_);
     for (auto it = callbacks_.begin(); it != callbacks_.end();) {
-        auto ret = (*it)->healthInfoChanged(*info);
+        auto ret = (*it)->healthInfoChanged(info);
         if (!ret.isOk() && ret.isDeadObject()) {
             it = callbacks_.erase(it);
         } else {
@@ -163,7 +156,17 @@
     (void)unregisterCallbackInternal(who.promote());
 }
 
-// Methods from ::android::hidl::base::V1_0::IBase follow.
+sp<IHealth> Health::initInstance(struct healthd_config* c) {
+    if (instance_ == nullptr) {
+        instance_ = new Health(c);
+    }
+    return instance_;
+}
+
+sp<Health> Health::getImplementation() {
+    CHECK(instance_ != nullptr);
+    return instance_;
+}
 
 }  // namespace implementation
 }  // namespace V2_0
diff --git a/healthd/HealthService.cpp b/healthd/HealthServiceCommon.cpp
similarity index 78%
rename from healthd/HealthService.cpp
rename to healthd/HealthServiceCommon.cpp
index 29a29ed..68ff526 100644
--- a/healthd/HealthService.cpp
+++ b/healthd/HealthServiceCommon.cpp
@@ -28,48 +28,40 @@
 
 using android::hardware::IPCThreadState;
 using android::hardware::configureRpcThreadpool;
+using android::hardware::health::V1_0::HealthInfo;
 using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
-using android::hardware::health::V2_0::HealthInfo;
 using android::hardware::health::V2_0::IHealth;
 using android::hardware::health::V2_0::implementation::Health;
 
-// see healthd_common.cpp
-android::sp<IHealth> gHealth;
-
-static int gBinderFd;
-
 extern int healthd_main(void);
 
 static void binder_event(uint32_t /*epevents*/) {
     IPCThreadState::self()->handlePolledCommands();
 }
 
-// TODO(b/67463967): healthd_board_* functions should be removed in health@2.0
-int healthd_board_battery_update(struct android::BatteryProperties*)
-{
-    // return 0 to log periodic polled battery status to kernel log
-    return 0;
-}
-
 void healthd_mode_service_2_0_init(struct healthd_config* config) {
+    int binderFd;
+
     LOG(INFO) << LOG_TAG << " Hal is starting up...";
 
+    configureRpcThreadpool(1, false /* callerWillJoin */);
+    IPCThreadState::self()->disableBackgroundScheduling(true);
+    IPCThreadState::self()->setupPolling(&binderFd);
+
+    if (binderFd >= 0) {
+        if (healthd_register_event(binderFd, binder_event))
+            LOG(ERROR) << LOG_TAG << ": Register for binder events failed";
+    }
+
     // Implementation-defined init logic goes here.
     // 1. config->periodic_chores_interval_* variables
     // 2. config->battery*Path variables
     // 3. config->energyCounter. In this implementation, energyCounter is not defined.
+    // TODO(b/68724651): healthd_board_* functions should be removed in health@2.0
+    healthd_board_init(config);
 
-    configureRpcThreadpool(1, false /* callerWillJoin */);
-    IPCThreadState::self()->disableBackgroundScheduling(true);
-    IPCThreadState::self()->setupPolling(&gBinderFd);
-
-    if (gBinderFd >= 0) {
-        if (healthd_register_event(gBinderFd, binder_event))
-            LOG(ERROR) << LOG_TAG << ": Register for binder events failed";
-    }
-
-    gHealth = new ::android::hardware::health::V2_0::implementation::Health(config);
-    CHECK_EQ(gHealth->registerAsService(HEALTH_INSTANCE_NAME), android::OK)
+    android::sp<IHealth> service = Health::initInstance(config);
+    CHECK_EQ(service->registerAsService(HEALTH_INSTANCE_NAME), android::OK)
         << LOG_TAG << ": Failed to register HAL";
 
     LOG(INFO) << LOG_TAG << ": Hal init done";
@@ -85,13 +77,12 @@
 }
 
 void healthd_mode_service_2_0_battery_update(struct android::BatteryProperties* prop) {
-
     // Implementation-defined update logic goes here. An implementation
     // can make modifications to prop before broadcasting it to all callbacks.
 
-    HealthInfo info{};
-    convertToHealthInfo(prop, info.legacy);
-    static_cast<Health*>(gHealth.get())->updateAndNotify(&info);
+    HealthInfo info;
+    convertToHealthInfo(prop, info);
+    Health::getImplementation()->notifyListeners(info);
 }
 
 static struct healthd_mode_ops healthd_mode_service_2_0_ops = {
diff --git a/healthd/HealthServiceDefault.cpp b/healthd/HealthServiceDefault.cpp
new file mode 100644
index 0000000..42e76d9
--- /dev/null
+++ b/healthd/HealthServiceDefault.cpp
@@ -0,0 +1,26 @@
+/*
+ * 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 <healthd/healthd.h>
+
+void healthd_board_init(struct healthd_config*) {
+    // use defaults
+}
+
+int healthd_board_battery_update(struct android::BatteryProperties*) {
+    // return 0 to log periodic polled battery status to kernel log
+    return 0;
+}
diff --git a/healthd/HealthServiceHealthd.cpp b/healthd/HealthServiceHealthd.cpp
new file mode 100644
index 0000000..72a446a
--- /dev/null
+++ b/healthd/HealthServiceHealthd.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "healthd"
+#include <android-base/logging.h>
+
+#include <android/hardware/health/1.0/IHealth.h>
+#include <android/hardware/health/1.0/types.h>
+#include <hal_conversion.h>
+#include <healthd/healthd.h>
+#include <hidl/HidlTransportSupport.h>
+
+using android::OK;
+using android::NAME_NOT_FOUND;
+using android::hardware::health::V1_0::HealthConfig;
+using android::hardware::health::V1_0::HealthInfo;
+using android::hardware::health::V1_0::Result;
+using android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
+using android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
+
+using IHealthLegacy = android::hardware::health::V1_0::IHealth;
+
+static android::sp<IHealthLegacy> gHealth_1_0;
+
+static int healthd_board_get_energy_counter(int64_t* energy) {
+    if (gHealth_1_0 == nullptr) {
+        return NAME_NOT_FOUND;
+    }
+
+    Result result = Result::NOT_SUPPORTED;
+    gHealth_1_0->energyCounter([energy, &result](Result ret, int64_t energyOut) {
+        result = ret;
+        *energy = energyOut;
+    });
+
+    return result == Result::SUCCESS ? OK : NAME_NOT_FOUND;
+}
+
+void healthd_board_init(struct healthd_config* config) {
+    gHealth_1_0 = IHealthLegacy::getService();
+
+    if (gHealth_1_0 == nullptr) {
+        return;
+    }
+
+    HealthConfig halConfig{};
+    convertToHealthConfig(config, halConfig);
+    gHealth_1_0->init(halConfig, [config](const auto& halConfigOut) {
+        convertFromHealthConfig(halConfigOut, config);
+        // always redirect energy counter queries
+        config->energyCounter = healthd_board_get_energy_counter;
+    });
+    LOG(INFO) << LOG_TAG << ": redirecting calls to 1.0 health HAL";
+}
+
+// TODO(b/68724651): Move this function into healthd_mode_service_2_0_battery_update
+// with logthis returned.
+int healthd_board_battery_update(struct android::BatteryProperties* props) {
+    int logthis = 0;
+
+    if (gHealth_1_0 == nullptr) {
+        return logthis;
+    }
+
+    HealthInfo info;
+    convertToHealthInfo(props, info);
+    gHealth_1_0->update(info, [props, &logthis](int32_t ret, const auto& infoOut) {
+        logthis = ret;
+        convertFromHealthInfo(infoOut, props);
+    });
+
+    return logthis;
+}
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
deleted file mode 100644
index ed1971a..0000000
--- a/healthd/healthd.cpp
+++ /dev/null
@@ -1,123 +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.
- */
-
-#define LOG_TAG "healthd"
-#define KLOG_LEVEL 6
-
-#include <healthd/healthd.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <cutils/klog.h>
-
-#include <android/hardware/health/1.0/IHealth.h>
-#include <android/hardware/health/1.0/types.h>
-#include <hal_conversion.h>
-
-using namespace android;
-
-using IHealth = ::android::hardware::health::V1_0::IHealth;
-using Result = ::android::hardware::health::V1_0::Result;
-using HealthConfig = ::android::hardware::health::V1_0::HealthConfig;
-using HealthInfo = ::android::hardware::health::V1_0::HealthInfo;
-
-using ::android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
-using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
-using ::android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
-using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
-
-// device specific hal interface;
-static sp<IHealth> gHealth;
-
-// main healthd loop
-extern int healthd_main(void);
-
-// Android mode
-extern void healthd_mode_android_init(struct healthd_config *config);
-extern int healthd_mode_android_preparetowait(void);
-extern void healthd_mode_android_heartbeat(void);
-extern void healthd_mode_android_battery_update(
-    struct android::BatteryProperties *props);
-
-static struct healthd_mode_ops android_ops = {
-    .init = healthd_mode_android_init,
-    .preparetowait = healthd_mode_android_preparetowait,
-    .heartbeat = healthd_mode_android_heartbeat,
-    .battery_update = healthd_mode_android_battery_update,
-};
-
-// default energy counter property redirect to talk to device
-// HAL
-static int healthd_board_get_energy_counter(int64_t *energy) {
-
-    if (gHealth == nullptr) {
-        return NAME_NOT_FOUND;
-    }
-
-    Result result = Result::NOT_SUPPORTED;
-    gHealth->energyCounter([=, &result] (Result ret, int64_t energyOut) {
-                result = ret;
-                *energy = energyOut;
-            });
-
-    return result == Result::SUCCESS ? OK : NAME_NOT_FOUND;
-}
-
-void healthd_board_init(struct healthd_config *config) {
-
-    // Initialize the board HAL - Equivalent of healthd_board_init(config)
-    // in charger/recovery mode.
-
-    gHealth = IHealth::getService();
-    if (gHealth == nullptr) {
-        KLOG_WARNING(LOG_TAG, "unable to get HAL interface, using defaults\n");
-        return;
-    }
-
-    HealthConfig halConfig;
-    convertToHealthConfig(config, halConfig);
-    gHealth->init(halConfig, [=] (const auto &halConfigOut) {
-            convertFromHealthConfig(halConfigOut, config);
-            // always redirect energy counter queries
-            config->energyCounter = healthd_board_get_energy_counter;
-            });
-}
-
-int healthd_board_battery_update(struct android::BatteryProperties *props) {
-    int logthis = 0;
-
-    if (gHealth == nullptr) {
-        return logthis;
-    }
-
-    HealthInfo info;
-    convertToHealthInfo(props, info);
-    gHealth->update(info,
-            [=, &logthis] (int32_t ret, const auto &infoOut) {
-                logthis = ret;
-                convertFromHealthInfo(infoOut, props);
-            });
-
-    return logthis;
-}
-
-int main(int /*argc*/, char ** /*argv*/) {
-
-    healthd_mode_ops = &android_ops;
-
-    return healthd_main();
-}
diff --git a/healthd/healthd_common.cpp b/healthd/healthd_common.cpp
index 19e600f..140c49d 100644
--- a/healthd/healthd_common.cpp
+++ b/healthd/healthd_common.cpp
@@ -91,7 +91,7 @@
 #ifndef HEALTHD_USE_HEALTH_2_0
 static BatteryMonitor* gBatteryMonitor = nullptr;
 #else
-extern sp<::android::hardware::health::V2_0::IHealth> gHealth;
+using ::android::hardware::health::V2_0::implementation::Health;
 #endif
 
 struct healthd_mode_ops *healthd_mode_ops = nullptr;
@@ -160,42 +160,42 @@
     status_t err = UNKNOWN_ERROR;
     switch (id) {
         case BATTERY_PROP_CHARGE_COUNTER: {
-            gHealth->getChargeCounter([&](Result r, int32_t v) {
+            Health::getImplementation()->getChargeCounter([&](Result r, int32_t v) {
                 err = convertStatus(r);
                 val->valueInt64 = v;
             });
             break;
         }
         case BATTERY_PROP_CURRENT_NOW: {
-            gHealth->getCurrentNow([&](Result r, int32_t v) {
+            Health::getImplementation()->getCurrentNow([&](Result r, int32_t v) {
                 err = convertStatus(r);
                 val->valueInt64 = v;
             });
             break;
         }
         case BATTERY_PROP_CURRENT_AVG: {
-            gHealth->getCurrentAverage([&](Result r, int32_t v) {
+            Health::getImplementation()->getCurrentAverage([&](Result r, int32_t v) {
                 err = convertStatus(r);
                 val->valueInt64 = v;
             });
             break;
         }
         case BATTERY_PROP_CAPACITY: {
-            gHealth->getCapacity([&](Result r, int32_t v) {
+            Health::getImplementation()->getCapacity([&](Result r, int32_t v) {
                 err = convertStatus(r);
                 val->valueInt64 = v;
             });
             break;
         }
         case BATTERY_PROP_ENERGY_COUNTER: {
-            gHealth->getEnergyCounter([&](Result r, int64_t v) {
+            Health::getImplementation()->getEnergyCounter([&](Result r, int64_t v) {
                 err = convertStatus(r);
                 val->valueInt64 = v;
             });
             break;
         }
         case BATTERY_PROP_BATTERY_STATUS: {
-            gHealth->getChargeStatus([&](Result r, BatteryStatus v) {
+            Health::getImplementation()->getChargeStatus([&](Result r, BatteryStatus v) {
                 err = convertStatus(r);
                 val->valueInt64 = static_cast<int64_t>(v);
             });
@@ -237,7 +237,7 @@
 #ifndef HEALTHD_USE_HEALTH_2_0
     healthd_battery_update_internal(gBatteryMonitor->update());
 #else
-    gHealth->update();
+    Health::getImplementation()->update();
 #endif
 }
 
@@ -249,7 +249,7 @@
     nativeHandle->data[0] = fd;
     ::android::hardware::hidl_handle handle;
     handle.setTo(nativeHandle, true /* shouldOwn */);
-    gHealth->debug(handle, {} /* options */);
+    Health::getImplementation()->debug(handle, {} /* options */);
 #endif
 
     fsync(fd);
diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp
deleted file mode 100644
index c612313..0000000
--- a/healthd/healthd_mode_android.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "healthd-android"
-
-#include <healthd/healthd.h>
-#include "BatteryPropertiesRegistrar.h"
-
-#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
-#include <cutils/klog.h>
-#include <sys/epoll.h>
-
-using namespace android;
-
-static int gBinderFd;
-static sp<BatteryPropertiesRegistrar> gBatteryPropertiesRegistrar;
-
-void healthd_mode_android_battery_update(
-    struct android::BatteryProperties *props) {
-    if (gBatteryPropertiesRegistrar != NULL)
-        gBatteryPropertiesRegistrar->notifyListeners(*props);
-
-    return;
-}
-
-int healthd_mode_android_preparetowait(void) {
-    IPCThreadState::self()->flushCommands();
-    return -1;
-}
-
-void healthd_mode_android_heartbeat(void) {
-}
-
-static void binder_event(uint32_t /*epevents*/) {
-    IPCThreadState::self()->handlePolledCommands();
-}
-
-void healthd_mode_android_init(struct healthd_config* /*config*/) {
-    ProcessState::self()->setThreadPoolMaxThreadCount(0);
-    IPCThreadState::self()->disableBackgroundScheduling(true);
-    IPCThreadState::self()->setupPolling(&gBinderFd);
-
-    if (gBinderFd >= 0) {
-        if (healthd_register_event(gBinderFd, binder_event))
-            KLOG_ERROR(LOG_TAG,
-                       "Register for binder events failed\n");
-    }
-
-    gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
-    gBatteryPropertiesRegistrar->publish(gBatteryPropertiesRegistrar);
-}
diff --git a/healthd/include/health2/Health.h b/healthd/include/health2/Health.h
index d390b92..012b95b 100644
--- a/healthd/include/health2/Health.h
+++ b/healthd/include/health2/Health.h
@@ -16,15 +16,23 @@
 namespace implementation {
 
 using V1_0::BatteryStatus;
+using V1_0::HealthInfo;
 
 using ::android::hidl::base::V1_0::IBase;
 
 struct Health : public IHealth, hidl_death_recipient {
   public:
+    static sp<IHealth> initInstance(struct healthd_config* c);
+    // Should only be called by implementation itself (-impl, -service).
+    // Clients should not call this function. Instead, initInstance() initializes and returns the
+    // global instance that has fewer functions.
+    // TODO(b/62229583): clean up and hide these functions after update() logic is simplified.
+    static sp<Health> getImplementation();
+
     Health(struct healthd_config* c);
 
-    // TODO(b/62229583): clean up and hide these functions.
-    void updateAndNotify(HealthInfo* info);
+    // TODO(b/62229583): clean up and hide these functions after update() logic is simplified.
+    void notifyListeners(const HealthInfo& info);
 
     // Methods from IHealth follow.
     Return<Result> registerCallback(const sp<IHealthInfoCallback>& callback) override;
@@ -43,6 +51,8 @@
     void serviceDied(uint64_t cookie, const wp<IBase>& /* who */) override;
 
   private:
+    static sp<Health> instance_;
+
     std::mutex callbacks_lock_;
     std::vector<sp<IHealthInfoCallback>> callbacks_;
     std::unique_ptr<BatteryMonitor> battery_monitor_;
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index 17efbd6..97c7a8c 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -82,8 +82,13 @@
 
 int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup = EVENT_NO_WAKEUP_FD);
 void healthd_battery_update();
+
+// deprecated.
+// TODO(b/62229583): This function should be removed since it is only used by
+// BatteryPropertiesRegistrar.
 android::status_t healthd_get_property(int id,
     struct android::BatteryProperty *val);
+
 void healthd_dump_battery_state(int fd);
 
 struct healthd_mode_ops {
diff --git a/init/Android.bp b/init/Android.bp
index e906771..45ee754 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -20,7 +20,7 @@
     sanitize: {
         misc_undefined: ["signed-integer-overflow"],
     },
-    cppflags: [
+    cflags: [
         "-DLOG_UEVENTS=0",
         "-Wall",
         "-Wextra",
@@ -89,6 +89,7 @@
     whole_static_libs: ["libcap"],
     static_libs: [
         "libbase",
+        "libhidl-gen-utils",
         "libselinux",
         "liblog",
         "libprocessgroup",
@@ -136,6 +137,7 @@
         "libfs_mgr",
         "libfec",
         "libfec_rs",
+        "libhidl-gen-utils",
         "libsquashfs_utils",
         "liblogwrap",
         "libext4_utils",
@@ -185,6 +187,7 @@
     ],
     static_libs: [
         "libinit",
+        "libhidl-gen-utils",
         "libselinux",
         "libcrypto",
         "libprotobuf-cpp-lite",
diff --git a/init/Android.mk b/init/Android.mk
index dd0f1bf..44300f6 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -64,6 +64,7 @@
     libfs_mgr \
     libfec \
     libfec_rs \
+    libhidl-gen-utils \
     libsquashfs_utils \
     liblogwrap \
     libext4_utils \
diff --git a/init/action.cpp b/init/action.cpp
index 2617d00..5fa6bec 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -379,10 +379,12 @@
     return action_ ? action_->AddCommand(std::move(args), line) : Success();
 }
 
-void ActionParser::EndSection() {
+Result<Success> ActionParser::EndSection() {
     if (action_ && action_->NumCommands() > 0) {
         action_manager_->AddAction(std::move(action_));
     }
+
+    return Success();
 }
 
 }  // namespace init
diff --git a/init/action.h b/init/action.h
index cdfc6a0..1bfc6c7 100644
--- a/init/action.h
+++ b/init/action.h
@@ -130,7 +130,7 @@
     Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                  int line) override;
     Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
-    void EndSection() override;
+    Result<Success> EndSection() override;
 
   private:
     ActionManager* action_manager_;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 9be274b..950a551 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1033,7 +1033,7 @@
         {"verity_load_state",       {0,     0,    {false,  do_verity_load_state}}},
         {"verity_update_state",     {0,     0,    {false,  do_verity_update_state}}},
         {"wait",                    {1,     2,    {true,   do_wait}}},
-        {"wait_for_prop",           {2,     2,    {true,   do_wait_for_prop}}},
+        {"wait_for_prop",           {2,     2,    {false,  do_wait_for_prop}}},
         {"write",                   {2,     2,    {true,   do_write}}},
     };
     // clang-format on
diff --git a/init/init.cpp b/init/init.cpp
index 51a98a2..571da7c 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -202,24 +202,90 @@
     return next_process_restart_time;
 }
 
+static Result<Success> DoControlStart(Service* service) {
+    return service->Start();
+}
+
+static Result<Success> DoControlStop(Service* service) {
+    service->Stop();
+    return Success();
+}
+
+static Result<Success> DoControlRestart(Service* service) {
+    service->Restart();
+    return Success();
+}
+
+enum class ControlTarget {
+    SERVICE,    // function gets called for the named service
+    INTERFACE,  // action gets called for every service that holds this interface
+};
+
+struct ControlMessageFunction {
+    ControlTarget target;
+    std::function<Result<Success>(Service*)> action;
+};
+
+static const std::map<std::string, ControlMessageFunction>& get_control_message_map() {
+    // clang-format off
+    static const std::map<std::string, ControlMessageFunction> control_message_functions = {
+        {"start",             {ControlTarget::SERVICE,   DoControlStart}},
+        {"stop",              {ControlTarget::SERVICE,   DoControlStop}},
+        {"restart",           {ControlTarget::SERVICE,   DoControlRestart}},
+        {"interface_start",   {ControlTarget::INTERFACE, DoControlStart}},
+        {"interface_stop",    {ControlTarget::INTERFACE, DoControlStop}},
+        {"interface_restart", {ControlTarget::INTERFACE, DoControlRestart}},
+    };
+    // clang-format on
+
+    return control_message_functions;
+}
+
 void handle_control_message(const std::string& msg, const std::string& name) {
-    Service* svc = ServiceList::GetInstance().FindService(name);
-    if (svc == nullptr) {
-        LOG(ERROR) << "no such service '" << name << "'";
+    const auto& map = get_control_message_map();
+    const auto it = map.find(msg);
+
+    if (it == map.end()) {
+        LOG(ERROR) << "Unknown control msg '" << msg << "'";
         return;
     }
 
-    if (msg == "start") {
-        if (auto result = svc->Start(); !result) {
-            LOG(ERROR) << "Could not ctl.start service '" << name << "': " << result.error();
+    const ControlMessageFunction& function = it->second;
+
+    if (function.target == ControlTarget::SERVICE) {
+        Service* svc = ServiceList::GetInstance().FindService(name);
+        if (svc == nullptr) {
+            LOG(ERROR) << "No such service '" << name << "' for ctl." << msg;
+            return;
         }
-    } else if (msg == "stop") {
-        svc->Stop();
-    } else if (msg == "restart") {
-        svc->Restart();
-    } else {
-        LOG(ERROR) << "unknown control msg '" << msg << "'";
+        if (auto result = function.action(svc); !result) {
+            LOG(ERROR) << "Could not ctl." << msg << " for service " << name << ": "
+                       << result.error();
+        }
+
+        return;
     }
+
+    if (function.target == ControlTarget::INTERFACE) {
+        for (const auto& svc : ServiceList::GetInstance()) {
+            if (svc->interfaces().count(name) == 0) {
+                continue;
+            }
+
+            if (auto result = function.action(svc.get()); !result) {
+                LOG(ERROR) << "Could not handle ctl." << msg << " for service " << svc->name()
+                           << " with interface " << name << ": " << result.error();
+            }
+
+            return;
+        }
+
+        LOG(ERROR) << "Could not find service hosting interface " << name;
+        return;
+    }
+
+    LOG(ERROR) << "Invalid function target from static map key '" << msg
+               << "': " << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
 }
 
 static Result<Success> wait_for_coldboot_done_action(const BuiltinArguments& args) {
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 0f7e38f..6fa07e7 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -118,7 +118,10 @@
 FirstStageMount::FirstStageMount()
     : need_dm_verity_(false), device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) {
     if (!device_tree_fstab_) {
-        LOG(ERROR) << "Failed to read fstab from device tree";
+        // The client of FirstStageMount should check the existence of fstab in device-tree
+        // in advance, without parsing it. Reaching here means there is a FATAL error when
+        // parsing the fstab. So stop here to expose the failure.
+        LOG(FATAL) << "Failed to read fstab from device tree";
         return;
     }
     // Stores device_tree_fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>)
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 29a65ab..268873c 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -25,6 +25,7 @@
 #include "import_parser.h"
 #include "keyword_map.h"
 #include "parser.h"
+#include "service.h"
 #include "test_function_map.h"
 #include "util.h"
 
@@ -34,12 +35,13 @@
 using ActionManagerCommand = std::function<void(ActionManager&)>;
 
 void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
-              const std::vector<ActionManagerCommand>& commands) {
+              const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
     ActionManager am;
 
     Action::set_function_map(&test_function_map);
 
     Parser parser;
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(service_list, nullptr));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
@@ -55,11 +57,11 @@
 }
 
 void TestInitText(const std::string& init_script, const TestFunctionMap& test_function_map,
-                  const std::vector<ActionManagerCommand>& commands) {
+                  const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
     ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
-    TestInit(tf.path, test_function_map, commands);
+    TestInit(tf.path, test_function_map, commands, service_list);
 }
 
 TEST(init, SimpleEventTrigger) {
@@ -76,7 +78,8 @@
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
 
-    TestInitText(init_script, test_function_map, commands);
+    ServiceList service_list;
+    TestInitText(init_script, test_function_map, commands, &service_list);
 
     EXPECT_TRUE(expect_true);
 }
@@ -104,7 +107,30 @@
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
 
-    TestInitText(init_script, test_function_map, commands);
+    ServiceList service_list;
+    TestInitText(init_script, test_function_map, commands, &service_list);
+}
+
+TEST(init, OverrideService) {
+    std::string init_script = R"init(
+service A something
+    class first
+
+service A something
+    class second
+    override
+
+)init";
+
+    ServiceList service_list;
+    TestInitText(init_script, TestFunctionMap(), {}, &service_list);
+    ASSERT_EQ(1, std::distance(service_list.begin(), service_list.end()));
+
+    auto service = service_list.begin()->get();
+    ASSERT_NE(nullptr, service);
+    EXPECT_EQ(std::set<std::string>({"second"}), service->classnames());
+    EXPECT_EQ("A", service->name());
+    EXPECT_TRUE(service->is_override());
 }
 
 TEST(init, EventTriggerOrderMultipleFiles) {
@@ -162,7 +188,9 @@
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
 
-    TestInit(start.path, test_function_map, commands);
+    ServiceList service_list;
+
+    TestInit(start.path, test_function_map, commands, &service_list);
 
     EXPECT_EQ(6, num_executed);
 }
diff --git a/init/parser.cpp b/init/parser.cpp
index 8a4e798..6ddb09f 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -50,12 +50,24 @@
     state.nexttoken = 0;
 
     SectionParser* section_parser = nullptr;
+    int section_start_line = -1;
     std::vector<std::string> args;
 
+    auto end_section = [&] {
+        if (section_parser == nullptr) return;
+
+        if (auto result = section_parser->EndSection(); !result) {
+            LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
+        }
+
+        section_parser = nullptr;
+        section_start_line = -1;
+    };
+
     for (;;) {
         switch (next_token(&state)) {
             case T_EOF:
-                if (section_parser) section_parser->EndSection();
+                end_section();
                 return;
             case T_NEWLINE:
                 state.line++;
@@ -65,18 +77,18 @@
                 // uevent.
                 for (const auto& [prefix, callback] : line_callbacks_) {
                     if (android::base::StartsWith(args[0], prefix.c_str())) {
-                        if (section_parser) section_parser->EndSection();
+                        end_section();
 
                         if (auto result = callback(std::move(args)); !result) {
                             LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                         }
-                        section_parser = nullptr;
                         break;
                     }
                 }
                 if (section_parsers_.count(args[0])) {
-                    if (section_parser) section_parser->EndSection();
+                    end_section();
                     section_parser = section_parsers_[args[0]].get();
+                    section_start_line = state.line;
                     if (auto result =
                             section_parser->ParseSection(std::move(args), filename, state.line);
                         !result) {
diff --git a/init/parser.h b/init/parser.h
index 4ab24a4..110a468 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -26,24 +26,22 @@
 
 //  SectionParser is an interface that can parse a given 'section' in init.
 //
-//  You can implement up to 4 functions below, with ParseSection() being mandatory.
-//  The first two function return bool with false indicating a failure and has a std::string* err
-//  parameter into which an error string can be written.  It will be reported along with the
-//  filename and line number of where the error occurred.
+//  You can implement up to 4 functions below, with ParseSection being mandatory. The first two
+//  functions return Result<Success> indicating if they have an error. It will be reported along
+//  with the filename and line number of where the error occurred.
 //
-//  1) bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
-//                       int line, std::string* err)
+//  1) ParseSection
 //    This function is called when a section is first encountered.
 //
-//  2) bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err)
+//  2) ParseLineSection
 //    This function is called on each subsequent line until the next section is encountered.
 //
-//  3) bool EndSection()
+//  3) EndSection
 //    This function is called either when a new section is found or at the end of the file.
 //    It indicates that parsing of the current section is complete and any relevant objects should
 //    be committed.
 //
-//  4) bool EndFile()
+//  4) EndFile
 //    This function is called at the end of the file.
 //    It indicates that the parsing has completed and any relevant objects should be committed.
 
@@ -56,7 +54,7 @@
     virtual Result<Success> ParseSection(std::vector<std::string>&& args,
                                          const std::string& filename, int line) = 0;
     virtual Result<Success> ParseLineSection(std::vector<std::string>&&, int) { return Success(); };
-    virtual void EndSection(){};
+    virtual Result<Success> EndSection() { return Success(); };
     virtual void EndFile(){};
 };
 
diff --git a/init/reboot.cpp b/init/reboot.cpp
index b17dbaf..21086dc 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -280,16 +280,16 @@
         }
         bool unmount_done = true;
         if (emulated_devices.size() > 0) {
-            unmount_done = std::all_of(emulated_devices.begin(), emulated_devices.end(),
-                                       [](auto& entry) { return entry.Umount(false); });
+            for (auto& entry : emulated_devices) {
+                if (!entry.Umount(false)) unmount_done = false;
+            }
             if (unmount_done) {
                 sync();
             }
         }
-        unmount_done =
-            std::all_of(block_devices.begin(), block_devices.end(),
-                        [&timeout](auto& entry) { return entry.Umount(timeout == 0ms); }) &&
-            unmount_done;
+        for (auto& entry : block_devices) {
+            if (!entry.Umount(timeout == 0ms)) unmount_done = false;
+        }
         if (unmount_done) {
             return UMOUNT_STAT_SUCCESS;
         }
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 4a3a271..3f9f7f4 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -115,7 +115,7 @@
         // fork succeeded -- this is executing in the child process
 
         // Close the pipe FD not used by this process
-        TEMP_FAILURE_RETRY(close(pipe_fds[0]));
+        close(pipe_fds[0]);
 
         // Redirect stderr to the pipe FD provided by the parent
         if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
@@ -123,7 +123,7 @@
             _exit(127);
             return false;
         }
-        TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+        close(pipe_fds[1]);
 
         if (execv(filename, argv) == -1) {
             PLOG(ERROR) << "Failed to execve " << filename;
@@ -137,7 +137,7 @@
         // fork succeeded -- this is executing in the original/parent process
 
         // Close the pipe FD not used by this process
-        TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+        close(pipe_fds[1]);
 
         // Log the redirected output of the child process.
         // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
@@ -148,7 +148,7 @@
         if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
             PLOG(ERROR) << "Failed to capture full output of " << filename;
         }
-        TEMP_FAILURE_RETRY(close(child_out_fd));
+        close(child_out_fd);
         if (!child_output.empty()) {
             // Log captured output, line by line, because LOG expects to be invoked for each line
             std::istringstream in(child_output);
diff --git a/init/service.cpp b/init/service.cpp
index b339bc0..481df65 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -37,6 +37,7 @@
 #include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <hidl-util/FQName.h>
 #include <processgroup/processgroup.h>
 #include <selinux/selinux.h>
 #include <system/thread_defs.h>
@@ -100,8 +101,22 @@
 
     // It's OK to LOG(FATAL) in this function since it's running in the first
     // child process.
-    if (mount("", "/proc", "proc", kSafeFlags | MS_REMOUNT, "") == -1) {
-        PLOG(FATAL) << "couldn't remount(/proc) for " << service_name;
+
+    // Recursively remount / as slave like zygote does so unmounting and mounting /proc
+    // doesn't interfere with the parent namespace's /proc mount. This will also
+    // prevent any other mounts/unmounts initiated by the service from interfering
+    // with the parent namespace but will still allow mount events from the parent
+    // namespace to propagate to the child.
+    if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
+        PLOG(FATAL) << "couldn't remount(/) recursively as slave for " << service_name;
+    }
+    // umount() then mount() /proc.
+    // Note that it is not sufficient to mount with MS_REMOUNT.
+    if (umount("/proc") == -1) {
+        PLOG(FATAL) << "couldn't umount(/proc) for " << service_name;
+    }
+    if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
+        PLOG(FATAL) << "couldn't mount(/proc) for " << service_name;
     }
 
     if (prctl(PR_SET_NAME, service_name.c_str()) == -1) {
@@ -418,6 +433,37 @@
     return Success();
 }
 
+Result<Success> Service::ParseInterface(const std::vector<std::string>& args) {
+    const std::string& interface_name = args[1];
+    const std::string& instance_name = args[2];
+
+    const FQName fq_name = FQName(interface_name);
+    if (!fq_name.isValid()) {
+        return Error() << "Invalid fully-qualified name for interface '" << interface_name << "'";
+    }
+
+    if (!fq_name.isFullyQualified()) {
+        return Error() << "Interface name not fully-qualified '" << interface_name << "'";
+    }
+
+    if (fq_name.isValidValueName()) {
+        return Error() << "Interface name must not be a value name '" << interface_name << "'";
+    }
+
+    const std::string fullname = interface_name + "/" + instance_name;
+
+    for (const auto& svc : ServiceList::GetInstance()) {
+        if (svc->interfaces().count(fullname) > 0) {
+            return Error() << "Interface '" << fullname << "' redefined in " << name()
+                           << " but is already defined by " << svc->name();
+        }
+    }
+
+    interfaces_.insert(fullname);
+
+    return Success();
+}
+
 Result<Success> Service::ParseIoprio(const std::vector<std::string>& args) {
     if (!ParseInt(args[2], &ioprio_pri_, 0, 7)) {
         return Error() << "priority value must be range 0 - 7";
@@ -484,6 +530,11 @@
     return Success();
 }
 
+Result<Success> Service::ParseOverride(const std::vector<std::string>& args) {
+    override_ = true;
+    return Success();
+}
+
 Result<Success> Service::ParseMemcgSwappiness(const std::vector<std::string>& args) {
     if (!ParseInt(args[1], &swappiness_, 0)) {
         return Error() << "swappiness value must be equal or greater than 0";
@@ -619,11 +670,13 @@
         {"critical",    {0,     0,    &Service::ParseCritical}},
         {"disabled",    {0,     0,    &Service::ParseDisabled}},
         {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
+        {"interface",   {2,     2,    &Service::ParseInterface}},
         {"ioprio",      {2,     2,    &Service::ParseIoprio}},
         {"priority",    {1,     1,    &Service::ParsePriority}},
         {"keycodes",    {1,     kMax, &Service::ParseKeycodes}},
         {"oneshot",     {0,     0,    &Service::ParseOneshot}},
         {"onrestart",   {1,     kMax, &Service::ParseOnrestart}},
+        {"override",    {0,     0,    &Service::ParseOverride}},
         {"oom_score_adjust",
                         {1,     1,    &Service::ParseOomScoreAdjust}},
         {"memcg.swappiness",
@@ -673,14 +726,20 @@
 }
 
 Result<Success> Service::Start() {
+    bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
     // Starting a service removes it from the disabled or reset state and
     // immediately takes it out of the restarting state if it was in there.
     flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
 
     // Running processes require no additional work --- if they're in the
     // process of exiting, we've ensured that they will immediately restart
-    // on exit, unless they are ONESHOT.
+    // on exit, unless they are ONESHOT. For ONESHOT service, if it's in
+    // stopping status, we just set SVC_RESTART flag so it will get restarted
+    // in Reap().
     if (flags_ & SVC_RUNNING) {
+        if ((flags_ & SVC_ONESHOT) && disabled) {
+            flags_ |= SVC_RESTART;
+        }
         // It is not an error to try to start a service that is already running.
         return Success();
     }
@@ -907,6 +966,13 @@
     } else {
         flags_ |= how;
     }
+    // Make sure it's in right status when a restart immediately follow a
+    // stop/reset or vice versa.
+    if (how == SVC_RESTART) {
+        flags_ &= (~(SVC_DISABLED | SVC_RESET));
+    } else {
+        flags_ &= (~SVC_RESTART);
+    }
 
     if (pid_) {
         KillProcessGroup(SIGKILL);
@@ -1051,11 +1117,6 @@
         return Error() << "invalid service name '" << name << "'";
     }
 
-    Service* old_service = service_list_->FindService(name);
-    if (old_service) {
-        return Error() << "ignored duplicate definition of service '" << name << "'";
-    }
-
     Subcontext* restart_action_subcontext = nullptr;
     if (subcontexts_) {
         for (auto& subcontext : *subcontexts_) {
@@ -1075,10 +1136,23 @@
     return service_ ? service_->ParseLine(std::move(args)) : Success();
 }
 
-void ServiceParser::EndSection() {
+Result<Success> ServiceParser::EndSection() {
     if (service_) {
+        Service* old_service = service_list_->FindService(service_->name());
+        if (old_service) {
+            if (!service_->is_override()) {
+                return Error() << "ignored duplicate definition of service '" << service_->name()
+                               << "'";
+            }
+
+            service_list_->RemoveService(*old_service);
+            old_service = nullptr;
+        }
+
         service_list_->AddService(std::move(service_));
     }
+
+    return Success();
 }
 
 bool ServiceParser::IsValidName(const std::string& name) const {
diff --git a/init/service.h b/init/service.h
index 89dd780..d46a413 100644
--- a/init/service.h
+++ b/init/service.h
@@ -108,8 +108,10 @@
     void set_keychord_id(int keychord_id) { keychord_id_ = keychord_id; }
     IoSchedClass ioprio_class() const { return ioprio_class_; }
     int ioprio_pri() const { return ioprio_pri_; }
+    const std::set<std::string>& interfaces() const { return interfaces_; }
     int priority() const { return priority_; }
     int oom_score_adjust() const { return oom_score_adjust_; }
+    bool is_override() const { return override_; }
     bool process_cgroup_empty() const { return process_cgroup_empty_; }
     unsigned long start_order() const { return start_order_; }
     const std::vector<std::string>& args() const { return args_; }
@@ -132,11 +134,13 @@
     Result<Success> ParseDisabled(const std::vector<std::string>& args);
     Result<Success> ParseGroup(const std::vector<std::string>& args);
     Result<Success> ParsePriority(const std::vector<std::string>& args);
+    Result<Success> ParseInterface(const std::vector<std::string>& args);
     Result<Success> ParseIoprio(const std::vector<std::string>& args);
     Result<Success> ParseKeycodes(const std::vector<std::string>& args);
     Result<Success> ParseOneshot(const std::vector<std::string>& args);
     Result<Success> ParseOnrestart(const std::vector<std::string>& args);
     Result<Success> ParseOomScoreAdjust(const std::vector<std::string>& args);
+    Result<Success> ParseOverride(const std::vector<std::string>& args);
     Result<Success> ParseMemcgLimitInBytes(const std::vector<std::string>& args);
     Result<Success> ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args);
     Result<Success> ParseMemcgSwappiness(const std::vector<std::string>& args);
@@ -181,6 +185,8 @@
 
     std::vector<std::string> writepid_files_;
 
+    std::set<std::string> interfaces_;  // e.g. some.package.foo@1.0::IBaz/instance-name
+
     // keycodes for triggering this service via /dev/keychord
     std::vector<int> keycodes_;
     int keychord_id_;
@@ -197,6 +203,8 @@
 
     bool process_cgroup_empty_ = false;
 
+    bool override_ = false;
+
     unsigned long start_order_;
 
     std::vector<std::pair<int, rlimit>> rlimits_;
@@ -244,7 +252,7 @@
     Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                  int line) override;
     Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
-    void EndSection() override;
+    Result<Success> EndSection() override;
 
   private:
     bool IsValidName(const std::string& name) const;
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 927953d..84feeee 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -28,6 +28,7 @@
 #include <selinux/android.h>
 
 #include "action.h"
+#include "selinux.h"
 #include "system/core/init/subcontext.pb.h"
 #include "util.h"
 
@@ -165,6 +166,7 @@
     auto context = std::string(argv[2]);
     auto init_fd = std::atoi(argv[3]);
 
+    SelabelInitialize();
     auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
     subcontext_process.MainLoop();
     return 0;
diff --git a/init/subcontext.h b/init/subcontext.h
index ac77e08..eadabee 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -35,7 +35,7 @@
 class Subcontext {
   public:
     Subcontext(std::string path_prefix, std::string context)
-        : path_prefix_(path_prefix), context_(std::move(context)) {
+        : path_prefix_(std::move(path_prefix)), context_(std::move(context)), pid_(0) {
         Fork();
     }
 
@@ -55,21 +55,6 @@
     android::base::unique_fd socket_;
 };
 
-// For testing, to kill the subcontext after the test has completed.
-class SubcontextKiller {
-  public:
-    SubcontextKiller(const Subcontext& subcontext) : subcontext_(subcontext) {}
-    ~SubcontextKiller() {
-        if (subcontext_.pid() > 0) {
-            kill(subcontext_.pid(), SIGTERM);
-            kill(subcontext_.pid(), SIGKILL);
-        }
-    }
-
-  private:
-    const Subcontext& subcontext_;
-};
-
 int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map);
 std::vector<Subcontext>* InitializeSubcontexts();
 bool SubcontextChildReap(pid_t pid);
diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp
index a62b959..6307993 100644
--- a/init/subcontext_benchmark.cpp
+++ b/init/subcontext_benchmark.cpp
@@ -17,6 +17,7 @@
 #include "subcontext.h"
 
 #include <benchmark/benchmark.h>
+#include <selinux/selinux.h>
 
 #include "test_function_map.h"
 
@@ -24,12 +25,27 @@
 namespace init {
 
 static void BenchmarkSuccess(benchmark::State& state) {
-    auto subcontext = Subcontext("path", kVendorContext);
-    auto subcontext_killer = SubcontextKiller(subcontext);
+    if (getuid() != 0) {
+        state.SkipWithError("Skipping benchmark, must be run as root.");
+        return;
+    }
+    char* context;
+    if (getcon(&context) != 0) {
+        state.SkipWithError("getcon() failed");
+        return;
+    }
+
+    auto subcontext = Subcontext("path", context);
+    free(context);
 
     while (state.KeepRunning()) {
         subcontext.Execute(std::vector<std::string>{"return_success"});
     }
+
+    if (subcontext.pid() > 0) {
+        kill(subcontext.pid(), SIGTERM);
+        kill(subcontext.pid(), SIGKILL);
+    }
 }
 
 BENCHMARK(BenchmarkSuccess);
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index 60b45b9..ca45266 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -23,6 +23,7 @@
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <gtest/gtest.h>
+#include <selinux/selinux.h>
 
 #include "builtin_arguments.h"
 #include "test_function_map.h"
@@ -38,88 +39,108 @@
 namespace android {
 namespace init {
 
+// I would use test fixtures, but I cannot skip the test if not root with them, so instead we have
+// this test runner.
+template <typename F>
+void RunTest(F&& test_function) {
+    if (getuid() != 0) {
+        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        return;
+    }
+
+    char* context;
+    ASSERT_EQ(0, getcon(&context));
+    auto context_string = std::string(context);
+    free(context);
+
+    auto subcontext = Subcontext("dummy_path", context_string);
+    ASSERT_NE(0, subcontext.pid());
+
+    test_function(subcontext, context_string);
+
+    if (subcontext.pid() > 0) {
+        kill(subcontext.pid(), SIGTERM);
+        kill(subcontext.pid(), SIGKILL);
+    }
+}
+
 TEST(subcontext, CheckDifferentPid) {
-    auto subcontext = Subcontext("path", kVendorContext);
-    auto subcontext_killer = SubcontextKiller(subcontext);
+    RunTest([](auto& subcontext, auto& context_string) {
+        auto result = subcontext.Execute(std::vector<std::string>{"return_pids_as_error"});
+        ASSERT_FALSE(result);
 
-    auto result = subcontext.Execute(std::vector<std::string>{"return_pids_as_error"});
-    ASSERT_FALSE(result);
-
-    auto pids = Split(result.error_string(), " ");
-    ASSERT_EQ(2U, pids.size());
-    auto our_pid = std::to_string(getpid());
-    EXPECT_NE(our_pid, pids[0]);
-    EXPECT_EQ(our_pid, pids[1]);
+        auto pids = Split(result.error_string(), " ");
+        ASSERT_EQ(2U, pids.size());
+        auto our_pid = std::to_string(getpid());
+        EXPECT_NE(our_pid, pids[0]);
+        EXPECT_EQ(our_pid, pids[1]);
+    });
 }
 
 TEST(subcontext, SetProp) {
-    auto subcontext = Subcontext("path", kVendorContext);
-    auto subcontext_killer = SubcontextKiller(subcontext);
+    RunTest([](auto& subcontext, auto& context_string) {
+        SetProperty("init.test.subcontext", "fail");
+        WaitForProperty("init.test.subcontext", "fail");
 
-    SetProperty("init.test.subcontext", "fail");
-    WaitForProperty("init.test.subcontext", "fail");
-
-    auto args = std::vector<std::string>{
-        "setprop",
-        "init.test.subcontext",
-        "success",
-    };
-    auto result = subcontext.Execute(args);
-    ASSERT_TRUE(result) << result.error();
-
-    EXPECT_TRUE(WaitForProperty("init.test.subcontext", "success", 10s));
-}
-
-TEST(subcontext, MultipleCommands) {
-    auto subcontext = Subcontext("path", kVendorContext);
-    auto subcontext_killer = SubcontextKiller(subcontext);
-
-    auto first_pid = subcontext.pid();
-
-    auto expected_words = std::vector<std::string>{
-        "this",
-        "is",
-        "a",
-        "test",
-    };
-
-    for (const auto& word : expected_words) {
         auto args = std::vector<std::string>{
-            "add_word",
-            word,
+            "setprop",
+            "init.test.subcontext",
+            "success",
         };
         auto result = subcontext.Execute(args);
         ASSERT_TRUE(result) << result.error();
-    }
 
-    auto result = subcontext.Execute(std::vector<std::string>{"return_words_as_error"});
-    ASSERT_FALSE(result);
-    EXPECT_EQ(Join(expected_words, " "), result.error_string());
-    EXPECT_EQ(first_pid, subcontext.pid());
+        EXPECT_TRUE(WaitForProperty("init.test.subcontext", "success", 10s));
+    });
+}
+
+TEST(subcontext, MultipleCommands) {
+    RunTest([](auto& subcontext, auto& context_string) {
+        auto first_pid = subcontext.pid();
+
+        auto expected_words = std::vector<std::string>{
+            "this",
+            "is",
+            "a",
+            "test",
+        };
+
+        for (const auto& word : expected_words) {
+            auto args = std::vector<std::string>{
+                "add_word",
+                word,
+            };
+            auto result = subcontext.Execute(args);
+            ASSERT_TRUE(result) << result.error();
+        }
+
+        auto result = subcontext.Execute(std::vector<std::string>{"return_words_as_error"});
+        ASSERT_FALSE(result);
+        EXPECT_EQ(Join(expected_words, " "), result.error_string());
+        EXPECT_EQ(first_pid, subcontext.pid());
+    });
 }
 
 TEST(subcontext, RecoverAfterAbort) {
-    auto subcontext = Subcontext("path", kVendorContext);
-    auto subcontext_killer = SubcontextKiller(subcontext);
+    RunTest([](auto& subcontext, auto& context_string) {
+        auto first_pid = subcontext.pid();
 
-    auto first_pid = subcontext.pid();
+        auto result = subcontext.Execute(std::vector<std::string>{"cause_log_fatal"});
+        ASSERT_FALSE(result);
 
-    auto result = subcontext.Execute(std::vector<std::string>{"cause_log_fatal"});
-    ASSERT_FALSE(result);
-
-    auto result2 = subcontext.Execute(std::vector<std::string>{"generate_sane_error"});
-    ASSERT_FALSE(result2);
-    EXPECT_EQ("Sane error!", result2.error_string());
-    EXPECT_NE(subcontext.pid(), first_pid);
+        auto result2 = subcontext.Execute(std::vector<std::string>{"generate_sane_error"});
+        ASSERT_FALSE(result2);
+        EXPECT_EQ("Sane error!", result2.error_string());
+        EXPECT_NE(subcontext.pid(), first_pid);
+    });
 }
 
 TEST(subcontext, ContextString) {
-    auto subcontext = Subcontext("path", kVendorContext);
-    auto subcontext_killer = SubcontextKiller(subcontext);
-
-    auto result = subcontext.Execute(std::vector<std::string>{"return_context_as_error"});
-    ASSERT_FALSE(result);
-    ASSERT_EQ(kVendorContext, result.error_string());
+    RunTest([](auto& subcontext, auto& context_string) {
+        auto result = subcontext.Execute(std::vector<std::string>{"return_context_as_error"});
+        ASSERT_FALSE(result);
+        ASSERT_EQ(context_string, result.error_string());
+    });
 }
 
 TestFunctionMap BuildTestFunctionMap() {
diff --git a/init/test_service/Android.bp b/init/test_service/Android.bp
index 9bd6f27..6399699 100644
--- a/init/test_service/Android.bp
+++ b/init/test_service/Android.bp
@@ -17,6 +17,7 @@
 cc_binary {
     name: "test_service",
     srcs: ["test_service.cpp"],
+    cflags: ["-Wall", "-Werror"],
     shared_libs: ["libbase"],
     init_rc: ["test_service.rc"],
 }
diff --git a/init/test_service/test_service.cpp b/init/test_service/test_service.cpp
index e7206f8..71d1ea4 100644
--- a/init/test_service/test_service.cpp
+++ b/init/test_service/test_service.cpp
@@ -59,7 +59,6 @@
     }
 
     bool test_fails = false;
-    size_t uargc = static_cast<size_t>(argc);  // |argc| >= 3.
     for (size_t i = 1; i < static_cast<size_t>(argc); i = i + 2) {
         std::string expected_value = argv[i + 1];
         auto f = fields.find(argv[i]);
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index cd7adb4..f74c878 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -132,8 +132,10 @@
     return std::invoke(*parser, this, std::move(args));
 }
 
-void SubsystemParser::EndSection() {
+Result<Success> SubsystemParser::EndSection() {
     subsystems_->emplace_back(std::move(subsystem_));
+
+    return Success();
 }
 
 }  // namespace init
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 18d1027..83684f3 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -32,7 +32,7 @@
     Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                  int line) override;
     Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
-    void EndSection() override;
+    Result<Success> EndSection() override;
 
   private:
     Result<Success> ParseDevName(std::vector<std::string>&& args);
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 9a637ac..9a12f0d 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -14,20 +14,19 @@
 // limitations under the License.
 //
 
-libasyncio_cppflags = [
-    "-Wall",
-    "-Wextra",
-    "-Werror",
-]
+cc_defaults {
+    name: "libasyncio_defaults",
+    cflags: ["-Wall", "-Werror", "-Wextra"],
+}
 
 cc_library {
     name: "libasyncio",
+    defaults: ["libasyncio_defaults"],
     vendor_available: true,
     host_supported: true,
     srcs: [
         "AsyncIO.cpp",
     ],
-    cppflags: libasyncio_cppflags,
 
     export_include_dirs: ["include"],
     target: {
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 2f20684..0b2ce1d 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -65,7 +65,7 @@
 
 cc_library {
     name: "libbacktrace",
-    vendor_available: true,
+    vendor_available: false,
     vndk: {
         enabled: true,
         support_system_process: true,
@@ -73,6 +73,10 @@
     defaults: ["libbacktrace_common"],
     host_supported: true,
 
+    cflags: [
+        "-Wexit-time-destructors",
+    ],
+
     srcs: [
         "BacktraceMap.cpp",
     ],
@@ -83,20 +87,7 @@
         darwin: {
             enabled: true,
         },
-        linux_glibc: {
-            srcs: libbacktrace_sources,
-
-            shared_libs: [
-                "libbase",
-                "liblog",
-                "libunwind",
-                "libunwindstack",
-            ],
-
-            static_libs: ["libcutils"],
-        },
-        linux_bionic: {
-            enabled: true,
+        linux: {
             srcs: libbacktrace_sources,
 
             shared_libs: [
@@ -109,16 +100,7 @@
             static_libs: ["libcutils"],
         },
         android: {
-            srcs: libbacktrace_sources,
-
-            shared_libs: [
-                "libbase",
-                "liblog",
-                "libunwind",
-                "libunwindstack",
-            ],
-
-            static_libs: ["libasync_safe", "libcutils"],
+            static_libs: ["libasync_safe"],
         },
     },
     whole_static_libs: ["libdemangle"],
@@ -135,13 +117,7 @@
     srcs: ["backtrace_testlib.cpp"],
 
     target: {
-        linux_glibc: {
-            shared_libs: [
-                "libunwind",
-                "libunwindstack",
-            ],
-        },
-        android: {
+        linux: {
             shared_libs: [
                 "libunwind",
                 "libunwindstack",
@@ -258,5 +234,6 @@
     shared_libs: [
         "libbacktrace",
         "libbase",
+        "libunwindstack",
     ],
 }
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 81f5e32..e18dbf3 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -30,9 +30,10 @@
 #include <demangle.h>
 
 #include "BacktraceLog.h"
-#include "thread_utils.h"
 #include "UnwindCurrent.h"
 #include "UnwindPtrace.h"
+#include "UnwindStack.h"
+#include "thread_utils.h"
 
 using android::base::StringPrintf;
 
@@ -134,9 +135,9 @@
   }
 
   if (pid == getpid()) {
-    return new UnwindCurrent(pid, tid, map);
+    return new UnwindStackCurrent(pid, tid, map);
   } else {
-    return new UnwindPtrace(pid, tid, map);
+    return new UnwindStackPtrace(pid, tid, map);
   }
 }
 
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
index 0b8232b..3cab0d1 100644
--- a/libbacktrace/UnwindMap.cpp
+++ b/libbacktrace/UnwindMap.cpp
@@ -146,24 +146,3 @@
     }
   }
 }
-
-//-------------------------------------------------------------------------
-// BacktraceMap create function.
-//-------------------------------------------------------------------------
-BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
-  BacktraceMap* map;
-
-  if (uncached) {
-    // Force use of the base class to parse the maps when this call is made.
-    map = new BacktraceMap(pid);
-  } else if (pid == getpid()) {
-    map = new UnwindMapLocal();
-  } else {
-    map = new UnwindMapRemote(pid);
-  }
-  if (!map->Build()) {
-    delete map;
-    return nullptr;
-  }
-  return map;
-}
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 41153ce..3a38839 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -15,7 +15,6 @@
  */
 
 #define _GNU_SOURCE 1
-#include <assert.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -44,36 +43,14 @@
 #include "UnwindStack.h"
 #include "UnwindStackMap.h"
 
-static std::string GetFunctionName(BacktraceMap* back_map, uintptr_t pc, uintptr_t* offset) {
-  *offset = 0;
-  unwindstack::Maps* maps = reinterpret_cast<UnwindStackMap*>(back_map)->stack_maps();
-
-  // Get the map for this
-  unwindstack::MapInfo* map_info = maps->Find(pc);
-  if (map_info == nullptr || map_info->flags & PROT_DEVICE_MAP) {
-    return "";
-  }
-
-  UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
-  unwindstack::Elf* elf = map_info->GetElf(stack_map->process_memory(), true);
-
-  std::string name;
-  uint64_t func_offset;
-  if (!elf->GetFunctionName(elf->GetRelPc(pc, map_info), &name, &func_offset)) {
-    return "";
-  }
-  *offset = func_offset;
-  return name;
-}
-
-static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
-                   std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames) {
-  static std::set<std::string> skip_names{"libunwindstack.so", "libbacktrace.so"};
+bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
+                       std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
+                       std::vector<std::string>* skip_names) {
   UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
   auto process_memory = stack_map->process_memory();
   unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
                                  regs, stack_map->process_memory());
-  unwinder.Unwind(&skip_names);
+  unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore());
 
   if (num_ignore_frames >= unwinder.NumFrames()) {
     frames->resize(0);
@@ -87,13 +64,13 @@
     auto frame = &unwinder_frames[i];
     backtrace_frame_data_t* back_frame = &frames->at(cur_frame);
 
-    back_frame->num = frame->num;
+    back_frame->num = frame->num - num_ignore_frames;
 
     back_frame->rel_pc = frame->rel_pc;
     back_frame->pc = frame->pc;
     back_frame->sp = frame->sp;
 
-    back_frame->func_name = frame->function_name;
+    back_frame->func_name = demangle(frame->function_name.c_str());
     back_frame->func_offset = frame->function_offset;
 
     back_frame->map.name = frame->map_name;
@@ -111,7 +88,7 @@
     : BacktraceCurrent(pid, tid, map) {}
 
 std::string UnwindStackCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
-  return ::GetFunctionName(GetMap(), pc, offset);
+  return GetMap()->GetFunctionName(pc, offset);
 }
 
 bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) {
@@ -127,14 +104,15 @@
   }
 
   error_ = BACKTRACE_UNWIND_NO_ERROR;
-  return ::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames);
+  std::vector<std::string> skip_names{"libunwindstack.so", "libbacktrace.so"};
+  return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, &skip_names);
 }
 
 UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
     : BacktracePtrace(pid, tid, map) {}
 
 std::string UnwindStackPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
-  return ::GetFunctionName(GetMap(), pc, offset);
+  return GetMap()->GetFunctionName(pc, offset);
 }
 
 bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, ucontext_t* context) {
@@ -147,33 +125,5 @@
   }
 
   error_ = BACKTRACE_UNWIND_NO_ERROR;
-  return ::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames);
-}
-
-Backtrace* Backtrace::CreateNew(pid_t pid, pid_t tid, BacktraceMap* map) {
-  if (pid == BACKTRACE_CURRENT_PROCESS) {
-    pid = getpid();
-    if (tid == BACKTRACE_CURRENT_THREAD) {
-      tid = gettid();
-    }
-  } else if (tid == BACKTRACE_CURRENT_THREAD) {
-    tid = pid;
-  }
-
-  if (map == nullptr) {
-// This would cause the wrong type of map object to be created, so disallow.
-#if defined(__ANDROID__)
-    __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__,
-              "Backtrace::CreateNew() must be called with a real map pointer.");
-#else
-    BACK_LOGE("Backtrace::CreateNew() must be called with a real map pointer.");
-    abort();
-#endif
-  }
-
-  if (pid == getpid()) {
-    return new UnwindStackCurrent(pid, tid, map);
-  } else {
-    return new UnwindStackPtrace(pid, tid, map);
-  }
+  return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, nullptr);
 }
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index d4a2444..25e5002 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -75,10 +75,35 @@
   map->load_bias = elf->GetLoadBias();
 }
 
+std::string UnwindStackMap::GetFunctionName(uintptr_t pc, uintptr_t* offset) {
+  *offset = 0;
+  unwindstack::Maps* maps = stack_maps();
+
+  // Get the map for this
+  unwindstack::MapInfo* map_info = maps->Find(pc);
+  if (map_info == nullptr || map_info->flags & PROT_DEVICE_MAP) {
+    return "";
+  }
+
+  unwindstack::Elf* elf = map_info->GetElf(process_memory(), true);
+
+  std::string name;
+  uint64_t func_offset;
+  if (!elf->GetFunctionName(elf->GetRelPc(pc, map_info), &name, &func_offset)) {
+    return "";
+  }
+  *offset = func_offset;
+  return name;
+}
+
+std::shared_ptr<unwindstack::Memory> UnwindStackMap::GetProcessMemory() {
+  return process_memory_;
+}
+
 //-------------------------------------------------------------------------
 // BacktraceMap create function.
 //-------------------------------------------------------------------------
-BacktraceMap* BacktraceMap::CreateNew(pid_t pid, bool uncached) {
+BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
   BacktraceMap* map;
 
   if (uncached) {
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index b93b340..bc432e7 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -34,6 +34,9 @@
 
   void FillIn(uintptr_t addr, backtrace_map_t* map) override;
 
+  virtual std::string GetFunctionName(uintptr_t pc, uintptr_t* offset) override;
+  virtual std::shared_ptr<unwindstack::Memory> GetProcessMemory() override final;
+
   unwindstack::Maps* stack_maps() { return stack_maps_.get(); }
 
   const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; }
diff --git a/libbacktrace/backtrace_benchmarks.cpp b/libbacktrace/backtrace_benchmarks.cpp
index 30c2a55..bb4134f 100644
--- a/libbacktrace/backtrace_benchmarks.cpp
+++ b/libbacktrace/backtrace_benchmarks.cpp
@@ -32,13 +32,13 @@
 
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
+#include <unwindstack/Memory.h>
 
 // Definitions of prctl arguments to set a vma name in Android kernels.
 #define ANDROID_PR_SET_VMA 0x53564d41
 #define ANDROID_PR_SET_VMA_ANON_NAME 0
 
 constexpr size_t kNumMaps = 2000;
-constexpr size_t kNumIterations = 1000;
 
 static bool CountMaps(pid_t pid, size_t* num_maps) {
   // Minimize the calls that might allocate memory. If too much memory
@@ -70,7 +70,6 @@
 }
 
 static void CreateMap(benchmark::State& state, BacktraceMap* (*map_func)(pid_t, bool)) {
-  state.PauseTiming();
   // Create a remote process so that the map data is exactly the same.
   // Also, so that we can create a set number of maps.
   pid_t pid;
@@ -132,18 +131,14 @@
     return;
   }
 
-  state.ResumeTiming();
   while (state.KeepRunning()) {
-    for (size_t i = 0; i < static_cast<size_t>(state.range(0)); i++) {
-      BacktraceMap* map = map_func(pid, false);
-      if (map == nullptr) {
-        fprintf(stderr, "Failed to create map\n");
-        return;
-      }
-      delete map;
+    BacktraceMap* map = map_func(pid, false);
+    if (map == nullptr) {
+      fprintf(stderr, "Failed to create map\n");
+      return;
     }
+    delete map;
   }
-  state.PauseTiming();
 
   kill(pid, SIGKILL);
   waitpid(pid, nullptr, 0);
@@ -152,11 +147,21 @@
 static void BM_create_map(benchmark::State& state) {
   CreateMap(state, BacktraceMap::Create);
 }
-BENCHMARK(BM_create_map)->Arg(kNumIterations);
+BENCHMARK(BM_create_map);
 
-static void BM_create_map_new(benchmark::State& state) {
-  CreateMap(state, BacktraceMap::CreateNew);
+using BacktraceCreateFn = decltype(Backtrace::Create);
+
+static void CreateBacktrace(benchmark::State& state, BacktraceMap* map, BacktraceCreateFn fn) {
+  while (state.KeepRunning()) {
+    std::unique_ptr<Backtrace> backtrace(fn(getpid(), gettid(), map));
+    backtrace->Unwind(0);
+  }
 }
-BENCHMARK(BM_create_map_new)->Arg(kNumIterations);
+
+static void BM_create_backtrace(benchmark::State& state) {
+  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(getpid()));
+  CreateBacktrace(state, backtrace_map.get(), Backtrace::Create);
+}
+BENCHMARK(BM_create_backtrace);
 
 BENCHMARK_MAIN();
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index e5eb9e3..0a60ec4 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -368,20 +368,6 @@
   ASSERT_EQ(waitpid(pid, &status, 0), pid);
 }
 
-TEST(libbacktrace, ptrace_trace_new) {
-  pid_t pid;
-  if ((pid = fork()) == 0) {
-    ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
-    _exit(1);
-  }
-  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyLevelDump,
-                 Backtrace::CreateNew, BacktraceMap::CreateNew);
-
-  kill(pid, SIGKILL);
-  int status;
-  ASSERT_EQ(waitpid(pid, &status, 0), pid);
-}
-
 TEST(libbacktrace, ptrace_max_trace) {
   pid_t pid;
   if ((pid = fork()) == 0) {
@@ -396,20 +382,6 @@
   ASSERT_EQ(waitpid(pid, &status, 0), pid);
 }
 
-TEST(libbacktrace, ptrace_max_trace_new) {
-  pid_t pid;
-  if ((pid = fork()) == 0) {
-    ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES + 10, nullptr, nullptr), 0);
-    _exit(1);
-  }
-  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyMaxBacktrace, VerifyMaxDump,
-                 Backtrace::CreateNew, BacktraceMap::CreateNew);
-
-  kill(pid, SIGKILL);
-  int status;
-  ASSERT_EQ(waitpid(pid, &status, 0), pid);
-}
-
 static void VerifyProcessIgnoreFrames(Backtrace* bt_all, create_func_t create_func,
                                       map_create_func_t map_create_func) {
   std::unique_ptr<BacktraceMap> map(map_create_func(bt_all->Pid(), false));
@@ -440,20 +412,6 @@
   ASSERT_EQ(waitpid(pid, &status, 0), pid);
 }
 
-TEST(libbacktrace, ptrace_ignore_frames_new) {
-  pid_t pid;
-  if ((pid = fork()) == 0) {
-    ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
-    _exit(1);
-  }
-  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyProcessIgnoreFrames,
-                 Backtrace::CreateNew, BacktraceMap::CreateNew);
-
-  kill(pid, SIGKILL);
-  int status;
-  ASSERT_EQ(waitpid(pid, &status, 0), pid);
-}
-
 // Create a process with multiple threads and dump all of the threads.
 static void* PtraceThreadLevelRun(void*) {
   EXPECT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
@@ -517,45 +475,6 @@
   FinishRemoteProcess(pid);
 }
 
-TEST(libbacktrace, ptrace_threads_new) {
-  pid_t pid;
-  if ((pid = fork()) == 0) {
-    for (size_t i = 0; i < NUM_PTRACE_THREADS; i++) {
-      pthread_attr_t attr;
-      pthread_attr_init(&attr);
-      pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-
-      pthread_t thread;
-      ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, nullptr) == 0);
-    }
-    ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
-    _exit(1);
-  }
-
-  // Check to see that all of the threads are running before unwinding.
-  std::vector<pid_t> threads;
-  uint64_t start = NanoTime();
-  do {
-    usleep(US_PER_MSEC);
-    threads.clear();
-    GetThreads(pid, &threads);
-  } while ((threads.size() != NUM_PTRACE_THREADS + 1) && ((NanoTime() - start) <= 5 * NS_PER_SEC));
-  ASSERT_EQ(threads.size(), static_cast<size_t>(NUM_PTRACE_THREADS + 1));
-
-  ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
-  WaitForStop(pid);
-  for (std::vector<int>::const_iterator it = threads.begin(); it != threads.end(); ++it) {
-    // Skip the current forked process, we only care about the threads.
-    if (pid == *it) {
-      continue;
-    }
-    VerifyProcTest(pid, *it, ReadyLevelBacktrace, VerifyLevelDump, Backtrace::CreateNew,
-                   BacktraceMap::CreateNew);
-  }
-
-  FinishRemoteProcess(pid);
-}
-
 void VerifyLevelThread(void*) {
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
   ASSERT_TRUE(backtrace.get() != nullptr);
@@ -1663,7 +1582,7 @@
   munmap(device_map, DEVICE_MAP_SIZE);
 }
 
-TEST(libbacktrace, unwind_disallow_device_map_remote_new) {
+TEST(libbacktrace, unwind_disallow_device_map_remote) {
   void* device_map;
   SetupDeviceMap(&device_map);
 
@@ -1672,9 +1591,7 @@
   CreateRemoteProcess(&pid);
 
   // Now create an unwind object.
-  std::unique_ptr<BacktraceMap> map(BacktraceMap::CreateNew(pid));
-  ASSERT_TRUE(map.get() != nullptr);
-  std::unique_ptr<Backtrace> backtrace(Backtrace::CreateNew(pid, pid, map.get()));
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
 
   UnwindFromDevice(backtrace.get(), device_map);
 
@@ -1832,16 +1749,20 @@
   UnwindThroughSignal(false, Backtrace::Create, BacktraceMap::Create);
 }
 
-TEST(libbacktrace, unwind_remote_through_signal_using_handler_new) {
-  UnwindThroughSignal(false, Backtrace::CreateNew, BacktraceMap::CreateNew);
-}
-
 TEST(libbacktrace, unwind_remote_through_signal_using_action) {
   UnwindThroughSignal(true, Backtrace::Create, BacktraceMap::Create);
 }
 
-TEST(libbacktrace, unwind_remote_through_signal_using_action_new) {
-  UnwindThroughSignal(true, Backtrace::CreateNew, BacktraceMap::CreateNew);
+static void TestFrameSkipNumbering(create_func_t create_func, map_create_func_t map_create_func) {
+  std::unique_ptr<BacktraceMap> map(map_create_func(getpid(), false));
+  std::unique_ptr<Backtrace> backtrace(create_func(getpid(), gettid(), map.get()));
+  backtrace->Unwind(1);
+  ASSERT_NE(0U, backtrace->NumFrames());
+  ASSERT_EQ(0U, backtrace->GetFrame(0)->num);
+}
+
+TEST(libbacktrace, unwind_frame_skip_numbering) {
+  TestFrameSkipNumbering(Backtrace::Create, BacktraceMap::Create);
 }
 
 #if defined(ENABLE_PSS_TESTS)
@@ -1850,9 +1771,11 @@
 #define MAX_LEAK_BYTES (32*1024UL)
 
 static void CheckForLeak(pid_t pid, pid_t tid) {
+  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
+
   // Do a few runs to get the PSS stable.
   for (size_t i = 0; i < 100; i++) {
-    Backtrace* backtrace = Backtrace::Create(pid, tid);
+    Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
     ASSERT_TRUE(backtrace != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
     ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
@@ -1863,7 +1786,7 @@
 
   // Loop enough that even a small leak should be detectable.
   for (size_t i = 0; i < 4096; i++) {
-    Backtrace* backtrace = Backtrace::Create(pid, tid);
+    Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
     ASSERT_TRUE(backtrace != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
     ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 289fd0c..e073533 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -77,6 +77,10 @@
   const uint8_t* data;
 };
 
+namespace unwindstack {
+class Regs;
+}
+
 class Backtrace {
 public:
   // Create the correct Backtrace object based on what is to be unwound.
@@ -90,8 +94,6 @@
   // If map is NULL, then create the map and manage it internally.
   // If map is not NULL, the map is still owned by the caller.
   static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
-  // Same as above, but uses a different underlying unwinder.
-  static Backtrace* CreateNew(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
 
   // Create an offline Backtrace object that can be used to do an unwind without a process
   // that is still running. If cache_file is set to true, then elf information will be cached
@@ -106,6 +108,10 @@
   // Get the current stack trace and store in the backtrace_ structure.
   virtual bool Unwind(size_t num_ignore_frames, ucontext_t* context = NULL) = 0;
 
+  static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
+                     std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
+                     std::vector<std::string>* skip_names);
+
   // Get the function name and offset into the function given the pc.
   // If the string is empty, then no valid function name was found,
   // or the pc is not in any valid map.
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index 6cf8b3f..d078392 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -46,14 +46,16 @@
   std::string name;
 };
 
+namespace unwindstack {
+class Memory;
+}
+
 class BacktraceMap {
 public:
   // If uncached is true, then parse the current process map as of the call.
   // Passing a map created with uncached set to true to Backtrace::Create()
   // is unsupported.
   static BacktraceMap* Create(pid_t pid, bool uncached = false);
-  // Same as above, but is compatible with the new unwinder.
-  static BacktraceMap* CreateNew(pid_t pid, bool uncached = false);
 
   static BacktraceMap* Create(pid_t pid, const std::vector<backtrace_map_t>& maps);
 
@@ -62,6 +64,10 @@
   // Fill in the map data structure for the given address.
   virtual void FillIn(uintptr_t addr, backtrace_map_t* map);
 
+  // Only supported with the new unwinder.
+  virtual std::string GetFunctionName(uintptr_t /*pc*/, uintptr_t* /*offset*/) { return ""; }
+  virtual std::shared_ptr<unwindstack::Memory> GetProcessMemory() { return nullptr; }
+
   // The flags returned are the same flags as used by the mmap call.
   // The values are PROT_*.
   int GetFlags(uintptr_t pc) {
@@ -99,13 +105,20 @@
     return map.end > 0;
   }
 
-protected:
+  void SetSuffixesToIgnore(std::vector<std::string> suffixes) {
+    suffixes_to_ignore_.insert(suffixes_to_ignore_.end(), suffixes.begin(), suffixes.end());
+  }
+
+  const std::vector<std::string>& GetSuffixesToIgnore() { return suffixes_to_ignore_; }
+
+ protected:
   BacktraceMap(pid_t pid);
 
   virtual bool ParseLine(const char* line, backtrace_map_t* map);
 
-  std::deque<backtrace_map_t> maps_;
   pid_t pid_;
+  std::deque<backtrace_map_t> maps_;
+  std::vector<std::string> suffixes_to_ignore_;
 };
 
 class ScopedBacktraceMapIteratorLock {
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index cfe8d29..9cba109 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -19,14 +19,14 @@
 // which are also hard or even impossible to port to native Win32
 libcutils_nonwindows_sources = [
     "android_get_control_file.cpp",
-    "fs.c",
-    "multiuser.c",
-    "socket_inaddr_any_server_unix.c",
-    "socket_local_client_unix.c",
-    "socket_local_server_unix.c",
-    "socket_network_client_unix.c",
+    "fs.cpp",
+    "multiuser.cpp",
+    "socket_inaddr_any_server_unix.cpp",
+    "socket_local_client_unix.cpp",
+    "socket_local_server_unix.cpp",
+    "socket_network_client_unix.cpp",
     "sockets_unix.cpp",
-    "str_parms.c",
+    "str_parms.cpp",
 ]
 
 cc_library_headers {
@@ -56,41 +56,37 @@
     },
     host_supported: true,
     srcs: [
-        "config_utils.c",
+        "config_utils.cpp",
         "fs_config.cpp",
-        "canned_fs_config.c",
-        "hashmap.c",
-        "iosched_policy.c",
-        "load_file.c",
-        "native_handle.c",
+        "canned_fs_config.cpp",
+        "hashmap.cpp",
+        "iosched_policy.cpp",
+        "load_file.cpp",
+        "native_handle.cpp",
         "open_memstream.c",
-        "record_stream.c",
+        "record_stream.cpp",
         "sched_policy.cpp",
         "sockets.cpp",
-        "strdup16to8.c",
-        "strdup8to16.c",
+        "strdup16to8.cpp",
+        "strdup8to16.cpp",
         "strlcpy.c",
-        "threads.c",
+        "threads.cpp",
     ],
 
     target: {
-        host: {
-            srcs: ["dlmalloc_stubs.c"],
-        },
         linux_bionic: {
             enabled: true,
-            exclude_srcs: ["dlmalloc_stubs.c"],
         },
         not_windows: {
             srcs: libcutils_nonwindows_sources + [
-                "ashmem-host.c",
-                "trace-host.c",
+                "ashmem-host.cpp",
+                "trace-host.cpp",
             ],
         },
         windows: {
             srcs: [
-                "socket_inaddr_any_server_windows.c",
-                "socket_network_client_windows.c",
+                "socket_inaddr_any_server_windows.cpp",
+                "socket_network_client_windows.cpp",
                 "sockets_windows.cpp",
             ],
 
@@ -105,32 +101,41 @@
 
         android: {
             srcs: libcutils_nonwindows_sources + [
-                "android_reboot.c",
-                "ashmem-dev.c",
+                "android_reboot.cpp",
+                "ashmem-dev.cpp",
                 "klog.cpp",
-                "partition_utils.c",
+                "partition_utils.cpp",
                 "properties.cpp",
-                "qtaguid.c",
-                "trace-dev.c",
-                "uevent.c",
+                "qtaguid.cpp",
+                "trace-dev.cpp",
+                "uevent.cpp",
             ],
+        },
+
+        android_arm: {
+            srcs: ["arch-arm/memset32.S"],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
+        },
+        android_arm64: {
+            srcs: ["arch-arm64/android_memset.S"],
             sanitize: {
                 misc_undefined: ["integer"],
             },
         },
 
-        android_arm: {
-            srcs: ["arch-arm/memset32.S"],
-        },
-        android_arm64: {
-            srcs: ["arch-arm64/android_memset.S"],
-        },
-
         android_mips: {
             srcs: ["arch-mips/android_memset.c"],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
         },
         android_mips64: {
             srcs: ["arch-mips/android_memset.c"],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
         },
 
         android_x86: {
@@ -138,6 +143,12 @@
                 "arch-x86/android_memset16.S",
                 "arch-x86/android_memset32.S",
             ],
+            // TODO: This is to work around b/29412086.
+            // Remove once __mulodi4 is available and move the "sanitize" block
+            // to the android target.
+            sanitize: {
+                misc_undefined: [],
+            },
         },
 
         android_x86_64: {
@@ -145,6 +156,9 @@
                 "arch-x86_64/android_memset16.S",
                 "arch-x86_64/android_memset32.S",
             ],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
         },
     },
 
diff --git a/libcutils/android_get_control_file.cpp b/libcutils/android_get_control_file.cpp
index 780d9f1..d8121f5 100644
--- a/libcutils/android_get_control_file.cpp
+++ b/libcutils/android_get_control_file.cpp
@@ -25,6 +25,9 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
+#include <cutils/android_get_control_file.h>
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -36,8 +39,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <cutils/android_get_control_file.h>
-
 #include "android_get_control_env.h"
 
 #ifndef TEMP_FAILURE_RETRY
diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.cpp
similarity index 96%
rename from libcutils/android_reboot.c
rename to libcutils/android_reboot.cpp
index 996d89d..5e864d4 100644
--- a/libcutils/android_reboot.c
+++ b/libcutils/android_reboot.cpp
@@ -13,10 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+#include <cutils/android_reboot.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <cutils/android_reboot.h>
 #include <cutils/properties.h>
 
 #define TAG "android_reboot"
@@ -26,7 +28,7 @@
     const char* restart_cmd = NULL;
     char* prop_value;
 
-    switch (cmd) {
+    switch (static_cast<unsigned>(cmd)) {
         case ANDROID_RB_RESTART:  // deprecated
         case ANDROID_RB_RESTART2:
             restart_cmd = "reboot";
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.cpp
similarity index 94%
rename from libcutils/ashmem-dev.c
rename to libcutils/ashmem-dev.cpp
index 95f2259..15ace0e 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/ashmem.h>
+
 /*
  * Implementation of the user-space ashmem API for devices, which have our
  * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
@@ -31,8 +33,6 @@
 #include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <unistd.h>
-
-#include <cutils/ashmem.h>
 #include <log/log.h>
 
 #define ASHMEM_DEVICE "/dev/ashmem"
@@ -192,7 +192,8 @@
 
 int ashmem_pin_region(int fd, size_t offset, size_t len)
 {
-    struct ashmem_pin pin = { offset, len };
+    // TODO: should LP64 reject too-large offset/len?
+    ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
 
     int ret = __ashmem_is_ashmem(fd, 1);
     if (ret < 0) {
@@ -204,7 +205,8 @@
 
 int ashmem_unpin_region(int fd, size_t offset, size_t len)
 {
-    struct ashmem_pin pin = { offset, len };
+    // TODO: should LP64 reject too-large offset/len?
+    ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
 
     int ret = __ashmem_is_ashmem(fd, 1);
     if (ret < 0) {
diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.cpp
similarity index 92%
rename from libcutils/ashmem-host.c
rename to libcutils/ashmem-host.cpp
index 1f9f753..d2c28f3 100644
--- a/libcutils/ashmem-host.c
+++ b/libcutils/ashmem-host.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/ashmem.h>
+
 /*
  * Implementation of the user-space ashmem API for the simulator, which lacks
  * an ashmem-enabled kernel. See ashmem-dev.c for the real ashmem-based version.
@@ -31,7 +33,6 @@
 #include <time.h>
 #include <unistd.h>
 
-#include <cutils/ashmem.h>
 #include <utils/Compat.h>
 
 #ifndef __unused
@@ -40,12 +41,12 @@
 
 int ashmem_create_region(const char *ignored __unused, size_t size)
 {
-    char template[PATH_MAX];
-    snprintf(template, sizeof(template), "/tmp/android-ashmem-%d-XXXXXXXXX", getpid());
-    int fd = mkstemp(template);
+    char pattern[PATH_MAX];
+    snprintf(pattern, sizeof(pattern), "/tmp/android-ashmem-%d-XXXXXXXXX", getpid());
+    int fd = mkstemp(pattern);
     if (fd == -1) return -1;
 
-    unlink(template);
+    unlink(pattern);
 
     if (TEMP_FAILURE_RETRY(ftruncate(fd, size)) == -1) {
       close(fd);
diff --git a/libcutils/canned_fs_config.c b/libcutils/canned_fs_config.cpp
similarity index 99%
rename from libcutils/canned_fs_config.c
rename to libcutils/canned_fs_config.cpp
index 819a846..6b5763b 100644
--- a/libcutils/canned_fs_config.c
+++ b/libcutils/canned_fs_config.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#include <private/android_filesystem_config.h>
+#include <private/canned_fs_config.h>
+#include <private/fs_config.h>
+
 #include <errno.h>
 #include <inttypes.h>
 #include <limits.h>
@@ -22,10 +26,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <private/android_filesystem_config.h>
-#include <private/fs_config.h>
-#include <private/canned_fs_config.h>
-
 typedef struct {
     const char* path;
     unsigned uid;
diff --git a/libcutils/config_utils.c b/libcutils/config_utils.cpp
similarity index 97%
rename from libcutils/config_utils.c
rename to libcutils/config_utils.cpp
index fc5ca78..a3af01a 100644
--- a/libcutils/config_utils.c
+++ b/libcutils/config_utils.cpp
@@ -14,20 +14,19 @@
  * limitations under the License.
  */
 
+#include <cutils/config_utils.h>
+
 #include <string.h>
 #include <ctype.h>
 #include <stdlib.h>
 #include <fcntl.h>
 #include <unistd.h>
 
-#include <cutils/config_utils.h>
 #include <cutils/misc.h>
 
 cnode* config_node(const char *name, const char *value)
 {
-    cnode *node;
-
-    node = calloc(sizeof(cnode), 1);
+    cnode* node = static_cast<cnode*>(calloc(sizeof(cnode), 1));
     if(node) {
         node->name = name ? name : "";
         node->value = value ? value : "";
@@ -311,9 +310,9 @@
 
 void config_load_file(cnode *root, const char *fn)
 {
-    char *data;
-    data = load_file(fn, 0);
+    char* data = static_cast<char*>(load_file(fn, nullptr));
     config_load(root, data);
+    // TODO: deliberate leak :-/
 }
 
 void config_free(cnode *root)
diff --git a/libcutils/dlmalloc_stubs.c b/libcutils/dlmalloc_stubs.c
deleted file mode 100644
index 2cff9dd..0000000
--- a/libcutils/dlmalloc_stubs.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "dlmalloc-stubs"
-
-#include "log/log.h"
-
-#define UNUSED __attribute__((__unused__))
-
-/*
- * Stubs for functions defined in bionic/libc/bionic/dlmalloc.c. These
- * are used in host builds, as the host libc will not contain these
- * functions.
- */
-void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*) UNUSED,
-                          void* arg UNUSED)
-{
-  ALOGW("Called host unimplemented stub: dlmalloc_inspect_all");
-}
-
-int dlmalloc_trim(size_t unused UNUSED)
-{
-  ALOGW("Called host unimplemented stub: dlmalloc_trim");
-  return 0;
-}
diff --git a/libcutils/fs.c b/libcutils/fs.cpp
similarity index 93%
rename from libcutils/fs.c
rename to libcutils/fs.cpp
index b253b1c..ef85acc 100644
--- a/libcutils/fs.c
+++ b/libcutils/fs.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/fs.h>
+
 #define LOG_TAG "cutils"
 
 /* These defines are only needed because prebuilt headers are out of date */
@@ -32,7 +34,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <cutils/fs.h>
 #include <log/log.h>
 
 #define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
@@ -40,6 +41,11 @@
 
 static int fs_prepare_path_impl(const char* path, mode_t mode, uid_t uid, gid_t gid,
         int allow_fixup, int prepare_as_dir) {
+    // TODO: fix the goto hell below.
+    int type_ok;
+    int owner_match;
+    int mode_match;
+
     // Check if path needs to be created
     struct stat sb;
     int create_result = -1;
@@ -53,14 +59,14 @@
     }
 
     // Exists, verify status
-    int type_ok = prepare_as_dir ? S_ISDIR(sb.st_mode) : S_ISREG(sb.st_mode);
+    type_ok = prepare_as_dir ? S_ISDIR(sb.st_mode) : S_ISREG(sb.st_mode);
     if (!type_ok) {
         ALOGE("Not a %s: %s", (prepare_as_dir ? "directory" : "regular file"), path);
         return -1;
     }
 
-    int owner_match = ((sb.st_uid == uid) && (sb.st_gid == gid));
-    int mode_match = ((sb.st_mode & ALL_PERMS) == mode);
+    owner_match = ((sb.st_uid == uid) && (sb.st_gid == gid));
+    mode_match = ((sb.st_mode & ALL_PERMS) == mode);
     if (owner_match && mode_match) {
         return 0;
     } else if (allow_fixup) {
@@ -188,23 +194,20 @@
 #ifndef __APPLE__
 
 int fs_mkdirs(const char* path, mode_t mode) {
-    int res = 0;
-    int fd = 0;
-    struct stat sb;
-    char* buf = strdup(path);
-
-    if (*buf != '/') {
-        ALOGE("Relative paths are not allowed: %s", buf);
-        res = -EINVAL;
-        goto done;
+    if (*path != '/') {
+        ALOGE("Relative paths are not allowed: %s", path);
+        return -EINVAL;
     }
 
-    if ((fd = open("/", 0)) == -1) {
+    int fd = open("/", 0);
+    if (fd == -1) {
         ALOGE("Failed to open(/): %s", strerror(errno));
-        res = -errno;
-        goto done;
+        return -errno;
     }
 
+    struct stat sb;
+    int res = 0;
+    char* buf = strdup(path);
     char* segment = buf + 1;
     char* p = segment;
     while (*p != '\0') {
@@ -266,7 +269,6 @@
 
 done_close:
     close(fd);
-done:
     free(buf);
     return res;
 }
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 7603ffc..f45472e 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <private/fs_config.h>
+
 // This file is used to define the properties of the filesystem
 // images generated by build tools (mkbootfs and mkyaffs2image) and
 // by the device side of adb.
@@ -31,7 +33,6 @@
 
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
-#include <private/fs_config.h>
 #include <utils/Compat.h>
 
 #ifndef O_BINARY
diff --git a/libcutils/hashmap.c b/libcutils/hashmap.cpp
similarity index 96%
rename from libcutils/hashmap.c
rename to libcutils/hashmap.cpp
index ede3b98..65b6ab1 100644
--- a/libcutils/hashmap.c
+++ b/libcutils/hashmap.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <cutils/hashmap.h>
+
 #include <assert.h>
 #include <errno.h>
 #include <cutils/threads.h>
@@ -45,7 +46,7 @@
     assert(hash != NULL);
     assert(equals != NULL);
     
-    Hashmap* map = malloc(sizeof(Hashmap));
+    Hashmap* map = static_cast<Hashmap*>(malloc(sizeof(Hashmap)));
     if (map == NULL) {
         return NULL;
     }
@@ -58,7 +59,7 @@
         map->bucketCount <<= 1; 
     }
 
-    map->buckets = calloc(map->bucketCount, sizeof(Entry*));
+    map->buckets = static_cast<Entry**>(calloc(map->bucketCount, sizeof(Entry*)));
     if (map->buckets == NULL) {
         free(map);
         return NULL;
@@ -106,7 +107,7 @@
     if (map->size > (map->bucketCount * 3 / 4)) {
         // Start off with a 0.33 load factor.
         size_t newBucketCount = map->bucketCount << 1;
-        Entry** newBuckets = calloc(newBucketCount, sizeof(Entry*));
+        Entry** newBuckets = static_cast<Entry**>(calloc(newBucketCount, sizeof(Entry*)));
         if (newBuckets == NULL) {
             // Abort expansion.
             return;
@@ -171,7 +172,7 @@
 }
 
 static Entry* createEntry(void* key, int hash, void* value) {
-    Entry* entry = malloc(sizeof(Entry));
+    Entry* entry = static_cast<Entry*>(malloc(sizeof(Entry)));
     if (entry == NULL) {
         return NULL;
     }
diff --git a/libcutils/include/cutils/android_reboot.h b/libcutils/include/cutils/android_reboot.h
index a903adb..99030ed 100644
--- a/libcutils/include/cutils/android_reboot.h
+++ b/libcutils/include/cutils/android_reboot.h
@@ -17,6 +17,7 @@
 #ifndef __CUTILS_ANDROID_REBOOT_H__
 #define __CUTILS_ANDROID_REBOOT_H__
 
+#include <sys/cdefs.h>
 
 __BEGIN_DECLS
 
diff --git a/libcutils/include/cutils/partition_utils.h b/libcutils/include/cutils/partition_utils.h
index 72ca80d..7518559 100644
--- a/libcutils/include/cutils/partition_utils.h
+++ b/libcutils/include/cutils/partition_utils.h
@@ -17,6 +17,8 @@
 #ifndef __CUTILS_PARTITION_WIPED_H__
 #define __CUTILS_PARTITION_WIPED_H__
 
+#include <sys/cdefs.h>
+
 __BEGIN_DECLS
 
 int partition_wiped(char *source);
diff --git a/libcutils/include/cutils/qtaguid.h b/libcutils/include/cutils/qtaguid.h
index 803fe0d..3f5e41f 100644
--- a/libcutils/include/cutils/qtaguid.h
+++ b/libcutils/include/cutils/qtaguid.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 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.
@@ -17,18 +17,14 @@
 #ifndef __CUTILS_QTAGUID_H
 #define __CUTILS_QTAGUID_H
 
-#include <stdint.h>
 #include <sys/types.h>
-#include <unistd.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /*
- * Set tags (and owning UIDs) for network sockets. The socket must be untagged
- * by calling qtaguid_untagSocket() before closing it, otherwise the qtaguid
- * module will keep a reference to it even after close.
+ * Set tags (and owning UIDs) for network sockets.
  */
 extern int qtaguid_tagSocket(int sockfd, int tag, uid_t uid);
 
@@ -46,8 +42,8 @@
 
 /*
  * Delete all tag info that relates to the given tag an uid.
- * If the tag is 0, then ALL info about the uid is freeded.
- * The delete data also affects active tagged socketd, which are
+ * If the tag is 0, then ALL info about the uid is freed.
+ * The delete data also affects active tagged sockets, which are
  * then untagged.
  * The calling process can only operate on its own tags.
  * Unless it is part of the happy AID_NET_BW_ACCT group.
diff --git a/libcutils/include/cutils/record_stream.h b/libcutils/include/cutils/record_stream.h
index bfac87a..bcfc80d 100644
--- a/libcutils/include/cutils/record_stream.h
+++ b/libcutils/include/cutils/record_stream.h
@@ -25,6 +25,7 @@
 extern "C" {
 #endif
 
+#include <stddef.h>
 
 typedef struct RecordStream RecordStream;
 
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 55ece54..2ecf5bc 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -170,6 +170,14 @@
 #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
 #define AID_SHARED_GID_END 59999   /* end of gids for apps in each user to share */
 
+/*
+ * This is a magic number in the kernel and not something that was picked
+ * arbitrarily. This value is returned whenever a uid that has no mapping in the
+ * user namespace is returned to userspace:
+ * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/highuid.h?h=v4.4#n40
+ */
+#define AID_OVERFLOWUID 65534 /* unmapped user in the user namespace */
+
 #define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
 #define AID_ISOLATED_END 99999   /* end of uids for fully isolated sandboxed processes */
 
diff --git a/libcutils/include/private/canned_fs_config.h b/libcutils/include/private/canned_fs_config.h
index 71e1537..135b91c 100644
--- a/libcutils/include/private/canned_fs_config.h
+++ b/libcutils/include/private/canned_fs_config.h
@@ -19,8 +19,12 @@
 
 #include <inttypes.h>
 
+__BEGIN_DECLS
+
 int load_canned_fs_config(const char* fn);
 void canned_fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid,
                       unsigned* gid, unsigned* mode, uint64_t* capabilities);
 
+__END_DECLS
+
 #endif
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
index aab5042..8926491 100644
--- a/libcutils/include/private/fs_config.h
+++ b/libcutils/include/private/fs_config.h
@@ -24,6 +24,7 @@
 
 #include <stdint.h>
 #include <sys/cdefs.h>
+#include <sys/types.h>
 
 #if defined(__BIONIC__)
 #include <linux/capability.h>
diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.cpp
similarity index 95%
rename from libcutils/iosched_policy.c
rename to libcutils/iosched_policy.cpp
index 13c2ceb..012c537 100644
--- a/libcutils/iosched_policy.c
+++ b/libcutils/iosched_policy.cpp
@@ -14,6 +14,8 @@
 ** limitations under the License.
 */
 
+#include <cutils/iosched_policy.h>
+
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
@@ -21,8 +23,6 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <cutils/iosched_policy.h>
-
 #if defined(__ANDROID__)
 #define IOPRIO_WHO_PROCESS (1)
 #define IOPRIO_CLASS_SHIFT (13)
@@ -49,7 +49,7 @@
         return -1;
     }
 
-    *clazz = (rc >> IOPRIO_CLASS_SHIFT);
+    *clazz = static_cast<IoSchedClass>(rc >> IOPRIO_CLASS_SHIFT);
     *ioprio = (rc & 0xff);
 #else
     *clazz = IoSchedClass_NONE;
diff --git a/libcutils/klog.cpp b/libcutils/klog.cpp
index d301276..6a9f4df 100644
--- a/libcutils/klog.cpp
+++ b/libcutils/klog.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/klog.h>
+
 #include <errno.h>
 #include <fcntl.h>
 #include <stdarg.h>
@@ -25,7 +27,6 @@
 #include <unistd.h>
 
 #include <cutils/android_get_control_file.h>
-#include <cutils/klog.h>
 
 static int klog_level = KLOG_INFO_LEVEL;
 
diff --git a/libcutils/load_file.c b/libcutils/load_file.cpp
similarity index 97%
rename from libcutils/load_file.c
rename to libcutils/load_file.cpp
index 99f2965..346105c 100644
--- a/libcutils/load_file.c
+++ b/libcutils/load_file.cpp
@@ -15,6 +15,8 @@
 ** limitations under the License.
 */
 
+#include <cutils/misc.h>
+
 #include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
diff --git a/libcutils/multiuser.c b/libcutils/multiuser.cpp
similarity index 90%
rename from libcutils/multiuser.c
rename to libcutils/multiuser.cpp
index 61403f4..0fd3d0c 100644
--- a/libcutils/multiuser.c
+++ b/libcutils/multiuser.cpp
@@ -53,9 +53,11 @@
     }
 }
 
-gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id) {
+gid_t multiuser_get_shared_gid(userid_t, appid_t app_id) {
     if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
-        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_SHARED_GID_START);
+        return (app_id - AID_APP_START) + AID_SHARED_GID_START;
+    } else if (app_id >= AID_ROOT && app_id <= AID_APP_START) {
+        return app_id;
     } else {
         return -1;
     }
diff --git a/libcutils/native_handle.c b/libcutils/native_handle.cpp
similarity index 96%
rename from libcutils/native_handle.c
rename to libcutils/native_handle.cpp
index 95bbc41..66f7a3d 100644
--- a/libcutils/native_handle.c
+++ b/libcutils/native_handle.cpp
@@ -45,7 +45,7 @@
     }
 
     size_t mallocSize = sizeof(native_handle_t) + (sizeof(int) * (numFds + numInts));
-    native_handle_t* h = malloc(mallocSize);
+    native_handle_t* h = static_cast<native_handle_t*>(malloc(mallocSize));
     if (h) {
         h->version = sizeof(native_handle_t);
         h->numFds = numFds;
diff --git a/libcutils/partition_utils.c b/libcutils/partition_utils.cpp
similarity index 97%
rename from libcutils/partition_utils.c
rename to libcutils/partition_utils.cpp
index 823b162..6735d6c 100644
--- a/libcutils/partition_utils.c
+++ b/libcutils/partition_utils.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/partition_utils.h>
+
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h> /* for BLKGETSIZE */
diff --git a/libcutils/properties.cpp b/libcutils/properties.cpp
index 25ff1a3..5dbbeba 100644
--- a/libcutils/properties.cpp
+++ b/libcutils/properties.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/properties.h>
+
 #define LOG_TAG "properties"
 // #define LOG_NDEBUG 0
 
@@ -25,7 +27,6 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <cutils/properties.h>
 #include <cutils/sockets.h>
 #include <log/log.h>
 
diff --git a/libcutils/qtaguid.cpp b/libcutils/qtaguid.cpp
new file mode 100644
index 0000000..b94d134
--- /dev/null
+++ b/libcutils/qtaguid.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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 <cutils/qtaguid.h>
+
+// #define LOG_NDEBUG 0
+
+#define LOG_TAG "qtaguid"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+class netdHandler {
+  public:
+    int (*netdTagSocket)(int, uint32_t, uid_t);
+    int (*netdUntagSocket)(int);
+    int (*netdSetCounterSet)(uint32_t, uid_t);
+    int (*netdDeleteTagData)(uint32_t, uid_t);
+};
+
+int dummyTagSocket(int, uint32_t, uid_t) {
+    return -EREMOTEIO;
+}
+
+int dummyUntagSocket(int) {
+    return -EREMOTEIO;
+}
+
+int dummySetCounterSet(uint32_t, uid_t) {
+    return -EREMOTEIO;
+}
+
+int dummyDeleteTagData(uint32_t, uid_t) {
+    return -EREMOTEIO;
+}
+
+netdHandler initHandler(void) {
+    netdHandler handler = {dummyTagSocket, dummyUntagSocket, dummySetCounterSet, dummyDeleteTagData};
+
+    void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW);
+    if (!netdClientHandle) {
+        ALOGE("Failed to open libnetd_client.so: %s", dlerror());
+        return handler;
+    }
+
+    handler.netdTagSocket = (int (*)(int, uint32_t, uid_t))dlsym(netdClientHandle, "tagSocket");
+    if (!handler.netdTagSocket) {
+        ALOGE("load netdTagSocket handler failed: %s", dlerror());
+    }
+
+    handler.netdUntagSocket = (int (*)(int))dlsym(netdClientHandle, "untagSocket");
+    if (!handler.netdUntagSocket) {
+        ALOGE("load netdUntagSocket handler failed: %s", dlerror());
+    }
+
+    handler.netdSetCounterSet = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "setCounterSet");
+    if (!handler.netdSetCounterSet) {
+        ALOGE("load netdSetCounterSet handler failed: %s", dlerror());
+    }
+
+    handler.netdDeleteTagData = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "deleteTagData");
+    if (!handler.netdDeleteTagData) {
+        ALOGE("load netdDeleteTagData handler failed: %s", dlerror());
+    }
+    return handler;
+}
+
+// The language guarantees that this object will be initialized in a thread-safe way.
+static netdHandler& getHandler() {
+    static netdHandler instance = initHandler();
+    return instance;
+}
+
+int qtaguid_tagSocket(int sockfd, int tag, uid_t uid) {
+    // Check the socket fd passed to us is still valid before we load the netd
+    // client. Pass a already closed socket fd to netd client may let netd open
+    // the unix socket with the same fd number and pass it to server for
+    // tagging.
+    // TODO: move the check into netdTagSocket.
+    int res = fcntl(sockfd, F_GETFD);
+    if (res < 0) return res;
+
+    ALOGV("Tagging socket %d with tag %u for uid %d", sockfd, tag, uid);
+    return getHandler().netdTagSocket(sockfd, tag, uid);
+}
+
+int qtaguid_untagSocket(int sockfd) {
+    // Similiar to tag socket. We need a check before untag to make sure untag a closed socket fail
+    // as expected.
+    // TODO: move the check into netdTagSocket.
+    int res = fcntl(sockfd, F_GETFD);
+    if (res < 0) return res;
+
+    ALOGV("Untagging socket %d", sockfd);
+    return getHandler().netdUntagSocket(sockfd);
+}
+
+int qtaguid_setCounterSet(int counterSetNum, uid_t uid) {
+    ALOGV("Setting counters to set %d for uid %d", counterSetNum, uid);
+    return getHandler().netdSetCounterSet(counterSetNum, uid);
+}
+
+int qtaguid_deleteTagData(int tag, uid_t uid) {
+    ALOGV("Deleting tag data with tag %u for uid %d", tag, uid);
+    return getHandler().netdDeleteTagData(tag, uid);
+}
diff --git a/libcutils/record_stream.c b/libcutils/record_stream.cpp
similarity index 99%
rename from libcutils/record_stream.c
rename to libcutils/record_stream.cpp
index 2bc4226..5a86b83 100644
--- a/libcutils/record_stream.c
+++ b/libcutils/record_stream.cpp
@@ -15,11 +15,12 @@
 ** limitations under the License.
 */
 
+#include <cutils/record_stream.h>
+
 #include <stdlib.h>
 #include <unistd.h>
 #include <assert.h>
 #include <errno.h>
-#include <cutils/record_stream.h>
 #include <string.h>
 #include <stdint.h>
 #if defined(_WIN32)
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index b00fa85..0e6d333 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -14,6 +14,8 @@
 ** limitations under the License.
 */
 
+#include <cutils/sched_policy.h>
+
 #define LOG_TAG "SchedPolicy"
 
 #include <errno.h>
@@ -24,7 +26,6 @@
 #include <unistd.h>
 
 #include <log/log.h>
-#include <cutils/sched_policy.h>
 
 #define UNUSED __attribute__((__unused__))
 
diff --git a/libcutils/socket_inaddr_any_server_unix.c b/libcutils/socket_inaddr_any_server_unix.cpp
similarity index 99%
rename from libcutils/socket_inaddr_any_server_unix.c
rename to libcutils/socket_inaddr_any_server_unix.cpp
index 387258f..27c5333 100644
--- a/libcutils/socket_inaddr_any_server_unix.c
+++ b/libcutils/socket_inaddr_any_server_unix.cpp
@@ -14,6 +14,8 @@
 ** limitations under the License.
 */
 
+#include <cutils/sockets.h>
+
 #include <errno.h>
 #include <stddef.h>
 #include <stdlib.h>
@@ -25,8 +27,6 @@
 #include <sys/types.h>
 #include <netinet/in.h>
 
-#include <cutils/sockets.h>
-
 #define LISTEN_BACKLOG 4
 
 /* open listen() port on any interface */
diff --git a/libcutils/socket_inaddr_any_server_windows.c b/libcutils/socket_inaddr_any_server_windows.cpp
similarity index 99%
rename from libcutils/socket_inaddr_any_server_windows.c
rename to libcutils/socket_inaddr_any_server_windows.cpp
index c15200a..1d73206 100644
--- a/libcutils/socket_inaddr_any_server_windows.c
+++ b/libcutils/socket_inaddr_any_server_windows.cpp
@@ -26,10 +26,10 @@
  * SUCH DAMAGE.
  */
 
-#include <errno.h>
-
 #include <cutils/sockets.h>
 
+#include <errno.h>
+
 #define LISTEN_BACKLOG 4
 
 extern bool initialize_windows_sockets();
diff --git a/libcutils/socket_local_client_unix.c b/libcutils/socket_local_client_unix.cpp
similarity index 99%
rename from libcutils/socket_local_client_unix.c
rename to libcutils/socket_local_client_unix.cpp
index 92fb9f1..68b2b0c 100644
--- a/libcutils/socket_local_client_unix.c
+++ b/libcutils/socket_local_client_unix.cpp
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
+#include <cutils/sockets.h>
+
 #include <errno.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#include <cutils/sockets.h>
-
 #if defined(_WIN32)
 
 int socket_local_client(const char *name, int namespaceId, int type)
diff --git a/libcutils/socket_local_server_unix.c b/libcutils/socket_local_server_unix.cpp
similarity index 94%
rename from libcutils/socket_local_server_unix.c
rename to libcutils/socket_local_server_unix.cpp
index db9e1e0..855e5da 100644
--- a/libcutils/socket_local_server_unix.c
+++ b/libcutils/socket_local_server_unix.cpp
@@ -94,7 +94,7 @@
  *  Returns fd on success, -1 on fail
  */
 
-int socket_local_server(const char *name, int namespace, int type)
+int socket_local_server(const char *name, int namespaceId, int type)
 {
     int err;
     int s;
@@ -102,7 +102,7 @@
     s = socket(AF_LOCAL, type, 0);
     if (s < 0) return -1;
 
-    err = socket_local_server_bind(s, name, namespace);
+    err = socket_local_server_bind(s, name, namespaceId);
 
     if (err < 0) {
         close(s);
diff --git a/libcutils/socket_network_client_unix.c b/libcutils/socket_network_client_unix.cpp
similarity index 99%
rename from libcutils/socket_network_client_unix.c
rename to libcutils/socket_network_client_unix.cpp
index 1b87c49..be3c535 100644
--- a/libcutils/socket_network_client_unix.c
+++ b/libcutils/socket_network_client_unix.cpp
@@ -14,6 +14,8 @@
 ** limitations under the License.
 */
 
+#include <cutils/sockets.h>
+
 #include <errno.h>
 #include <fcntl.h>
 #include <stddef.h>
@@ -27,8 +29,6 @@
 #include <netinet/in.h>
 #include <netdb.h>
 
-#include <cutils/sockets.h>
-
 static int toggle_O_NONBLOCK(int s) {
     int flags = fcntl(s, F_GETFL);
     if (flags == -1 || fcntl(s, F_SETFL, flags ^ O_NONBLOCK) == -1) {
diff --git a/libcutils/socket_network_client_windows.c b/libcutils/socket_network_client_windows.cpp
similarity index 100%
rename from libcutils/socket_network_client_windows.c
rename to libcutils/socket_network_client_windows.cpp
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index e91f358..2849aa8 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/sockets.h>
+
 #define LOG_TAG "socket-unix"
 
 #include <stdio.h>
@@ -26,7 +28,6 @@
 #include <unistd.h>
 
 #include <cutils/android_get_control_file.h>
-#include <cutils/sockets.h>
 #include <log/log.h>
 
 #include "android_get_control_env.h"
diff --git a/libcutils/sockets_windows.cpp b/libcutils/sockets_windows.cpp
index 3064c70..3e49b85 100644
--- a/libcutils/sockets_windows.cpp
+++ b/libcutils/sockets_windows.cpp
@@ -37,7 +37,7 @@
 // Both adb (1) and Chrome (2) purposefully avoid WSACleanup() with no issues.
 // (1) https://android.googlesource.com/platform/system/core.git/+/master/adb/sysdeps_win32.cpp
 // (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc
-extern "C" bool initialize_windows_sockets() {
+bool initialize_windows_sockets() {
     // There's no harm in calling WSAStartup() multiple times but no benefit
     // either, we may as well skip it after the first.
     static bool init_success = false;
diff --git a/libcutils/str_parms.c b/libcutils/str_parms.cpp
similarity index 90%
rename from libcutils/str_parms.c
rename to libcutils/str_parms.cpp
index 8dafded..139d62f 100644
--- a/libcutils/str_parms.c
+++ b/libcutils/str_parms.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/str_parms.h>
+
 #define LOG_TAG "str_params"
 //#define LOG_NDEBUG 0
 
@@ -26,7 +28,6 @@
 
 #include <cutils/hashmap.h>
 #include <cutils/memory.h>
-#include <cutils/str_parms.h>
 #include <log/log.h>
 
 #define UNUSED __attribute__((unused))
@@ -62,30 +63,24 @@
 static int str_hash_fn(void *str)
 {
     uint32_t hash = 5381;
-    char *p;
 
-    for (p = str; p && *p; p++)
+    for (char* p = static_cast<char*>(str); p && *p; p++)
         hash = ((hash << 5) + hash) + *p;
     return (int)hash;
 }
 
 struct str_parms *str_parms_create(void)
 {
-    struct str_parms *str_parms;
+    str_parms* s = static_cast<str_parms*>(calloc(1, sizeof(str_parms)));
+    if (!s) return NULL;
 
-    str_parms = calloc(1, sizeof(struct str_parms));
-    if (!str_parms)
+    s->map = hashmapCreate(5, str_hash_fn, str_eq);
+    if (!s->map) {
+        free(s);
         return NULL;
+    }
 
-    str_parms->map = hashmapCreate(5, str_hash_fn, str_eq);
-    if (!str_parms->map)
-        goto err;
-
-    return str_parms;
-
-err:
-    free(str_parms);
-    return NULL;
+    return s;
 }
 
 struct remove_ctxt {
@@ -95,7 +90,7 @@
 
 static bool remove_pair(void *key, void *value, void *context)
 {
-    struct remove_ctxt *ctxt = context;
+    remove_ctxt* ctxt = static_cast<remove_ctxt*>(context);
     bool should_continue;
 
     /*
@@ -109,7 +104,7 @@
     if (!ctxt->key) {
         should_continue = true;
         goto do_remove;
-    } else if (!strcmp(ctxt->key, key)) {
+    } else if (!strcmp(ctxt->key, static_cast<const char*>(key))) {
         should_continue = false;
         goto do_remove;
     }
@@ -292,9 +287,8 @@
 int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
                       int len)
 {
-    char *value;
-
-    value = hashmapGet(str_parms->map, (void *)key);
+    // TODO: hashmapGet should take a const* key.
+    char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)key));
     if (value)
         return strlcpy(val, value, len);
 
@@ -303,10 +297,10 @@
 
 int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)
 {
-    char *value;
     char *end;
 
-    value = hashmapGet(str_parms->map, (void *)key);
+    // TODO: hashmapGet should take a const* key.
+    char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)key));
     if (!value)
         return -ENOENT;
 
@@ -321,10 +315,10 @@
                         float *val)
 {
     float out;
-    char *value;
     char *end;
 
-    value = hashmapGet(str_parms->map, (void *)key);
+    // TODO: hashmapGet should take a const* key.
+    char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)(key)));
     if (!value)
         return -ENOENT;
 
@@ -338,7 +332,7 @@
 
 static bool combine_strings(void *key, void *value, void *context)
 {
-    char **old_str = context;
+    char** old_str = static_cast<char**>(context);
     char *new_str;
     int ret;
 
diff --git a/libcutils/strdup16to8.c b/libcutils/strdup16to8.cpp
similarity index 97%
rename from libcutils/strdup16to8.c
rename to libcutils/strdup16to8.cpp
index 4dc987e..d89181e 100644
--- a/libcutils/strdup16to8.c
+++ b/libcutils/strdup16to8.cpp
@@ -15,10 +15,10 @@
 ** limitations under the License.
 */
 
-#include <limits.h>  /* for SIZE_MAX */
-
 #include <cutils/jstring.h>
+
 #include <assert.h>
+#include <limits.h>  /* for SIZE_MAX */
 #include <stdlib.h>
 
 
@@ -145,14 +145,11 @@
  */
 char * strndup16to8 (const char16_t* s, size_t n)
 {
-    char*   ret;
-    size_t  len;
-
     if (s == NULL) {
         return NULL;
     }
 
-    len = strnlen16to8(s, n);
+    size_t len = strnlen16to8(s, n);
 
     /* We are paranoid, and we check for SIZE_MAX-1
      * too since it is an overflow value for our
@@ -161,7 +158,7 @@
     if (len >= SIZE_MAX-1)
         return NULL;
 
-    ret = malloc(len + 1);
+    char* ret = static_cast<char*>(malloc(len + 1));
     if (ret == NULL)
         return NULL;
 
diff --git a/libcutils/strdup8to16.c b/libcutils/strdup8to16.cpp
similarity index 98%
rename from libcutils/strdup8to16.c
rename to libcutils/strdup8to16.cpp
index c23cf8b..d1e51b9 100644
--- a/libcutils/strdup8to16.c
+++ b/libcutils/strdup8to16.cpp
@@ -16,9 +16,10 @@
 */
 
 #include <cutils/jstring.h>
+
 #include <assert.h>
-#include <stdlib.h>
 #include <limits.h>
+#include <stdlib.h>
 
 /* See http://www.unicode.org/reports/tr22/ for discussion
  * on invalid sequences
@@ -116,7 +117,7 @@
     int i;
 
     /* Mask for leader byte for lengths 1, 2, 3, and 4 respectively*/
-    static const char leaderMask[4] = {0xff, 0x1f, 0x0f, 0x07};
+    static const unsigned char leaderMask[4] = {0xff, 0x1f, 0x0f, 0x07};
 
     /* Bytes that start with bits "10" are not leading characters. */
     if (((**pUtf8Ptr) & 0xc0) == 0x80) {
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/tests/multiuser_test.cpp
index 2f9d854..4b0fd13 100644
--- a/libcutils/tests/multiuser_test.cpp
+++ b/libcutils/tests/multiuser_test.cpp
@@ -57,7 +57,10 @@
     EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 1000));
     EXPECT_EQ(20000U, multiuser_get_cache_gid(0, 10000));
     EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 50000));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 0));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 1000));
     EXPECT_EQ(1020000U, multiuser_get_cache_gid(10, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 50000));
 }
 
 TEST(MultiuserTest, TestExt) {
@@ -77,9 +80,12 @@
 }
 
 TEST(MultiuserTest, TestShared) {
-    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 0));
-    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 1000));
+    EXPECT_EQ(0U, multiuser_get_shared_gid(0, 0));
+    EXPECT_EQ(1000U, multiuser_get_shared_gid(0, 1000));
     EXPECT_EQ(50000U, multiuser_get_shared_gid(0, 10000));
     EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 50000));
-    EXPECT_EQ(1050000U, multiuser_get_shared_gid(10, 10000));
+    EXPECT_EQ(0U, multiuser_get_shared_gid(10, 0));
+    EXPECT_EQ(1000U, multiuser_get_shared_gid(10, 1000));
+    EXPECT_EQ(50000U, multiuser_get_shared_gid(10, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(10, 50000));
 }
diff --git a/libcutils/tests/trace-dev_test.cpp b/libcutils/tests/trace-dev_test.cpp
index edf981b..f8d4f00 100644
--- a/libcutils/tests/trace-dev_test.cpp
+++ b/libcutils/tests/trace-dev_test.cpp
@@ -25,7 +25,7 @@
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
-#include "../trace-dev.c"
+#include "../trace-dev.cpp"
 
 class TraceDevTest : public ::testing::Test {
  protected:
diff --git a/libcutils/threads.c b/libcutils/threads.cpp
similarity index 96%
rename from libcutils/threads.c
rename to libcutils/threads.cpp
index 036f8c5..beea8bd 100644
--- a/libcutils/threads.c
+++ b/libcutils/threads.cpp
@@ -14,7 +14,7 @@
 ** limitations under the License.
 */
 
-#include "cutils/threads.h"
+#include <cutils/threads.h>
 
 // For gettid.
 #if defined(__APPLE__)
@@ -35,7 +35,9 @@
 #ifndef __ANDROID__
 pid_t gettid() {
 #if defined(__APPLE__)
-  return syscall(SYS_thread_selfid);
+  uint64_t tid;
+  pthread_threadid_np(NULL, &tid);
+  return tid;
 #elif defined(__linux__)
   return syscall(__NR_gettid);
 #elif defined(_WIN32)
diff --git a/libcutils/trace-container.c b/libcutils/trace-container.cpp
similarity index 99%
rename from libcutils/trace-container.c
rename to libcutils/trace-container.cpp
index 03e91b1..d981f8f 100644
--- a/libcutils/trace-container.c
+++ b/libcutils/trace-container.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/trace.h>
+
 #include "trace-dev.inc"
 
 #include <cutils/sockets.h>
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.cpp
similarity index 98%
rename from libcutils/trace-dev.c
rename to libcutils/trace-dev.cpp
index 4468e83..4da8215 100644
--- a/libcutils/trace-dev.c
+++ b/libcutils/trace-dev.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/trace.h>
+
 #include "trace-dev.inc"
 
 static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
diff --git a/libcutils/trace-host.c b/libcutils/trace-host.cpp
similarity index 100%
rename from libcutils/trace-host.c
rename to libcutils/trace-host.cpp
diff --git a/libcutils/uevent.c b/libcutils/uevent.c
deleted file mode 100644
index f548dca..0000000
--- a/libcutils/uevent.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2011 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 <cutils/uevent.h>
-
-#include <errno.h>
-#include <stdbool.h>
-#include <string.h>
-#include <strings.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <linux/netlink.h>
-
-/**
- * Like recv(), but checks that messages actually originate from the kernel.
- */
-ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length)
-{
-    uid_t uid = -1;
-    return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid);
-}
-
-/**
- * Like the above, but passes a uid_t in by pointer. In the event that this
- * fails due to a bad uid check, the uid_t will be set to the uid of the
- * socket's peer.
- *
- * If this method rejects a netlink message from outside the kernel, it
- * returns -1, sets errno to EIO, and sets "user" to the UID associated with the
- * message. If the peer UID cannot be determined, "user" is set to -1."
- */
-ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer, size_t length, uid_t *uid)
-{
-    return uevent_kernel_recv(socket, buffer, length, true, uid);
-}
-
-ssize_t uevent_kernel_recv(int socket, void *buffer, size_t length, bool require_group, uid_t *uid)
-{
-    struct iovec iov = { buffer, length };
-    struct sockaddr_nl addr;
-    char control[CMSG_SPACE(sizeof(struct ucred))];
-    struct msghdr hdr = {
-        &addr,
-        sizeof(addr),
-        &iov,
-        1,
-        control,
-        sizeof(control),
-        0,
-    };
-
-    *uid = -1;
-    ssize_t n = recvmsg(socket, &hdr, 0);
-    if (n <= 0) {
-        return n;
-    }
-
-    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
-    if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
-        /* ignoring netlink message with no sender credentials */
-        goto out;
-    }
-
-    struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);
-    *uid = cred->uid;
-    if (cred->uid != 0) {
-        /* ignoring netlink message from non-root user */
-        goto out;
-    }
-
-    if (addr.nl_pid != 0) {
-        /* ignore non-kernel */
-        goto out;
-    }
-    if (require_group && addr.nl_groups == 0) {
-        /* ignore unicast messages when requested */
-        goto out;
-    }
-
-    return n;
-
-out:
-    /* clear residual potentially malicious data */
-    bzero(buffer, length);
-    errno = EIO;
-    return -1;
-}
-
-int uevent_open_socket(int buf_sz, bool passcred)
-{
-    struct sockaddr_nl addr;
-    int on = passcred;
-    int s;
-
-    memset(&addr, 0, sizeof(addr));
-    addr.nl_family = AF_NETLINK;
-    addr.nl_pid = getpid();
-    addr.nl_groups = 0xffffffff;
-
-    s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
-    if(s < 0)
-        return -1;
-
-    /* buf_sz should be less than net.core.rmem_max for this to succeed */
-    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0) {
-        close(s);
-        return -1;
-    }
-
-    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
-
-    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-        close(s);
-        return -1;
-    }
-
-    return s;
-}
diff --git a/libcutils/uevent.cpp b/libcutils/uevent.cpp
new file mode 100644
index 0000000..a84e5b0
--- /dev/null
+++ b/libcutils/uevent.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2011 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 <cutils/uevent.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <linux/netlink.h>
+
+#include <fstream>
+
+#include <private/android_filesystem_config.h>
+
+namespace {
+
+// Returns the uid of root in the current user namespace.
+// Returns AID_OVERFLOWUID if the root user is not mapped in the current
+// namespace.
+// Returns 0 if the kernel is not user namespace-aware (for backwards
+// compatibility) or if AID_OVERFLOWUID could not be validated to match what the
+// kernel would return.
+uid_t GetRootUid() {
+    constexpr uid_t kParentRootUid = 0;
+
+    std::ifstream uid_map_file("/proc/self/uid_map");
+    if (!uid_map_file) {
+        // The kernel does not support user namespaces.
+        return kParentRootUid;
+    }
+
+    uid_t current_namespace_uid, parent_namespace_uid;
+    uint32_t length;
+    while (uid_map_file >> current_namespace_uid >> parent_namespace_uid >> length) {
+        // Since kParentRootUid is 0, it should be the first entry in the mapped
+        // range.
+        if (parent_namespace_uid != kParentRootUid || length < 1) continue;
+        return current_namespace_uid;
+    }
+
+    // Sanity check: verify that the overflow UID is the one to be returned by
+    // the kernel.
+    std::ifstream overflowuid_file("/proc/sys/kernel/overflowuid");
+    if (!overflowuid_file) {
+        // It's better to return 0 in case we cannot make sure that the overflow
+        // UID matches.
+        return kParentRootUid;
+    }
+    uid_t kernel_overflow_uid;
+    if (!(overflowuid_file >> kernel_overflow_uid) || kernel_overflow_uid != AID_OVERFLOWUID)
+        return kParentRootUid;
+
+    // root is unmapped, use the kernel "overflow" uid.
+    return AID_OVERFLOWUID;
+}
+
+}  // namespace
+
+extern "C" {
+
+/**
+ * Like recv(), but checks that messages actually originate from the kernel.
+ */
+ssize_t uevent_kernel_multicast_recv(int socket, void* buffer, size_t length) {
+    uid_t uid = -1;
+    return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid);
+}
+
+/**
+ * Like the above, but passes a uid_t in by pointer. In the event that this
+ * fails due to a bad uid check, the uid_t will be set to the uid of the
+ * socket's peer.
+ *
+ * If this method rejects a netlink message from outside the kernel, it
+ * returns -1, sets errno to EIO, and sets "user" to the UID associated with the
+ * message. If the peer UID cannot be determined, "user" is set to -1."
+ */
+ssize_t uevent_kernel_multicast_uid_recv(int socket, void* buffer, size_t length, uid_t* uid) {
+    return uevent_kernel_recv(socket, buffer, length, true, uid);
+}
+
+ssize_t uevent_kernel_recv(int socket, void* buffer, size_t length, bool require_group, uid_t* uid) {
+    static const uid_t root_uid = GetRootUid();
+    struct iovec iov = {buffer, length};
+    struct sockaddr_nl addr;
+    char control[CMSG_SPACE(sizeof(struct ucred))];
+    struct msghdr hdr = {
+        &addr, sizeof(addr), &iov, 1, control, sizeof(control), 0,
+    };
+    struct ucred* cred;
+
+    *uid = -1;
+    ssize_t n = recvmsg(socket, &hdr, 0);
+    if (n <= 0) {
+        return n;
+    }
+
+    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+    if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
+        /* ignoring netlink message with no sender credentials */
+        goto out;
+    }
+
+    cred = (struct ucred*)CMSG_DATA(cmsg);
+    *uid = cred->uid;
+    if (cred->uid != root_uid) {
+        /* ignoring netlink message from non-root user */
+        goto out;
+    }
+
+    if (addr.nl_pid != 0) {
+        /* ignore non-kernel */
+        goto out;
+    }
+    if (require_group && addr.nl_groups == 0) {
+        /* ignore unicast messages when requested */
+        goto out;
+    }
+
+    return n;
+
+out:
+    /* clear residual potentially malicious data */
+    bzero(buffer, length);
+    errno = EIO;
+    return -1;
+}
+
+int uevent_open_socket(int buf_sz, bool passcred) {
+    struct sockaddr_nl addr;
+    int on = passcred;
+    int s;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.nl_family = AF_NETLINK;
+    addr.nl_pid = getpid();
+    addr.nl_groups = 0xffffffff;
+
+    s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
+    if (s < 0) return -1;
+
+    /* buf_sz should be less than net.core.rmem_max for this to succeed */
+    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0) {
+        close(s);
+        return -1;
+    }
+
+    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+
+    if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+        close(s);
+        return -1;
+    }
+
+    return s;
+}
+
+}  // extern "C"
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index cf03868..bcc0616 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -15,9 +15,12 @@
 cc_library_static {
     name: "libgrallocusage",
     vendor_available: true,
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     cppflags: [
         "-Weverything",
-        "-Werror",
         "-Wno-c++98-compat-pedantic",
         // Hide errors in headers we include
         "-Wno-global-constructors",
diff --git a/liblog/logger_name.c b/liblog/logger_name.c
index 979b82d..479bbfe 100644
--- a/liblog/logger_name.c
+++ b/liblog/logger_name.c
@@ -22,7 +22,7 @@
 
 /* In the future, we would like to make this list extensible */
 static const char* LOG_NAME[LOG_ID_MAX] = {
-  /* clang-format off */
+      /* clang-format off */
   [LOG_ID_MAIN] = "main",
   [LOG_ID_RADIO] = "radio",
   [LOG_ID_EVENTS] = "events",
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index 6ed568a..444a5ac 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -100,9 +100,13 @@
       EXPECT_LT(0, get_log_size);
       // crash buffer is allowed to be empty, that is actually healthy!
       // kernel buffer is allowed to be empty on "user" builds
+      // stats buffer is allowed to be empty TEMPORARILY.
+      // TODO: remove stats buffer from here once we start to use it in
+      // framework (b/68266385).
       EXPECT_LE(  // boolean 1 or 0 depending on expected content or empty
           !!((strcmp("crash", name) != 0) &&
-             ((strcmp("kernel", name) != 0) || __android_log_is_debuggable())),
+             ((strcmp("kernel", name) != 0) || __android_log_is_debuggable()) &&
+             (strcmp("stats", name) != 0)),
           android_logger_get_log_readable_size(logger));
     } else {
       EXPECT_NE(0, get_log_size);
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index b3861e0..5b9ba1c 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -33,10 +33,15 @@
     libnativebridge \
     libnativebridge-dummy
 
+libnativebridge_tests_common_cflags := \
+    -Wall \
+    -Werror \
+
 $(foreach file,$(test_src_files), \
     $(eval include $(CLEAR_VARS)) \
     $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
     $(eval LOCAL_SRC_FILES := $(file)) \
+    $(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
     $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
     $(eval include $(BUILD_NATIVE_TEST)) \
 )
@@ -45,6 +50,7 @@
     $(eval include $(CLEAR_VARS)) \
     $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
     $(eval LOCAL_SRC_FILES := $(file)) \
+    $(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
     $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
     $(eval include $(BUILD_HOST_NATIVE_TEST)) \
 )
diff --git a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
index f3e5f38..cd5a8e2 100644
--- a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
+++ b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
@@ -30,12 +30,12 @@
 
 namespace android {
 
-static constexpr const char* kTestData = "PreInitializeNativeBridge test.";
-
 TEST_F(NativeBridgeTest, PreInitializeNativeBridge) {
     ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
 #if !defined(__APPLE__)         // Mac OS does not support bind-mount.
 #if !defined(__ANDROID__)       // Cannot write into the hard-wired location.
+    static constexpr const char* kTestData = "PreInitializeNativeBridge test.";
+
     // Try to create our mount namespace.
     if (unshare(CLONE_NEWNS) != -1) {
         // Create a dummy file.
diff --git a/libnetutils/packet.c b/libnetutils/packet.c
index e53a4c8..9ecdd4f 100644
--- a/libnetutils/packet.c
+++ b/libnetutils/packet.c
@@ -218,6 +218,20 @@
      * to construct the pseudo header used in the checksum calculation.
      */
     dhcp_size = ntohs(packet.udp.len) - sizeof(packet.udp);
+    /*
+     * check validity of dhcp_size.
+     * 1) cannot be negative or zero.
+     * 2) src buffer contains enough bytes to copy
+     * 3) cannot exceed destination buffer
+     */
+    if ((dhcp_size <= 0) ||
+        ((int)(nread - sizeof(struct iphdr) - sizeof(struct udphdr)) < dhcp_size) ||
+        ((int)sizeof(struct dhcp_msg) < dhcp_size)) {
+#if VERBOSE
+        ALOGD("Malformed Packet");
+#endif
+        return -1;
+    }
     saddr = packet.ip.saddr;
     daddr = packet.ip.daddr;
     nread = ntohs(packet.ip.tot_len);
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
index a9fec7d..3d202fc 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -2,6 +2,7 @@
 
     name: "libpackagelistparser",
     srcs: ["packagelistparser.c"],
+    cflags: ["-Wall", "-Werror"],
     shared_libs: ["liblog"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index 55891db..c7306cd 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -25,6 +25,8 @@
 	buffer.cpp
 
 PIXELFLINGER_CFLAGS := -fstrict-aliasing -fomit-frame-pointer
+PIXELFLINGER_CFLAGS += -Wall -Werror
+PIXELFLINGER_CFLAGS += -Wno-unused-function
 
 PIXELFLINGER_SRC_FILES_arm := \
 	codeflinger/ARMAssembler.cpp \
diff --git a/libpixelflinger/codeflinger/Arm64Assembler.cpp b/libpixelflinger/codeflinger/Arm64Assembler.cpp
index bff87bb..aebc129 100644
--- a/libpixelflinger/codeflinger/Arm64Assembler.cpp
+++ b/libpixelflinger/codeflinger/Arm64Assembler.cpp
@@ -151,11 +151,11 @@
 
 namespace android {
 
-static const char* shift_codes[] =
+static __unused const char* shift_codes[] =
 {
     "LSL", "LSR", "ASR", "ROR"
 };
-static const char *cc_codes[] =
+static __unused const char *cc_codes[] =
 {
     "EQ", "NE", "CS", "CC", "MI",
     "PL", "VS", "VC", "HI", "LS",
@@ -984,7 +984,7 @@
 // A64 instructions
 // ----------------------------------------------------------------------------
 
-static const char * dataTransferOpName[] =
+static __unused const char * dataTransferOpName[] =
 {
     "LDR","LDRB","LDRH","STR","STRB","STRH"
 };
diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp
index 91fbd53..04e285d 100644
--- a/libpixelflinger/codeflinger/GGLAssembler.cpp
+++ b/libpixelflinger/codeflinger/GGLAssembler.cpp
@@ -94,8 +94,6 @@
 
 int GGLAssembler::scanline_core(const needs_t& needs, context_t const* c)
 {
-    int64_t duration = ggl_system_time();
-
     mBlendFactorCached = 0;
     mBlending = 0;
     mMasking = 0;
@@ -353,7 +351,6 @@
     fragment_parts_t& parts, const needs_t& needs)
 {
     Scratch scratches(registerFile());
-    int Rctx = mBuilderContext.Rctx;
 
     // compute count
     comment("compute ct (# of pixels to process)");
diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.cpp b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
index d5e4cea..d6d2156 100644
--- a/libpixelflinger/codeflinger/MIPS64Assembler.cpp
+++ b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
@@ -39,6 +39,7 @@
 #include "mips64_disassem.h"
 
 #define NOT_IMPLEMENTED()  LOG_ALWAYS_FATAL("Arm instruction %s not yet implemented\n", __func__)
+#define __unused __attribute__((__unused__))
 
 // ----------------------------------------------------------------------------
 
@@ -146,7 +147,7 @@
     mMips->MOVE(R_v0, R_a0);    // move context * passed in a0 to v0 (arm r0)
 }
 
-void ArmToMips64Assembler::epilog(uint32_t touched)
+void ArmToMips64Assembler::epilog(uint32_t touched __unused)
 {
     mArmPC[mInum++] = pc();  // save starting PC for this instr
 
@@ -205,7 +206,7 @@
 
 // shifters...
 
-bool ArmToMips64Assembler::isValidImmediate(uint32_t immediate)
+bool ArmToMips64Assembler::isValidImmediate(uint32_t immediate __unused)
 {
     // for MIPS, any 32-bit immediate is OK
     return true;
@@ -225,13 +226,14 @@
     return AMODE_REG_IMM;
 }
 
-uint32_t ArmToMips64Assembler::reg_rrx(int Rm)
+uint32_t ArmToMips64Assembler::reg_rrx(int Rm __unused)
 {
     // reg_rrx mode is not used in the GLLAssember code at this time
     return AMODE_UNSUPPORTED;
 }
 
-uint32_t ArmToMips64Assembler::reg_reg(int Rm, int type, int Rs)
+uint32_t ArmToMips64Assembler::reg_reg(int Rm __unused, int type __unused,
+                                       int Rs __unused)
 {
     // reg_reg mode is not used in the GLLAssember code at this time
     return AMODE_UNSUPPORTED;
@@ -272,14 +274,15 @@
     return AMODE_REG_SCALE_PRE;
 }
 
-uint32_t ArmToMips64Assembler::reg_scale_post(int Rm, int type, uint32_t shift)
+uint32_t ArmToMips64Assembler::reg_scale_post(int Rm __unused, int type __unused,
+                                              uint32_t shift __unused)
 {
     LOG_ALWAYS_FATAL("adr mode reg_scale_post not yet implemented\n");
     return AMODE_UNSUPPORTED;
 }
 
 // LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0)
-uint32_t ArmToMips64Assembler::immed8_pre(int32_t immed8, int W)
+uint32_t ArmToMips64Assembler::immed8_pre(int32_t immed8, int W __unused)
 {
     LOG_ALWAYS_FATAL("adr mode immed8_pre not yet implemented\n");
 
@@ -305,7 +308,7 @@
     return AMODE_REG_PRE;
 }
 
-uint32_t ArmToMips64Assembler::reg_post(int Rm)
+uint32_t ArmToMips64Assembler::reg_post(int Rm __unused)
 {
     LOG_ALWAYS_FATAL("adr mode reg_post not yet implemented\n");
     return AMODE_UNSUPPORTED;
@@ -320,12 +323,6 @@
 #pragma mark Data Processing...
 #endif
 
-
-static const char * const dpOpNames[] = {
-    "AND", "EOR", "SUB", "RSB", "ADD", "ADC", "SBC", "RSC",
-    "TST", "TEQ", "CMP", "CMN", "ORR", "MOV", "BIC", "MVN"
-};
-
 // check if the operand registers from a previous CMP or S-bit instruction
 // would be overwritten by this instruction. If so, move the value to a
 // safe register.
@@ -594,7 +591,7 @@
 #endif
 
 // multiply, accumulate
-void ArmToMips64Assembler::MLA(int cc, int s,
+void ArmToMips64Assembler::MLA(int cc __unused, int s,
         int Rd, int Rm, int Rs, int Rn) {
 
     //ALOGW("MLA");
@@ -608,7 +605,7 @@
     }
 }
 
-void ArmToMips64Assembler::MUL(int cc, int s,
+void ArmToMips64Assembler::MUL(int cc __unused, int s,
         int Rd, int Rm, int Rs) {
     mArmPC[mInum++] = pc();
     mMips->MUL(Rd, Rm, Rs);
@@ -618,7 +615,7 @@
     }
 }
 
-void ArmToMips64Assembler::UMULL(int cc, int s,
+void ArmToMips64Assembler::UMULL(int cc __unused, int s,
         int RdLo, int RdHi, int Rm, int Rs) {
     mArmPC[mInum++] = pc();
     mMips->MUH(RdHi, Rm, Rs);
@@ -631,8 +628,8 @@
     }
 }
 
-void ArmToMips64Assembler::UMUAL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs) {
+void ArmToMips64Assembler::UMUAL(int cc __unused, int s,
+        int RdLo __unused, int RdHi, int Rm __unused, int Rs __unused) {
     LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
                         "UMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
     // *mPC++ =    (cc<<28) | (1<<23) | (1<<21) | (s<<20) |
@@ -647,8 +644,8 @@
     }
 }
 
-void ArmToMips64Assembler::SMULL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs) {
+void ArmToMips64Assembler::SMULL(int cc __unused, int s,
+        int RdLo __unused, int RdHi, int Rm __unused, int Rs __unused) {
     LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
                         "SMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
     // *mPC++ =    (cc<<28) | (1<<23) | (1<<22) | (s<<20) |
@@ -662,8 +659,8 @@
         LOG_ALWAYS_FATAL("Condition on SMULL must be on 64-bit result\n");
     }
 }
-void ArmToMips64Assembler::SMUAL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs) {
+void ArmToMips64Assembler::SMUAL(int cc __unused, int s,
+        int RdLo __unused, int RdHi, int Rm __unused, int Rs __unused) {
     LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
                         "SMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
     // *mPC++ =    (cc<<28) | (1<<23) | (1<<22) | (1<<21) | (s<<20) |
@@ -717,26 +714,26 @@
     }
 }
 
-void ArmToMips64Assembler::BL(int cc, const char* label)
+void ArmToMips64Assembler::BL(int cc __unused, const char* label __unused)
 {
     LOG_ALWAYS_FATAL("branch-and-link not supported yet\n");
     mArmPC[mInum++] = pc();
 }
 
 // no use for Branches with integer PC, but they're in the Interface class ....
-void ArmToMips64Assembler::B(int cc, uint32_t* to_pc)
+void ArmToMips64Assembler::B(int cc __unused, uint32_t* to_pc __unused)
 {
     LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
     mArmPC[mInum++] = pc();
 }
 
-void ArmToMips64Assembler::BL(int cc, uint32_t* to_pc)
+void ArmToMips64Assembler::BL(int cc __unused, uint32_t* to_pc __unused)
 {
     LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
     mArmPC[mInum++] = pc();
 }
 
-void ArmToMips64Assembler::BX(int cc, int Rn)
+void ArmToMips64Assembler::BX(int cc __unused, int Rn __unused)
 {
     LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
     mArmPC[mInum++] = pc();
@@ -750,7 +747,7 @@
 #endif
 
 // data transfer...
-void ArmToMips64Assembler::LDR(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::LDR(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -784,7 +781,7 @@
     }
 }
 
-void ArmToMips64Assembler::LDRB(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::LDRB(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -813,7 +810,7 @@
 
 }
 
-void ArmToMips64Assembler::STR(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::STR(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -849,7 +846,7 @@
     }
 }
 
-void ArmToMips64Assembler::STRB(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::STRB(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -877,7 +874,7 @@
     }
 }
 
-void ArmToMips64Assembler::LDRH(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::LDRH(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed8_pre(0)
@@ -905,21 +902,23 @@
     }
 }
 
-void ArmToMips64Assembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::LDRSB(int cc __unused, int Rd __unused,
+                                 int Rn __unused, uint32_t offset __unused)
 {
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::LDRSH(int cc __unused, int Rd __unused,
+                                 int Rn __unused, uint32_t offset __unused)
 {
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::STRH(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::STRH(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed8_pre(0)
@@ -955,8 +954,8 @@
 #endif
 
 // block data transfer...
-void ArmToMips64Assembler::LDM(int cc, int dir,
-        int Rn, int W, uint32_t reg_list)
+void ArmToMips64Assembler::LDM(int cc __unused, int dir __unused,
+        int Rn __unused, int W __unused, uint32_t reg_list __unused)
 {   //                        ED FD EA FA      IB IA DB DA
     // const uint8_t P[8] = { 1, 0, 1, 0,      1, 0, 1, 0 };
     // const uint8_t U[8] = { 1, 1, 0, 0,      1, 1, 0, 0 };
@@ -967,8 +966,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::STM(int cc, int dir,
-        int Rn, int W, uint32_t reg_list)
+void ArmToMips64Assembler::STM(int cc __unused, int dir __unused,
+        int Rn __unused, int W __unused, uint32_t reg_list __unused)
 {   //                        FA EA FD ED      IB IA DB DA
     // const uint8_t P[8] = { 0, 1, 0, 1,      1, 0, 1, 0 };
     // const uint8_t U[8] = { 0, 0, 1, 1,      1, 1, 0, 0 };
@@ -987,21 +986,23 @@
 #endif
 
 // special...
-void ArmToMips64Assembler::SWP(int cc, int Rn, int Rd, int Rm) {
+void ArmToMips64Assembler::SWP(int cc __unused, int Rn __unused,
+                               int Rd __unused, int Rm __unused) {
     // *mPC++ = (cc<<28) | (2<<23) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::SWPB(int cc, int Rn, int Rd, int Rm) {
+void ArmToMips64Assembler::SWPB(int cc __unused, int Rn __unused,
+                                int Rd __unused, int Rm __unused) {
     // *mPC++ = (cc<<28) | (2<<23) | (1<<22) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::SWI(int cc, uint32_t comment) {
+void ArmToMips64Assembler::SWI(int cc __unused, uint32_t comment __unused) {
     // *mPC++ = (cc<<28) | (0xF<<24) | comment;
     mArmPC[mInum++] = pc();
     mMips->NOP2();
@@ -1015,7 +1016,7 @@
 #endif
 
 // DSP instructions...
-void ArmToMips64Assembler::PLD(int Rn, uint32_t offset) {
+void ArmToMips64Assembler::PLD(int Rn __unused, uint32_t offset) {
     LOG_ALWAYS_FATAL_IF(!((offset&(1<<24)) && !(offset&(1<<21))),
                         "PLD only P=1, W=0");
     // *mPC++ = 0xF550F000 | (Rn<<16) | offset;
@@ -1024,13 +1025,14 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::CLZ(int cc, int Rd, int Rm)
+void ArmToMips64Assembler::CLZ(int cc __unused, int Rd, int Rm)
 {
     mArmPC[mInum++] = pc();
     mMips->CLZ(Rd, Rm);
 }
 
-void ArmToMips64Assembler::QADD(int cc,  int Rd, int Rm, int Rn)
+void ArmToMips64Assembler::QADD(int cc __unused, int Rd __unused,
+                                int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1000050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1038,7 +1040,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::QDADD(int cc,  int Rd, int Rm, int Rn)
+void ArmToMips64Assembler::QDADD(int cc __unused, int Rd __unused,
+                                 int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1400050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1046,7 +1049,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::QSUB(int cc,  int Rd, int Rm, int Rn)
+void ArmToMips64Assembler::QSUB(int cc __unused, int Rd __unused,
+                                int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1200050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1054,7 +1058,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::QDSUB(int cc,  int Rd, int Rm, int Rn)
+void ArmToMips64Assembler::QDSUB(int cc __unused, int Rd __unused,
+                                 int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1600050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1063,7 +1068,7 @@
 }
 
 // 16 x 16 signed multiply (like SMLAxx without the accumulate)
-void ArmToMips64Assembler::SMUL(int cc, int xy,
+void ArmToMips64Assembler::SMUL(int cc __unused, int xy,
                 int Rd, int Rm, int Rs)
 {
     mArmPC[mInum++] = pc();
@@ -1092,7 +1097,7 @@
 }
 
 // signed 32b x 16b multiple, save top 32-bits of 48-bit result
-void ArmToMips64Assembler::SMULW(int cc, int y,
+void ArmToMips64Assembler::SMULW(int cc __unused, int y,
                 int Rd, int Rm, int Rs)
 {
     mArmPC[mInum++] = pc();
@@ -1111,7 +1116,7 @@
 }
 
 // 16 x 16 signed multiply, accumulate: Rd = Rm{16} * Rs{16} + Rn
-void ArmToMips64Assembler::SMLA(int cc, int xy,
+void ArmToMips64Assembler::SMLA(int cc __unused, int xy,
                 int Rd, int Rm, int Rs, int Rn)
 {
     mArmPC[mInum++] = pc();
@@ -1141,8 +1146,9 @@
     mMips->ADDU(Rd, R_at, Rn);
 }
 
-void ArmToMips64Assembler::SMLAL(int cc, int xy,
-                int RdHi, int RdLo, int Rs, int Rm)
+void ArmToMips64Assembler::SMLAL(int cc __unused, int xy __unused,
+                                 int RdHi __unused, int RdLo __unused,
+                                 int Rs __unused, int Rm __unused)
 {
     // *mPC++ = (cc<<28) | 0x1400080 | (RdHi<<16) | (RdLo<<12) | (Rs<<8) | (xy<<4) | Rm;
     mArmPC[mInum++] = pc();
@@ -1150,8 +1156,9 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::SMLAW(int cc, int y,
-                int Rd, int Rm, int Rs, int Rn)
+void ArmToMips64Assembler::SMLAW(int cc __unused, int y __unused,
+                                 int Rd __unused, int Rm __unused,
+                                 int Rs __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm;
     mArmPC[mInum++] = pc();
@@ -1160,7 +1167,7 @@
 }
 
 // used by ARMv6 version of GGLAssembler::filter32
-void ArmToMips64Assembler::UXTB16(int cc, int Rd, int Rm, int rotate)
+void ArmToMips64Assembler::UXTB16(int cc __unused, int Rd, int Rm, int rotate)
 {
     mArmPC[mInum++] = pc();
 
@@ -1173,7 +1180,8 @@
     mMips->AND(Rd, R_at2, R_at);
 }
 
-void ArmToMips64Assembler::UBFX(int cc, int Rd, int Rn, int lsb, int width)
+void ArmToMips64Assembler::UBFX(int cc __unused, int Rd __unused, int Rn __unused,
+                                int lsb __unused, int width __unused)
 {
      /* Placeholder for UBFX */
      mArmPC[mInum++] = pc();
@@ -1202,7 +1210,8 @@
     dataProcessing(opSUB64, cc, s, Rd, Rn, Op2);
 }
 
-void ArmToMips64Assembler::ADDR_LDR(int cc, int Rd, int Rn, uint32_t offset) {
+void ArmToMips64Assembler::ADDR_LDR(int cc __unused, int Rd,
+                                    int Rn, uint32_t offset) {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
     if (offset > AMODE_UNSUPPORTED) offset = 0;
@@ -1235,7 +1244,8 @@
     }
 }
 
-void ArmToMips64Assembler::ADDR_STR(int cc, int Rd, int Rn, uint32_t offset) {
+void ArmToMips64Assembler::ADDR_STR(int cc __unused, int Rd,
+                                    int Rn, uint32_t offset) {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
     if (offset > AMODE_UNSUPPORTED) offset = 0;
@@ -1290,14 +1300,12 @@
 */
 
 MIPS64Assembler::MIPS64Assembler(const sp<Assembly>& assembly, ArmToMips64Assembler *parent)
-    : mParent(parent),
-    MIPSAssembler::MIPSAssembler(assembly, NULL)
+    : MIPSAssembler::MIPSAssembler(assembly, NULL), mParent(parent)
 {
 }
 
 MIPS64Assembler::MIPS64Assembler(void* assembly, ArmToMips64Assembler *parent)
-    : mParent(parent),
-    MIPSAssembler::MIPSAssembler(assembly)
+    : MIPSAssembler::MIPSAssembler(assembly), mParent(parent)
 {
 }
 
@@ -1319,7 +1327,7 @@
 }
 
 
-void MIPS64Assembler::disassemble(const char* name)
+void MIPS64Assembler::disassemble(const char* name __unused)
 {
     char di_buf[140];
 
@@ -1334,11 +1342,6 @@
         }
     }
 
-    // iArm is an index to Arm instructions 1...n for this assembly sequence
-    // mArmPC[iArm] holds the value of the Mips-PC for the first MIPS
-    // instruction corresponding to that Arm instruction number
-
-    int iArm = 0;
     size_t count = pc()-base();
     uint32_t* mipsPC = base();
 
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp
index 865a568..039a725 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.cpp
+++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp
@@ -51,6 +51,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <inttypes.h>
 
 #include <cutils/properties.h>
 #include <log/log.h>
@@ -60,6 +61,8 @@
 #include "MIPSAssembler.h"
 #include "mips_disassem.h"
 
+#define __unused __attribute__((__unused__))
+
 // Choose MIPS arch variant following gcc flags
 #if defined(__mips__) && __mips==32 && __mips_isa_rev>=2
 #define mips32r2 1
@@ -167,7 +170,7 @@
     mMips->MOVE(R_v0, R_a0);    // move context * passed in a0 to v0 (arm r0)
 }
 
-void ArmToMipsAssembler::epilog(uint32_t touched)
+void ArmToMipsAssembler::epilog(uint32_t touched __unused)
 {
     mArmPC[mInum++] = pc();  // save starting PC for this instr
 
@@ -213,7 +216,7 @@
 
 // shifters...
 
-bool ArmToMipsAssembler::isValidImmediate(uint32_t immediate)
+bool ArmToMipsAssembler::isValidImmediate(uint32_t immediate __unused)
 {
     // for MIPS, any 32-bit immediate is OK
     return true;
@@ -234,13 +237,14 @@
     return AMODE_REG_IMM;
 }
 
-uint32_t ArmToMipsAssembler::reg_rrx(int Rm)
+uint32_t ArmToMipsAssembler::reg_rrx(int Rm __unused)
 {
     // reg_rrx mode is not used in the GLLAssember code at this time
     return AMODE_UNSUPPORTED;
 }
 
-uint32_t ArmToMipsAssembler::reg_reg(int Rm, int type, int Rs)
+uint32_t ArmToMipsAssembler::reg_reg(int Rm __unused, int type __unused,
+                                     int Rs __unused)
 {
     // reg_reg mode is not used in the GLLAssember code at this time
     return AMODE_UNSUPPORTED;
@@ -281,14 +285,15 @@
     return AMODE_REG_SCALE_PRE;
 }
 
-uint32_t ArmToMipsAssembler::reg_scale_post(int Rm, int type, uint32_t shift)
+uint32_t ArmToMipsAssembler::reg_scale_post(int Rm __unused, int type __unused,
+                                            uint32_t shift __unused)
 {
     LOG_ALWAYS_FATAL("adr mode reg_scale_post not yet implemented\n");
     return AMODE_UNSUPPORTED;
 }
 
 // LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0)
-uint32_t ArmToMipsAssembler::immed8_pre(int32_t immed8, int W)
+uint32_t ArmToMipsAssembler::immed8_pre(int32_t immed8, int W __unused)
 {
     // uint32_t offset = abs(immed8);
 
@@ -318,7 +323,7 @@
     return AMODE_REG_PRE;
 }
 
-uint32_t ArmToMipsAssembler::reg_post(int Rm)
+uint32_t ArmToMipsAssembler::reg_post(int Rm __unused)
 {
     LOG_ALWAYS_FATAL("adr mode reg_post not yet implemented\n");
     return AMODE_UNSUPPORTED;
@@ -333,12 +338,6 @@
 #pragma mark Data Processing...
 #endif
 
-
-static const char * const dpOpNames[] = {
-    "AND", "EOR", "SUB", "RSB", "ADD", "ADC", "SBC", "RSC",
-    "TST", "TEQ", "CMP", "CMN", "ORR", "MOV", "BIC", "MVN"
-};
-
 // check if the operand registers from a previous CMP or S-bit instruction
 // would be overwritten by this instruction. If so, move the value to a
 // safe register.
@@ -605,7 +604,7 @@
 #endif
 
 // multiply, accumulate
-void ArmToMipsAssembler::MLA(int cc, int s,
+void ArmToMipsAssembler::MLA(int cc __unused, int s,
         int Rd, int Rm, int Rs, int Rn) {
 
     mArmPC[mInum++] = pc();  // save starting PC for this instr
@@ -618,7 +617,7 @@
     }
 }
 
-void ArmToMipsAssembler::MUL(int cc, int s,
+void ArmToMipsAssembler::MUL(int cc __unused, int s,
         int Rd, int Rm, int Rs) {
     mArmPC[mInum++] = pc();
     mMips->MUL(Rd, Rm, Rs);
@@ -628,7 +627,7 @@
     }
 }
 
-void ArmToMipsAssembler::UMULL(int cc, int s,
+void ArmToMipsAssembler::UMULL(int cc __unused, int s,
         int RdLo, int RdHi, int Rm, int Rs) {
     mArmPC[mInum++] = pc();
     mMips->MULT(Rm, Rs);
@@ -641,8 +640,8 @@
     }
 }
 
-void ArmToMipsAssembler::UMUAL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs) {
+void ArmToMipsAssembler::UMUAL(int cc __unused, int s,
+        int RdLo __unused, int RdHi, int Rm __unused, int Rs __unused) {
     LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
                         "UMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
     // *mPC++ =    (cc<<28) | (1<<23) | (1<<21) | (s<<20) |
@@ -657,8 +656,8 @@
     }
 }
 
-void ArmToMipsAssembler::SMULL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs) {
+void ArmToMipsAssembler::SMULL(int cc __unused, int s,
+        int RdLo __unused, int RdHi, int Rm __unused, int Rs __unused) {
     LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
                         "SMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
     // *mPC++ =    (cc<<28) | (1<<23) | (1<<22) | (s<<20) |
@@ -672,8 +671,8 @@
         LOG_ALWAYS_FATAL("Condition on SMULL must be on 64-bit result\n");
     }
 }
-void ArmToMipsAssembler::SMUAL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs) {
+void ArmToMipsAssembler::SMUAL(int cc __unused, int s,
+        int RdLo __unused, int RdHi, int Rm __unused, int Rs __unused) {
     LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
                         "SMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
     // *mPC++ =    (cc<<28) | (1<<23) | (1<<22) | (1<<21) | (s<<20) |
@@ -727,26 +726,26 @@
     }
 }
 
-void ArmToMipsAssembler::BL(int cc, const char* label)
+void ArmToMipsAssembler::BL(int cc __unused, const char* label __unused)
 {
     LOG_ALWAYS_FATAL("branch-and-link not supported yet\n");
     mArmPC[mInum++] = pc();
 }
 
 // no use for Branches with integer PC, but they're in the Interface class ....
-void ArmToMipsAssembler::B(int cc, uint32_t* to_pc)
+void ArmToMipsAssembler::B(int cc __unused, uint32_t* to_pc __unused)
 {
     LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
     mArmPC[mInum++] = pc();
 }
 
-void ArmToMipsAssembler::BL(int cc, uint32_t* to_pc)
+void ArmToMipsAssembler::BL(int cc __unused, uint32_t* to_pc __unused)
 {
     LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
     mArmPC[mInum++] = pc();
 }
 
-void ArmToMipsAssembler::BX(int cc, int Rn)
+void ArmToMipsAssembler::BX(int cc __unused, int Rn __unused)
 {
     LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
     mArmPC[mInum++] = pc();
@@ -760,7 +759,7 @@
 #endif
 
 // data transfer...
-void ArmToMipsAssembler::LDR(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::LDR(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -794,7 +793,7 @@
     }
 }
 
-void ArmToMipsAssembler::LDRB(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::LDRB(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -823,7 +822,7 @@
 
 }
 
-void ArmToMipsAssembler::STR(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::STR(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -859,7 +858,7 @@
     }
 }
 
-void ArmToMipsAssembler::STRB(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::STRB(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -887,7 +886,7 @@
     }
 }
 
-void ArmToMipsAssembler::LDRH(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::LDRH(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed8_pre(0)
@@ -915,21 +914,23 @@
     }
 }
 
-void ArmToMipsAssembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::LDRSB(int cc __unused, int Rd __unused,
+                               int Rn __unused, uint32_t offset __unused)
 {
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::LDRSH(int cc __unused, int Rd __unused,
+                               int Rn __unused, uint32_t offset __unused)
 {
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::STRH(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::STRH(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed8_pre(0)
@@ -965,8 +966,8 @@
 #endif
 
 // block data transfer...
-void ArmToMipsAssembler::LDM(int cc, int dir,
-        int Rn, int W, uint32_t reg_list)
+void ArmToMipsAssembler::LDM(int cc __unused, int dir __unused,
+        int Rn __unused, int W __unused, uint32_t reg_list __unused)
 {   //                        ED FD EA FA      IB IA DB DA
     // const uint8_t P[8] = { 1, 0, 1, 0,      1, 0, 1, 0 };
     // const uint8_t U[8] = { 1, 1, 0, 0,      1, 1, 0, 0 };
@@ -977,8 +978,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::STM(int cc, int dir,
-        int Rn, int W, uint32_t reg_list)
+void ArmToMipsAssembler::STM(int cc __unused, int dir __unused,
+        int Rn __unused, int W __unused, uint32_t reg_list __unused)
 {   //                        FA EA FD ED      IB IA DB DA
     // const uint8_t P[8] = { 0, 1, 0, 1,      1, 0, 1, 0 };
     // const uint8_t U[8] = { 0, 0, 1, 1,      1, 1, 0, 0 };
@@ -997,21 +998,23 @@
 #endif
 
 // special...
-void ArmToMipsAssembler::SWP(int cc, int Rn, int Rd, int Rm) {
+void ArmToMipsAssembler::SWP(int cc __unused, int Rn __unused,
+                             int Rd __unused, int Rm __unused) {
     // *mPC++ = (cc<<28) | (2<<23) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::SWPB(int cc, int Rn, int Rd, int Rm) {
+void ArmToMipsAssembler::SWPB(int cc __unused, int Rn __unused,
+                              int Rd __unused, int Rm __unused) {
     // *mPC++ = (cc<<28) | (2<<23) | (1<<22) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::SWI(int cc, uint32_t comment) {
+void ArmToMipsAssembler::SWI(int cc __unused, uint32_t comment __unused) {
     // *mPC++ = (cc<<28) | (0xF<<24) | comment;
     mArmPC[mInum++] = pc();
     mMips->NOP2();
@@ -1025,7 +1028,7 @@
 #endif
 
 // DSP instructions...
-void ArmToMipsAssembler::PLD(int Rn, uint32_t offset) {
+void ArmToMipsAssembler::PLD(int Rn __unused, uint32_t offset) {
     LOG_ALWAYS_FATAL_IF(!((offset&(1<<24)) && !(offset&(1<<21))),
                         "PLD only P=1, W=0");
     // *mPC++ = 0xF550F000 | (Rn<<16) | offset;
@@ -1034,13 +1037,14 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::CLZ(int cc, int Rd, int Rm)
+void ArmToMipsAssembler::CLZ(int cc __unused, int Rd, int Rm)
 {
     mArmPC[mInum++] = pc();
     mMips->CLZ(Rd, Rm);
 }
 
-void ArmToMipsAssembler::QADD(int cc,  int Rd, int Rm, int Rn)
+void ArmToMipsAssembler::QADD(int cc __unused,  int Rd __unused,
+                              int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1000050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1048,7 +1052,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::QDADD(int cc,  int Rd, int Rm, int Rn)
+void ArmToMipsAssembler::QDADD(int cc __unused,  int Rd __unused,
+                               int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1400050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1056,7 +1061,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::QSUB(int cc,  int Rd, int Rm, int Rn)
+void ArmToMipsAssembler::QSUB(int cc __unused,  int Rd __unused,
+                              int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1200050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1064,7 +1070,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::QDSUB(int cc,  int Rd, int Rm, int Rn)
+void ArmToMipsAssembler::QDSUB(int cc __unused,  int Rd __unused,
+                               int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1600050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1073,7 +1080,7 @@
 }
 
 // 16 x 16 signed multiply (like SMLAxx without the accumulate)
-void ArmToMipsAssembler::SMUL(int cc, int xy,
+void ArmToMipsAssembler::SMUL(int cc __unused, int xy,
                 int Rd, int Rm, int Rs)
 {
     mArmPC[mInum++] = pc();
@@ -1112,7 +1119,7 @@
 }
 
 // signed 32b x 16b multiple, save top 32-bits of 48-bit result
-void ArmToMipsAssembler::SMULW(int cc, int y,
+void ArmToMipsAssembler::SMULW(int cc __unused, int y,
                 int Rd, int Rm, int Rs)
 {
     mArmPC[mInum++] = pc();
@@ -1132,7 +1139,7 @@
 }
 
 // 16 x 16 signed multiply, accumulate: Rd = Rm{16} * Rs{16} + Rn
-void ArmToMipsAssembler::SMLA(int cc, int xy,
+void ArmToMipsAssembler::SMLA(int cc __unused, int xy,
                 int Rd, int Rm, int Rs, int Rn)
 {
     mArmPC[mInum++] = pc();
@@ -1172,8 +1179,9 @@
     mMips->ADDU(Rd, R_at, Rn);
 }
 
-void ArmToMipsAssembler::SMLAL(int cc, int xy,
-                int RdHi, int RdLo, int Rs, int Rm)
+void ArmToMipsAssembler::SMLAL(int cc __unused, int xy __unused,
+                               int RdHi __unused, int RdLo __unused,
+                               int Rs __unused, int Rm __unused)
 {
     // *mPC++ = (cc<<28) | 0x1400080 | (RdHi<<16) | (RdLo<<12) | (Rs<<8) | (xy<<4) | Rm;
     mArmPC[mInum++] = pc();
@@ -1181,8 +1189,9 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::SMLAW(int cc, int y,
-                int Rd, int Rm, int Rs, int Rn)
+void ArmToMipsAssembler::SMLAW(int cc __unused, int y __unused,
+                               int Rd __unused, int Rm __unused,
+                               int Rs __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm;
     mArmPC[mInum++] = pc();
@@ -1191,7 +1200,7 @@
 }
 
 // used by ARMv6 version of GGLAssembler::filter32
-void ArmToMipsAssembler::UXTB16(int cc, int Rd, int Rm, int rotate)
+void ArmToMipsAssembler::UXTB16(int cc __unused, int Rd, int Rm, int rotate)
 {
     mArmPC[mInum++] = pc();
 
@@ -1202,7 +1211,9 @@
     mMips->AND(Rd, Rm, 0x00FF00FF);
 }
 
-void ArmToMipsAssembler::UBFX(int cc, int Rd, int Rn, int lsb, int width)
+void ArmToMipsAssembler::UBFX(int cc __unused, int Rd __unused,
+                              int Rn __unused, int lsb __unused,
+                              int width __unused)
 {
      /* Placeholder for UBFX */
      mArmPC[mInum++] = pc();
@@ -1339,11 +1350,6 @@
         }
     }
 
-    // iArm is an index to Arm instructions 1...n for this assembly sequence
-    // mArmPC[iArm] holds the value of the Mips-PC for the first MIPS
-    // instruction corresponding to that Arm instruction number
-
-    int iArm = 0;
     size_t count = pc()-base();
     uint32_t* mipsPC = base();
     while (count--) {
@@ -1359,7 +1365,7 @@
         ::mips_disassem(mipsPC, di_buf, arm_disasm_fmt);
         string_detab(di_buf);
         string_pad(di_buf, 30);
-        ALOGW("%08x:    %08x    %s", uintptr_t(mipsPC), uint32_t(*mipsPC), di_buf);
+        ALOGW("0x%p:    %08x    %s", mipsPC, uint32_t(*mipsPC), di_buf);
         mipsPC++;
     }
 }
@@ -1381,7 +1387,7 @@
     // empty - done in ArmToMipsAssembler
 }
 
-void MIPSAssembler::epilog(uint32_t touched)
+void MIPSAssembler::epilog(uint32_t touched __unused)
 {
     // empty - done in ArmToMipsAssembler
 }
@@ -1403,7 +1409,7 @@
 
     // the instruction & data caches are flushed by CodeCache
     const int64_t duration = ggl_system_time() - mDuration;
-    const char * const format = "generated %s (%d ins) at [%p:%p] in %lld ns\n";
+    const char * const format = "generated %s (%d ins) at [%p:%p] in %" PRId64 " ns\n";
     ALOGI(format, name, int(pc()-base()), base(), pc(), duration);
 
     char value[PROPERTY_VALUE_MAX];
@@ -1864,7 +1870,7 @@
     BEQ(Rs, R_zero, label);
 }
 
-void MIPSAssembler::BNEZ(int Rs, const char* label)
+void MIPSAssembler::BNEZ(int Rs __unused, const char* label)
 {
     BNE(R_at, R_zero, label);
 }
diff --git a/libpixelflinger/codeflinger/load_store.cpp b/libpixelflinger/codeflinger/load_store.cpp
index da21e1d..4db0a49 100644
--- a/libpixelflinger/codeflinger/load_store.cpp
+++ b/libpixelflinger/codeflinger/load_store.cpp
@@ -232,7 +232,6 @@
 void GGLAssembler::downshift(
         pixel_t& d, int component, component_t s, const reg_t& dither)
 {
-    const needs_t& needs = mBuilderContext.needs;
     Scratch scratches(registerFile());
 
     int sh = s.h;
diff --git a/libpixelflinger/codeflinger/mips64_disassem.c b/libpixelflinger/codeflinger/mips64_disassem.c
index 1856e5c..8528299 100644
--- a/libpixelflinger/codeflinger/mips64_disassem.c
+++ b/libpixelflinger/codeflinger/mips64_disassem.c
@@ -45,6 +45,8 @@
 
 #include "mips_opcode.h"
 
+#define __unused __attribute__((__unused__))
+
 static char *sprintf_buffer;
 static int sprintf_buf_len;
 
@@ -114,7 +116,7 @@
     "t8",   "t9",   "k0",   "k1",   "gp",   "sp",   "s8",   "ra"
 };
 
-static char ** reg_name =  &mips_reg_name[0];
+static char * const * reg_name =  &mips_reg_name[0];
 
 static const char * const c0_opname[64] = {
     "c0op00","tlbr",  "tlbwi", "c0op03","c0op04","c0op05","tlbwr", "c0op07",
@@ -147,7 +149,7 @@
  * 'loc' may in fact contain a breakpoint instruction.
  */
 static db_addr_t
-db_disasm_insn(int insn, db_addr_t loc, bool altfmt)
+db_disasm_insn(int insn, db_addr_t loc, bool altfmt __unused)
 {
     bool bdslot = false;
     InstFmt i;
diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c
index 83a9740..1fe6806 100644
--- a/libpixelflinger/codeflinger/mips_disassem.c
+++ b/libpixelflinger/codeflinger/mips_disassem.c
@@ -57,6 +57,7 @@
 // #include <ddb/db_extern.h>
 // #include <ddb/db_sym.h>
 
+#define __unused __attribute__((__unused__))
 
 static char *sprintf_buffer;
 static int sprintf_buf_len;
@@ -183,7 +184,7 @@
  * 'loc' may in fact contain a breakpoint instruction.
  */
 static db_addr_t
-db_disasm_insn(int insn, db_addr_t loc, bool altfmt)
+db_disasm_insn(int insn, db_addr_t loc, bool altfmt __unused)
 {
     bool bdslot = false;
     InstFmt i;
diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp
index 4c357af..e6997bd 100644
--- a/libpixelflinger/codeflinger/texturing.cpp
+++ b/libpixelflinger/codeflinger/texturing.cpp
@@ -41,7 +41,6 @@
 void GGLAssembler::init_iterated_color(fragment_parts_t& parts, const reg_t& x)
 {
     context_t const* c = mBuilderContext.c;
-    const needs_t& needs = mBuilderContext.needs;
 
     if (mSmooth) {
         // NOTE: we could take this case in the mDithering + !mSmooth case,
@@ -324,9 +323,7 @@
         tex_coord_t* coords,
         const reg_t& x, const reg_t& y)
 {
-    context_t const* c = mBuilderContext.c;
     const needs_t& needs = mBuilderContext.needs;
-    int Rctx = mBuilderContext.Rctx;
     int Rx = x.reg;
     int Ry = y.reg;
 
@@ -402,10 +399,6 @@
 void GGLAssembler::build_textures(  fragment_parts_t& parts,
                                     Scratch& regs)
 {
-    context_t const* c = mBuilderContext.c;
-    const needs_t& needs = mBuilderContext.needs;
-    int Rctx = mBuilderContext.Rctx;
-
     // We don't have a way to spill registers automatically
     // spill depth and AA regs, when we know we may have to.
     // build the spill list...
@@ -434,7 +427,6 @@
 
     Spill spill(registerFile(), *this, spill_list);
 
-    const bool multiTexture = mTextureMachine.activeUnits > 1;
     for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) {
         const texture_unit_t& tmu = mTextureMachine.tmu[i];
         if (tmu.format_idx == 0)
@@ -442,7 +434,7 @@
 
         pointer_t& txPtr = parts.coords[i].ptr;
         pixel_t& texel = parts.texel[i];
-            
+
         // repeat...
         if ((tmu.swrap == GGL_NEEDS_WRAP_11) &&
             (tmu.twrap == GGL_NEEDS_WRAP_11))
@@ -656,7 +648,6 @@
 void GGLAssembler::build_iterate_texture_coordinates(
     const fragment_parts_t& parts)
 {
-    const bool multiTexture = mTextureMachine.activeUnits > 1;
     for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) {
         const texture_unit_t& tmu = mTextureMachine.tmu[i];
         if (tmu.format_idx == 0)
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
index 17b85dd..51e9e26 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
@@ -497,7 +497,6 @@
 {
 
     GGLfixed result;
-    int rshift;
 
     asm("smull  %x[result], %w[x], %w[y]                     \n"
         "lsr    %x[result], %x[result], %x[shift]            \n"
diff --git a/libpixelflinger/raster.cpp b/libpixelflinger/raster.cpp
index 26d8e45..e95c2c8 100644
--- a/libpixelflinger/raster.cpp
+++ b/libpixelflinger/raster.cpp
@@ -153,7 +153,6 @@
      GGLint h = where[3];
 
     // exclsively enable this tmu
-    const GGLSurface& cbSurface = c->state.buffers.color.s;
     c->procs.activeTexture(c, tmu);
     c->procs.disable(c, GGL_W_LERP);
 
diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp
index c6cf5bf..4cc23c7 100644
--- a/libpixelflinger/scanline.cpp
+++ b/libpixelflinger/scanline.cpp
@@ -2144,7 +2144,6 @@
     const int32_t u = (c->state.texture[0].shade.is0>>16) + x;
     const int32_t v = (c->state.texture[0].shade.it0>>16) + y;
     uint32_t *src = reinterpret_cast<uint32_t*>(tex->data)+(u+(tex->stride*v));
-    int sR, sG, sB;
     uint32_t s, d;
 
     if (ct==1 || uintptr_t(dst)&2) {
diff --git a/libpixelflinger/tests/arch-arm64/assembler/Android.mk b/libpixelflinger/tests/arch-arm64/assembler/Android.mk
index bd0f24b..db5dc4d 100644
--- a/libpixelflinger/tests/arch-arm64/assembler/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/assembler/Android.mk
@@ -14,6 +14,8 @@
 
 LOCAL_MODULE:= test-pixelflinger-arm64-assembler-test
 
+LOCAL_CFLAGS := -Wall -Werror
+
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_MULTILIB := 64
diff --git a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
index 3368eb0..3096232 100644
--- a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
@@ -11,6 +11,8 @@
 
 LOCAL_MODULE:= test-pixelflinger-arm64-col32cb16blend
 
+LOCAL_CFLAGS := -Wall -Werror
+
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_MULTILIB := 64
diff --git a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk b/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
index d8f7e69..78f12af 100644
--- a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
@@ -9,6 +9,8 @@
 
 LOCAL_MODULE:= test-pixelflinger-arm64-disassembler-test
 
+LOCAL_CFLAGS := -Wall -Werror
+
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_MULTILIB := 64
diff --git a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
index 8e5ec5e..664347f 100644
--- a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
@@ -11,6 +11,8 @@
 
 LOCAL_MODULE:= test-pixelflinger-arm64-t32cb16blend
 
+LOCAL_CFLAGS := -Wall -Werror
+
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_MULTILIB := 64
diff --git a/libpixelflinger/tests/codegen/Android.mk b/libpixelflinger/tests/codegen/Android.mk
index 2f9ca2f..72d71ef 100644
--- a/libpixelflinger/tests/codegen/Android.mk
+++ b/libpixelflinger/tests/codegen/Android.mk
@@ -13,6 +13,8 @@
 
 LOCAL_MODULE:= test-opengl-codegen
 
+LOCAL_CFLAGS:= -Wall -Werror
+
 LOCAL_MODULE_TAGS := tests
 
 include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp
index efa6d87..dce4ed7 100644
--- a/libpixelflinger/tests/codegen/codegen.cpp
+++ b/libpixelflinger/tests/codegen/codegen.cpp
@@ -40,9 +40,9 @@
     const AssemblyKey<needs_t>& key() const { return mKey; }
 };
 
+#if ANDROID_ARM_CODEGEN
 static void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1)
 {
-#if ANDROID_ARM_CODEGEN
     GGLContext* c;
     gglInit(&c);
     needs_t needs;
@@ -73,10 +73,12 @@
         printf("error %08x (%s)\n", err, strerror(-err));
     }
     gglUninit(c);
-#else
-    printf("This test runs only on ARM, Arm64 or MIPS\n");
-#endif
 }
+#else
+static void ggl_test_codegen(uint32_t, uint32_t, uint32_t, uint32_t) {
+    printf("This test runs only on ARM, Arm64 or MIPS\n");
+}
+#endif
 
 int main(int argc, char** argv)
 {
diff --git a/libpixelflinger/tests/gglmul/Android.mk b/libpixelflinger/tests/gglmul/Android.mk
index 75bd39e..67f358f 100644
--- a/libpixelflinger/tests/gglmul/Android.mk
+++ b/libpixelflinger/tests/gglmul/Android.mk
@@ -11,6 +11,8 @@
 
 LOCAL_MODULE:= test-pixelflinger-gglmul
 
+LOCAL_CFLAGS:= -Wall -Werror
+
 LOCAL_MODULE_TAGS := tests
 
 include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/trap.cpp b/libpixelflinger/trap.cpp
index 234bfdd..06ad237 100644
--- a/libpixelflinger/trap.cpp
+++ b/libpixelflinger/trap.cpp
@@ -349,7 +349,6 @@
 
 static void linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord width)
 {
-    GGL_CONTEXT(c, con);
     GGLcoord v[4][2];
     v[0][0] = v0[0];    v[0][1] = v0[1];
     v[1][0] = v1[0];    v[1][1] = v1[1];
@@ -377,7 +376,6 @@
 
 static void aa_linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord width)
 {
-    GGL_CONTEXT(c, con);
     GGLcoord v[4][2];
     v[0][0] = v0[0];    v[0][1] = v0[1];
     v[1][0] = v1[0];    v[1][1] = v1[1];
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index b568ee5..1cfabd5 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -14,14 +14,14 @@
 // limitations under the License.
 //
 
-libprocinfo_cppflags = [
-    "-Wall",
-    "-Wextra",
-    "-Werror",
-]
+cc_defaults {
+    name: "libprocinfo_defaults",
+    cflags: ["-Wall", "-Werror", "-Wextra"],
+}
 
 cc_library {
     name: "libprocinfo",
+    defaults: ["libprocinfo_defaults"],
     vendor_available: true,
     vndk: {
         enabled: true,
@@ -30,7 +30,6 @@
     srcs: [
         "process.cpp",
     ],
-    cppflags: libprocinfo_cppflags,
 
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
@@ -52,6 +51,7 @@
 // ------------------------------------------------------------------------------
 cc_test {
     name: "libprocinfo_test",
+    defaults: ["libprocinfo_defaults"],
     host_supported: true,
     srcs: [
         "process_test.cpp",
@@ -65,7 +65,6 @@
         },
     },
 
-    cppflags: libprocinfo_cppflags,
     shared_libs: ["libbase", "libprocinfo"],
 
     compile_multilib: "both",
diff --git a/libqtaguid/Android.bp b/libqtaguid/Android.bp
new file mode 100644
index 0000000..de632ca
--- /dev/null
+++ b/libqtaguid/Android.bp
@@ -0,0 +1,56 @@
+//
+// 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.
+//
+
+cc_library_headers {
+    name: "libqtaguid_headers",
+    vendor_available: false,
+    host_supported: false,
+    export_include_dirs: ["include"],
+    target: {
+        linux_bionic: {
+            enabled: true,
+        },
+    },
+}
+
+cc_library {
+    name: "libqtaguid",
+    vendor_available: false,
+    host_supported: false,
+    target: {
+        android: {
+            srcs: [
+                "qtaguid.c",
+            ],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
+        },
+    },
+
+    shared_libs: ["liblog"],
+    header_libs: [
+        "libqtaguid_headers",
+    ],
+    export_header_lib_headers: ["libqtaguid_headers"],
+    local_include_dirs: ["include"],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+    ],
+}
diff --git a/libqtaguid/include/qtaguid/qtaguid.h b/libqtaguid/include/qtaguid/qtaguid.h
new file mode 100644
index 0000000..72285e5
--- /dev/null
+++ b/libqtaguid/include/qtaguid/qtaguid.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 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 __LEGACY_QTAGUID_H
+#define __LEGACY_QTAGUID_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Set tags (and owning UIDs) for network sockets. The socket must be untagged
+ * by calling qtaguid_untagSocket() before closing it, otherwise the qtaguid
+ * module will keep a reference to it even after close.
+ */
+extern int legacy_tagSocket(int sockfd, int tag, uid_t uid);
+
+/*
+ * Untag a network socket before closing.
+ */
+extern int legacy_untagSocket(int sockfd);
+
+/*
+ * For the given uid, switch counter sets.
+ * The kernel only keeps a limited number of sets.
+ * 2 for now.
+ */
+extern int legacy_setCounterSet(int counterSetNum, uid_t uid);
+
+/*
+ * Delete all tag info that relates to the given tag an uid.
+ * If the tag is 0, then ALL info about the uid is freeded.
+ * The delete data also affects active tagged socketd, which are
+ * then untagged.
+ * The calling process can only operate on its own tags.
+ * Unless it is part of the happy AID_NET_BW_ACCT group.
+ * In which case it can clobber everything.
+ */
+extern int legacy_deleteTagData(int tag, uid_t uid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LEGACY_QTAGUID_H */
diff --git a/libcutils/qtaguid.c b/libqtaguid/qtaguid.c
similarity index 72%
rename from libcutils/qtaguid.c
rename to libqtaguid/qtaguid.c
index 22b8325..cd38bad 100644
--- a/libcutils/qtaguid.c
+++ b/libqtaguid/qtaguid.c
@@ -27,12 +27,10 @@
 #include <unistd.h>
 
 #include <log/log.h>
-#include <cutils/qtaguid.h>
+#include <qtaguid/qtaguid.h>
 
 static const char* CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl";
 static const int CTRL_MAX_INPUT_LEN = 128;
-static const char *GLOBAL_PACIFIER_PARAM = "/sys/module/xt_qtaguid/parameters/passive";
-static const char *TAG_PACIFIER_PARAM = "/sys/module/xt_qtaguid/parameters/tag_tracking_passive";
 
 /*
  * One per proccess.
@@ -46,7 +44,7 @@
 pthread_once_t resTrackInitDone = PTHREAD_ONCE_INIT;
 
 /* Only call once per process. */
-void qtaguid_resTrack(void) {
+void legacy_resTrack(void) {
     resTrackFd = TEMP_FAILURE_RETRY(open("/dev/xt_qtaguid", O_RDONLY | O_CLOEXEC));
 }
 
@@ -55,7 +53,7 @@
  *   0 on success.
  *   -errno on failure.
  */
-static int write_ctrl(const char *cmd) {
+static int write_ctrl(const char* cmd) {
     int fd, res, savedErrno;
 
     ALOGV("write_ctrl(%s)", cmd);
@@ -79,28 +77,12 @@
     return -savedErrno;
 }
 
-static int write_param(const char *param_path, const char *value) {
-    int param_fd;
-    int res;
-
-    param_fd = TEMP_FAILURE_RETRY(open(param_path, O_WRONLY | O_CLOEXEC));
-    if (param_fd < 0) {
-        return -errno;
-    }
-    res = TEMP_FAILURE_RETRY(write(param_fd, value, strlen(value)));
-    if (res < 0) {
-        return -errno;
-    }
-    close(param_fd);
-    return 0;
-}
-
-int qtaguid_tagSocket(int sockfd, int tag, uid_t uid) {
+int legacy_tagSocket(int sockfd, int tag, uid_t uid) {
     char lineBuf[CTRL_MAX_INPUT_LEN];
     int res;
     uint64_t kTag = ((uint64_t)tag << 32);
 
-    pthread_once(&resTrackInitDone, qtaguid_resTrack);
+    pthread_once(&resTrackInitDone, legacy_resTrack);
 
     snprintf(lineBuf, sizeof(lineBuf), "t %d %" PRIu64 " %d", sockfd, kTag, uid);
 
@@ -108,14 +90,14 @@
 
     res = write_ctrl(lineBuf);
     if (res < 0) {
-        ALOGI("Tagging socket %d with tag %" PRIx64 "(%d) for uid %d failed errno=%d",
-             sockfd, kTag, tag, uid, res);
+        ALOGI("Tagging socket %d with tag %" PRIx64 "(%d) for uid %d failed errno=%d", sockfd, kTag,
+              tag, uid, res);
     }
 
     return res;
 }
 
-int qtaguid_untagSocket(int sockfd) {
+int legacy_untagSocket(int sockfd) {
     char lineBuf[CTRL_MAX_INPUT_LEN];
     int res;
 
@@ -130,7 +112,7 @@
     return res;
 }
 
-int qtaguid_setCounterSet(int counterSetNum, uid_t uid) {
+int legacy_setCounterSet(int counterSetNum, uid_t uid) {
     char lineBuf[CTRL_MAX_INPUT_LEN];
     int res;
 
@@ -141,34 +123,21 @@
     return res;
 }
 
-int qtaguid_deleteTagData(int tag, uid_t uid) {
+int legacy_deleteTagData(int tag, uid_t uid) {
     char lineBuf[CTRL_MAX_INPUT_LEN];
     int cnt = 0, res = 0;
     uint64_t kTag = (uint64_t)tag << 32;
 
     ALOGV("Deleting tag data with tag %" PRIx64 "{%d,0} for uid %d", kTag, tag, uid);
 
-    pthread_once(&resTrackInitDone, qtaguid_resTrack);
+    pthread_once(&resTrackInitDone, legacy_resTrack);
 
     snprintf(lineBuf, sizeof(lineBuf), "d %" PRIu64 " %d", kTag, uid);
     res = write_ctrl(lineBuf);
     if (res < 0) {
         ALOGI("Deleting tag data with tag %" PRIx64 "/%d for uid %d failed with cnt=%d errno=%d",
-             kTag, tag, uid, cnt, errno);
+              kTag, tag, uid, cnt, errno);
     }
 
     return res;
 }
-
-int qtaguid_setPacifier(int on) {
-    const char *value;
-
-    value = on ? "Y" : "N";
-    if (write_param(GLOBAL_PACIFIER_PARAM, value) < 0) {
-        return -errno;
-    }
-    if (write_param(TAG_PACIFIER_PARAM, value) < 0) {
-        return -errno;
-    }
-    return 0;
-}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index f40086e..40364fe 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -48,8 +48,7 @@
     srcs: [
         "ArmExidx.cpp",
         "DwarfCfa.cpp",
-        "DwarfDebugFrame.cpp",
-        "DwarfEhFrame.cpp",
+        "DwarfEhFrameWithHdr.cpp",
         "DwarfMemory.cpp",
         "DwarfOp.cpp",
         "DwarfSection.cpp",
@@ -65,6 +64,10 @@
         "Symbols.cpp",
     ],
 
+    cflags: [
+        "-Wexit-time-destructors",
+    ],
+
     target: {
         // Always disable optimizations for host to make it easier to debug.
         host: {
@@ -102,6 +105,7 @@
         "tests/DwarfCfaTest.cpp",
         "tests/DwarfDebugFrameTest.cpp",
         "tests/DwarfEhFrameTest.cpp",
+        "tests/DwarfEhFrameWithHdrTest.cpp",
         "tests/DwarfMemoryTest.cpp",
         "tests/DwarfOpLogTest.cpp",
         "tests/DwarfOpTest.cpp",
@@ -122,6 +126,7 @@
         "tests/MemoryRangeTest.cpp",
         "tests/MemoryRemoteTest.cpp",
         "tests/MemoryTest.cpp",
+        "tests/RegsIterateTest.cpp",
         "tests/RegsStepIfSignalHandlerTest.cpp",
         "tests/RegsTest.cpp",
         "tests/SymbolsTest.cpp",
diff --git a/libunwindstack/DwarfDebugFrame.cpp b/libunwindstack/DwarfDebugFrame.cpp
deleted file mode 100644
index 5707596..0000000
--- a/libunwindstack/DwarfDebugFrame.cpp
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * 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
index 6a6178e..635cefd 100644
--- a/libunwindstack/DwarfDebugFrame.h
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -28,51 +28,21 @@
 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) {}
+  DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {
+    this->cie32_value_ = static_cast<uint32_t>(-1);
+    this->cie64_value_ = static_cast<uint64_t>(-1);
+  }
   virtual ~DwarfDebugFrame() = default;
 
-  bool Init(uint64_t offset, uint64_t size) override;
+  uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
+    return this->entries_offset_ + pointer;
+  }
 
-  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 GetCieOffsetFromFde64(uint64_t pointer) override {
+    return this->entries_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
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
index 4207b42..561d23a 100644
--- a/libunwindstack/DwarfEhFrame.h
+++ b/libunwindstack/DwarfEhFrame.h
@@ -20,74 +20,30 @@
 #include <stdint.h>
 
 #include <unwindstack/DwarfSection.h>
+#include <unwindstack/Memory.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;
+    return this->memory_.cur_offset() - pointer - 4;
   }
 
   uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
-    return memory_.cur_offset() - pointer - 8;
+    return this->memory_.cur_offset() - pointer - 8;
   }
 
   uint64_t AdjustPcFromFde(uint64_t pc) override {
     // The eh_frame uses relative pcs.
-    return pc + memory_.cur_offset();
+    return pc + this->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
+#endif  // _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
diff --git a/libunwindstack/DwarfEhFrame.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
similarity index 85%
rename from libunwindstack/DwarfEhFrame.cpp
rename to libunwindstack/DwarfEhFrameWithHdr.cpp
index db8f558..0337dba 100644
--- a/libunwindstack/DwarfEhFrame.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -20,13 +20,13 @@
 #include <unwindstack/Memory.h>
 
 #include "Check.h"
-#include "DwarfEhFrame.h"
+#include "DwarfEhFrameWithHdr.h"
 #include "DwarfError.h"
 
 namespace unwindstack {
 
 template <typename AddressType>
-bool DwarfEhFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
+bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size) {
   uint8_t data[4];
 
   memory_.clear_func_offset();
@@ -73,7 +73,7 @@
 }
 
 template <typename AddressType>
-const DwarfFde* DwarfEhFrame<AddressType>::GetFdeFromIndex(size_t index) {
+const DwarfFde* DwarfEhFrameWithHdr<AddressType>::GetFdeFromIndex(size_t index) {
   const FdeInfo* info = GetFdeInfoFromIndex(index);
   if (info == nullptr) {
     return nullptr;
@@ -82,8 +82,8 @@
 }
 
 template <typename AddressType>
-const typename DwarfEhFrame<AddressType>::FdeInfo* DwarfEhFrame<AddressType>::GetFdeInfoFromIndex(
-    size_t index) {
+const typename DwarfEhFrameWithHdr<AddressType>::FdeInfo*
+DwarfEhFrameWithHdr<AddressType>::GetFdeInfoFromIndex(size_t index) {
   auto entry = fde_info_.find(index);
   if (entry != fde_info_.end()) {
     return &fde_info_[index];
@@ -105,8 +105,8 @@
 }
 
 template <typename AddressType>
-bool DwarfEhFrame<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
-                                                   uint64_t total_entries) {
+bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
+                                                          uint64_t total_entries) {
   CHECK(fde_count_ > 0);
   CHECK(total_entries <= fde_count_);
 
@@ -115,6 +115,9 @@
   while (first < last) {
     size_t current = (first + last) / 2;
     const FdeInfo* info = GetFdeInfoFromIndex(current);
+    if (info == nullptr) {
+      return false;
+    }
     if (pc == info->pc) {
       *fde_offset = info->offset;
       return true;
@@ -127,6 +130,9 @@
   }
   if (last != 0) {
     const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
+    if (info == nullptr) {
+      return false;
+    }
     *fde_offset = info->offset;
     return true;
   }
@@ -134,7 +140,7 @@
 }
 
 template <typename AddressType>
-bool DwarfEhFrame<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
+bool DwarfEhFrameWithHdr<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
@@ -196,7 +202,7 @@
 }
 
 template <typename AddressType>
-bool DwarfEhFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
   if (fde_count_ == 0) {
     return false;
   }
@@ -210,8 +216,8 @@
   }
 }
 
-// Explicitly instantiate DwarfEhFrame.
-template class DwarfEhFrame<uint32_t>;
-template class DwarfEhFrame<uint64_t>;
+// Explicitly instantiate DwarfEhFrameWithHdr
+template class DwarfEhFrameWithHdr<uint32_t>;
+template class DwarfEhFrameWithHdr<uint64_t>;
 
 }  // namespace unwindstack
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
new file mode 100644
index 0000000..3571166
--- /dev/null
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -0,0 +1,78 @@
+/*
+ * 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_WITH_HDR_H
+#define _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
+
+#include <stdint.h>
+
+#include <unordered_map>
+
+#include "DwarfEhFrame.h"
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+template <typename AddressType>
+class DwarfEhFrameWithHdr : public DwarfEhFrame<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>::entries_offset_;
+  using DwarfSectionImpl<AddressType>::entries_end_;
+  using DwarfSectionImpl<AddressType>::last_error_;
+
+  struct FdeInfo {
+    AddressType pc;
+    uint64_t offset;
+  };
+
+  DwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrame<AddressType>(memory) {}
+  virtual ~DwarfEhFrameWithHdr() = 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;
+
+  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_data_offset_;
+  uint64_t cur_entries_offset_ = 0;
+
+  std::unordered_map<uint64_t, FdeInfo> fde_info_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 8b30b76..805dcd3 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -29,6 +29,9 @@
 #include "DwarfError.h"
 #include "DwarfOp.h"
 
+#include "DwarfDebugFrame.h"
+#include "DwarfEhFrame.h"
+
 namespace unwindstack {
 
 DwarfSection::DwarfSection(Memory* memory) : memory_(memory), last_error_(DWARF_ERROR_NONE) {}
@@ -39,6 +42,10 @@
     return nullptr;
   }
   const DwarfFde* fde = GetFdeFromOffset(fde_offset);
+  if (fde == nullptr) {
+    return nullptr;
+  }
+
   // Guaranteed pc >= pc_start, need to check pc in the fde range.
   if (pc < fde->pc_end) {
     return fde;
@@ -107,7 +114,6 @@
     return false;
   }
 
-  AddressType prev_pc = regs->pc();
   AddressType prev_cfa = regs->sp();
 
   AddressType cfa;
@@ -225,14 +231,16 @@
   // Find the return address location.
   if (return_address_undefined) {
     cur_regs->set_pc(0);
-    *finished = true;
   } else {
     cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
-    *finished = false;
   }
+
+  // If the pc was set to zero, consider this the final frame.
+  *finished = (cur_regs->pc() == 0) ? true : false;
+
   cur_regs->set_sp(cfa);
-  // Return false if the unwind is not finished or the cfa and pc didn't change.
-  return *finished || prev_cfa != cfa || prev_pc != cur_regs->pc();
+
+  return true;
 }
 
 template <typename AddressType>
@@ -277,7 +285,7 @@
       last_error_ = DWARF_ERROR_MEMORY_INVALID;
       return false;
     }
-    if (!IsCie64(cie_id)) {
+    if (cie_id != cie64_value_) {
       // This is not a Cie, something has gone horribly wrong.
       last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
@@ -292,7 +300,7 @@
       last_error_ = DWARF_ERROR_MEMORY_INVALID;
       return false;
     }
-    if (!IsCie32(cie_id)) {
+    if (cie_id != cie32_value_) {
       // This is not a Cie, something has gone horribly wrong.
       last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
@@ -435,7 +443,7 @@
       last_error_ = DWARF_ERROR_MEMORY_INVALID;
       return false;
     }
-    if (IsCie64(value64)) {
+    if (value64 == cie64_value_) {
       // This is a Cie, this means something has gone wrong.
       last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
@@ -453,7 +461,7 @@
       last_error_ = DWARF_ERROR_MEMORY_INVALID;
       return false;
     }
-    if (IsCie32(value32)) {
+    if (value32 == cie32_value_) {
       // This is a Cie, this means something has gone wrong.
       last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
@@ -551,8 +559,301 @@
   return true;
 }
 
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size) {
+  entries_offset_ = offset;
+  entries_end_ = offset + size;
+
+  memory_.clear_func_offset();
+  memory_.clear_text_offset();
+  memory_.set_data_offset(offset);
+  memory_.set_cur_offset(offset);
+  memory_.set_pc_offset(offset);
+
+  return CreateSortedFdeList();
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) {
+  uint8_t version;
+  if (!memory_.ReadBytes(&version, 1)) {
+    last_error_ = 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 DwarfSectionImpl<AddressType>::AddFdeInfo(uint64_t entry_offset, uint8_t segment_size,
+                                               uint8_t encoding) {
+  if (segment_size != 0) {
+    memory_.set_cur_offset(memory_.cur_offset() + 1);
+  }
+
+  uint64_t start;
+  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  start = AdjustPcFromFde(start);
+
+  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 DwarfSectionImpl<AddressType>::CreateSortedFdeList() {
+  memory_.set_cur_offset(entries_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() < entries_end_) {
+    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 == cie64_value_) {
+        // Cie 64 bit
+        address_encoding = DW_EH_PE_sdata8;
+        if (!GetCieInfo(&segment_size, &address_encoding)) {
+          return false;
+        }
+        cie_offset = cur_entry_offset;
+      } else {
+        uint64_t last_cie_offset = GetCieOffsetFromFde64(value64);
+        if (last_cie_offset != cie_offset) {
+          // This means that this Fde is not following the Cie.
+          last_error_ = 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 == cie32_value_) {
+        // Cie 32 bit
+        address_encoding = DW_EH_PE_sdata4;
+        if (!GetCieInfo(&segment_size, &address_encoding)) {
+          return false;
+        }
+        cie_offset = cur_entry_offset;
+      } else {
+        uint64_t last_cie_offset = GetCieOffsetFromFde32(value32);
+        if (last_cie_offset != cie_offset) {
+          // This means that this Fde is not following the Cie.
+          last_error_ = 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 DwarfSectionImpl<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+  if (fde_count_ == 0) {
+    return false;
+  }
+
+  size_t first = 0;
+  size_t last = fde_count_;
+  while (first < last) {
+    size_t current = (first + last) / 2;
+    const FdeInfo* info = &fdes_[current];
+    if (pc >= info->start && pc <= info->end) {
+      *fde_offset = info->offset;
+      return true;
+    }
+
+    if (pc < info->start) {
+      last = current;
+    } else {
+      first = current + 1;
+    }
+  }
+  return false;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromIndex(size_t index) {
+  if (index >= fdes_.size()) {
+    return nullptr;
+  }
+  return this->GetFdeFromOffset(fdes_[index].offset);
+}
+
 // Explicitly instantiate DwarfSectionImpl
 template class DwarfSectionImpl<uint32_t>;
 template class DwarfSectionImpl<uint64_t>;
 
+// Explicitly instantiate DwarfDebugFrame
+template class DwarfDebugFrame<uint32_t>;
+template class DwarfDebugFrame<uint64_t>;
+
+// Explicitly instantiate DwarfEhFrame
+template class DwarfEhFrame<uint32_t>;
+template class DwarfEhFrame<uint64_t>;
+
 }  // namespace unwindstack
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index dc6591d..edf7ac2 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -35,7 +35,8 @@
 
 namespace unwindstack {
 
-bool Elf::Init() {
+bool Elf::Init(bool init_gnu_debugdata) {
+  load_bias_ = 0;
   if (!memory_) {
     return false;
   }
@@ -45,9 +46,14 @@
     return false;
   }
 
-  valid_ = interface_->Init();
+  valid_ = interface_->Init(&load_bias_);
   if (valid_) {
     interface_->InitHeaders();
+    if (init_gnu_debugdata) {
+      InitGnuDebugdata();
+    } else {
+      gnu_debugdata_interface_.reset(nullptr);
+    }
   } else {
     interface_.reset(nullptr);
   }
@@ -67,7 +73,11 @@
   if (gnu == nullptr) {
     return;
   }
-  if (gnu->Init()) {
+
+  // Ignore the load_bias from the compressed section, the correct load bias
+  // is in the uncompressed data.
+  uint64_t load_bias;
+  if (gnu->Init(&load_bias)) {
     gnu->InitHeaders();
   } else {
     // Free all of the memory associated with the gnu_debugdata section.
@@ -81,38 +91,39 @@
 }
 
 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;
+  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)));
+  return valid_ && (interface_->GetFunctionName(addr, load_bias_, name, func_offset) ||
+                    (gnu_debugdata_interface_ && gnu_debugdata_interface_->GetFunctionName(
+                                                     addr, load_bias_, name, func_offset)));
 }
 
-bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished) {
+// The relative pc is always relative to the start of the map from which it comes.
+bool Elf::Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process_memory,
+               bool* finished) {
   if (!valid_) {
     return false;
   }
-  if (regs->StepIfSignalHandler(rel_pc, this, process_memory)) {
+
+  // The relative pc expectd by StepIfSignalHandler is relative to the start of the elf.
+  if (regs->StepIfSignalHandler(rel_pc + elf_offset, this, process_memory)) {
     *finished = false;
     return true;
   }
+
+  // Adjust the load bias to get the real relative pc.
+  if (rel_pc < load_bias_) {
+    return false;
+  }
+  rel_pc -= load_bias_;
+
   return interface_->Step(rel_pc, regs, process_memory, finished) ||
          (gnu_debugdata_interface_ &&
           gnu_debugdata_interface_->Step(rel_pc, regs, process_memory, finished));
 }
 
-uint64_t Elf::GetLoadBias() {
-  if (!valid_) return 0;
-  return interface_->load_bias();
-}
-
 bool Elf::IsValidElf(Memory* memory) {
   if (memory == nullptr) {
     return false;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 46a3f3f..d5d158f 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -32,6 +32,7 @@
 
 #include "DwarfDebugFrame.h"
 #include "DwarfEhFrame.h"
+#include "DwarfEhFrameWithHdr.h"
 #include "Symbols.h"
 
 namespace unwindstack {
@@ -98,7 +99,17 @@
 
 template <typename AddressType>
 void ElfInterface::InitHeadersWithTemplate() {
-  if (eh_frame_offset_ != 0) {
+  if (eh_frame_hdr_offset_ != 0) {
+    eh_frame_.reset(new DwarfEhFrameWithHdr<AddressType>(memory_));
+    if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_)) {
+      // Even if the eh_frame_offset_ is non-zero, do not bother
+      // trying to read that since something has gone wrong.
+      eh_frame_.reset(nullptr);
+      eh_frame_hdr_offset_ = 0;
+      eh_frame_hdr_size_ = static_cast<uint64_t>(-1);
+    }
+  } else if (eh_frame_offset_ != 0) {
+    // If there is a eh_frame section without a eh_frame_hdr section.
     eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
     if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_)) {
       eh_frame_.reset(nullptr);
@@ -118,13 +129,13 @@
 }
 
 template <typename EhdrType, typename PhdrType, typename ShdrType>
-bool ElfInterface::ReadAllHeaders() {
+bool ElfInterface::ReadAllHeaders(uint64_t* load_bias) {
   EhdrType ehdr;
   if (!memory_->Read(0, &ehdr, sizeof(ehdr))) {
     return false;
   }
 
-  if (!ReadProgramHeaders<EhdrType, PhdrType>(ehdr)) {
+  if (!ReadProgramHeaders<EhdrType, PhdrType>(ehdr, load_bias)) {
     return false;
   }
 
@@ -137,7 +148,7 @@
 }
 
 template <typename EhdrType, typename PhdrType>
-bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr) {
+bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) {
   uint64_t offset = ehdr.e_phoff;
   for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
     PhdrType phdr;
@@ -145,7 +156,7 @@
       return false;
     }
 
-    if (HandleType(offset, phdr.p_type)) {
+    if (HandleType(offset, phdr.p_type, *load_bias)) {
       continue;
     }
 
@@ -172,7 +183,7 @@
       pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
                                           static_cast<size_t>(phdr.p_memsz)};
       if (phdr.p_offset == 0) {
-        load_bias_ = phdr.p_vaddr;
+        *load_bias = phdr.p_vaddr;
       }
       break;
     }
@@ -181,11 +192,12 @@
       if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
         return false;
       }
-      eh_frame_offset_ = phdr.p_offset;
+      // This is really the pointer to the .eh_frame_hdr section.
+      eh_frame_hdr_offset_ = phdr.p_offset;
       if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
         return false;
       }
-      eh_frame_size_ = phdr.p_memsz;
+      eh_frame_hdr_size_ = phdr.p_memsz;
       break;
 
     case PT_DYNAMIC:
@@ -271,6 +283,12 @@
           } else if (name == ".gnu_debugdata") {
             offset_ptr = &gnu_debugdata_offset_;
             size_ptr = &gnu_debugdata_size_;
+          } else if (name == ".eh_frame") {
+            offset_ptr = &eh_frame_offset_;
+            size_ptr = &eh_frame_size_;
+          } else if (eh_frame_hdr_offset_ == 0 && name == ".eh_frame_hdr") {
+            offset_ptr = &eh_frame_hdr_offset_;
+            size_ptr = &eh_frame_hdr_size_;
           }
           if (offset_ptr != nullptr &&
               memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
@@ -334,14 +352,14 @@
 }
 
 template <typename SymType>
-bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name,
+bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, 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)) {
+    if (symbol->GetName<SymType>(addr, load_bias, memory_, name, func_offset)) {
       return true;
     }
   }
@@ -349,12 +367,6 @@
 }
 
 bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
-  // 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, finished)) {
@@ -389,11 +401,11 @@
 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>();
+template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(uint64_t*);
+template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(uint64_t*);
 
-template bool ElfInterface::ReadProgramHeaders<Elf32_Ehdr, Elf32_Phdr>(const Elf32_Ehdr&);
-template bool ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf64_Ehdr&);
+template bool ElfInterface::ReadProgramHeaders<Elf32_Ehdr, Elf32_Phdr>(const Elf32_Ehdr&, uint64_t*);
+template bool ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf64_Ehdr&, uint64_t*);
 
 template bool ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
 template bool ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
@@ -401,9 +413,9 @@
 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*,
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, uint64_t, std::string*,
                                                                    uint64_t*);
-template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, uint64_t, std::string*,
                                                                    uint64_t*);
 
 template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index 17364d0..170a5cd 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -31,12 +31,6 @@
     return false;
   }
 
-  // Need to subtract the load_bias from the pc.
-  if (pc < load_bias_) {
-    return false;
-  }
-  pc -= load_bias_;
-
   size_t first = 0;
   size_t last = total_entries_;
   while (first < last) {
@@ -81,7 +75,7 @@
 #define PT_ARM_EXIDX 0x70000001
 #endif
 
-bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) {
+bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) {
   if (type != PT_ARM_EXIDX) {
     return false;
   }
@@ -93,8 +87,7 @@
   if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
     return true;
   }
-  // The load_bias_ should always be set by this time.
-  start_offset_ = phdr.p_vaddr - load_bias_;
+  start_offset_ = phdr.p_vaddr - load_bias;
   total_entries_ = phdr.p_memsz / 8;
   return true;
 }
@@ -128,8 +121,10 @@
     }
     regs_arm->set_sp(arm.cfa());
     (*regs_arm)[ARM_REG_SP] = regs_arm->sp();
-    *finished = false;
     return_value = true;
+
+    // If the pc was set to zero, consider this the final frame.
+    *finished = (regs_arm->pc() == 0) ? true : false;
   }
 
   if (arm.status() == ARM_STATUS_NO_UNWIND) {
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index bfe7704..eeb2e17 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -68,7 +68,7 @@
 
   bool FindEntry(uint32_t pc, uint64_t* entry_offset);
 
-  bool HandleType(uint64_t offset, uint32_t type) override;
+  bool HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) override;
 
   bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
 
@@ -76,13 +76,9 @@
 
   uint64_t start_offset() { return start_offset_; }
 
-  void set_start_offset(uint64_t start_offset) { start_offset_ = start_offset; }
-
   size_t total_entries() { return total_entries_; }
 
-  void set_total_entries(size_t total_entries) { total_entries_ = total_entries; }
-
- private:
+ protected:
   uint64_t start_offset_ = 0;
   size_t total_entries_ = 0;
 
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 96f2cb4..140d05a 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -110,9 +110,8 @@
   }
 
   elf = new Elf(CreateMemory(process_memory));
-  if (elf->Init() && init_gnu_debugdata) {
-    elf->InitGnuDebugdata();
-  }
+  elf->Init(init_gnu_debugdata);
+
   // If the init fails, keep the elf around as an invalid object so we
   // don't try to reinit the object.
   return elf;
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index 69e6512..36b8e25 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -45,7 +45,7 @@
     return rel_pc;
   }
 
-  uint64_t load_bias = elf->interface()->load_bias();
+  uint64_t load_bias = elf->GetLoadBias();
   if (rel_pc < load_bias) {
     return rel_pc;
   }
@@ -80,6 +80,25 @@
   return true;
 }
 
+void RegsArm::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("r0", regs_[ARM_REG_R0]);
+  fn("r1", regs_[ARM_REG_R1]);
+  fn("r2", regs_[ARM_REG_R2]);
+  fn("r3", regs_[ARM_REG_R3]);
+  fn("r4", regs_[ARM_REG_R4]);
+  fn("r5", regs_[ARM_REG_R5]);
+  fn("r6", regs_[ARM_REG_R6]);
+  fn("r7", regs_[ARM_REG_R7]);
+  fn("r8", regs_[ARM_REG_R8]);
+  fn("r9", regs_[ARM_REG_R9]);
+  fn("r10", regs_[ARM_REG_R10]);
+  fn("r11", regs_[ARM_REG_R11]);
+  fn("ip", regs_[ARM_REG_R12]);
+  fn("sp", regs_[ARM_REG_SP]);
+  fn("lr", regs_[ARM_REG_LR]);
+  fn("pc", regs_[ARM_REG_PC]);
+}
+
 RegsArm64::RegsArm64()
     : RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
 
@@ -112,6 +131,42 @@
   return true;
 }
 
+void RegsArm64::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("x0", regs_[ARM64_REG_R0]);
+  fn("x1", regs_[ARM64_REG_R1]);
+  fn("x2", regs_[ARM64_REG_R2]);
+  fn("x3", regs_[ARM64_REG_R3]);
+  fn("x4", regs_[ARM64_REG_R4]);
+  fn("x5", regs_[ARM64_REG_R5]);
+  fn("x6", regs_[ARM64_REG_R6]);
+  fn("x7", regs_[ARM64_REG_R7]);
+  fn("x8", regs_[ARM64_REG_R8]);
+  fn("x9", regs_[ARM64_REG_R9]);
+  fn("x10", regs_[ARM64_REG_R10]);
+  fn("x11", regs_[ARM64_REG_R11]);
+  fn("x12", regs_[ARM64_REG_R12]);
+  fn("x13", regs_[ARM64_REG_R13]);
+  fn("x14", regs_[ARM64_REG_R14]);
+  fn("x15", regs_[ARM64_REG_R15]);
+  fn("x16", regs_[ARM64_REG_R16]);
+  fn("x17", regs_[ARM64_REG_R17]);
+  fn("x18", regs_[ARM64_REG_R18]);
+  fn("x19", regs_[ARM64_REG_R19]);
+  fn("x20", regs_[ARM64_REG_R20]);
+  fn("x21", regs_[ARM64_REG_R21]);
+  fn("x22", regs_[ARM64_REG_R22]);
+  fn("x23", regs_[ARM64_REG_R23]);
+  fn("x24", regs_[ARM64_REG_R24]);
+  fn("x25", regs_[ARM64_REG_R25]);
+  fn("x26", regs_[ARM64_REG_R26]);
+  fn("x27", regs_[ARM64_REG_R27]);
+  fn("x28", regs_[ARM64_REG_R28]);
+  fn("x29", regs_[ARM64_REG_R29]);
+  fn("sp", regs_[ARM64_REG_SP]);
+  fn("lr", regs_[ARM64_REG_LR]);
+  fn("pc", regs_[ARM64_REG_PC]);
+}
+
 RegsX86::RegsX86()
     : RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
 
@@ -146,6 +201,18 @@
   return true;
 }
 
+void RegsX86::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("eax", regs_[X86_REG_EAX]);
+  fn("ebx", regs_[X86_REG_EBX]);
+  fn("ecx", regs_[X86_REG_ECX]);
+  fn("edx", regs_[X86_REG_EDX]);
+  fn("ebp", regs_[X86_REG_EBP]);
+  fn("edi", regs_[X86_REG_EDI]);
+  fn("esi", regs_[X86_REG_ESI]);
+  fn("esp", regs_[X86_REG_ESP]);
+  fn("eip", regs_[X86_REG_EIP]);
+}
+
 RegsX86_64::RegsX86_64()
     : RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
 
@@ -181,6 +248,26 @@
   return true;
 }
 
+void RegsX86_64::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("rax", regs_[X86_64_REG_RAX]);
+  fn("rbx", regs_[X86_64_REG_RBX]);
+  fn("rcx", regs_[X86_64_REG_RCX]);
+  fn("rdx", regs_[X86_64_REG_RDX]);
+  fn("r8", regs_[X86_64_REG_R8]);
+  fn("r9", regs_[X86_64_REG_R9]);
+  fn("r10", regs_[X86_64_REG_R10]);
+  fn("r11", regs_[X86_64_REG_R11]);
+  fn("r12", regs_[X86_64_REG_R12]);
+  fn("r13", regs_[X86_64_REG_R13]);
+  fn("r14", regs_[X86_64_REG_R14]);
+  fn("r15", regs_[X86_64_REG_R15]);
+  fn("rdi", regs_[X86_64_REG_RDI]);
+  fn("rsi", regs_[X86_64_REG_RSI]);
+  fn("rbp", regs_[X86_64_REG_RBP]);
+  fn("rsp", regs_[X86_64_REG_RSP]);
+  fn("rip", regs_[X86_64_REG_RIP]);
+}
+
 static Regs* ReadArm(void* remote_data) {
   arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data);
 
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index e648927..2e46a11 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -22,6 +22,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <algorithm>
+
 #include <android-base/stringprintf.h>
 
 #include <unwindstack/Elf.h>
@@ -52,7 +54,7 @@
   }
 
   frame->map_name = map_info->name;
-  frame->map_offset = map_info->elf_offset;
+  frame->map_offset = map_info->offset;
   frame->map_start = map_info->start;
   frame->map_end = map_info->end;
   frame->map_flags = map_info->flags;
@@ -64,26 +66,46 @@
   }
 }
 
-void Unwinder::Unwind(std::set<std::string>* initial_map_names_to_skip) {
+static bool ShouldStop(const std::vector<std::string>* map_suffixes_to_ignore,
+                       std::string& map_name) {
+  if (map_suffixes_to_ignore == nullptr) {
+    return false;
+  }
+  auto pos = map_name.find_last_of('.');
+  if (pos == std::string::npos) {
+    return false;
+  }
+
+  return std::find(map_suffixes_to_ignore->begin(), map_suffixes_to_ignore->end(),
+                   map_name.substr(pos + 1)) != map_suffixes_to_ignore->end();
+}
+
+void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
+                      const std::vector<std::string>* map_suffixes_to_ignore) {
   frames_.clear();
 
   bool return_address_attempt = false;
   bool adjust_pc = false;
   for (; frames_.size() < max_frames_;) {
-    MapInfo* map_info = maps_->Find(regs_->pc());
+    uint64_t cur_pc = regs_->pc();
+    uint64_t cur_sp = regs_->sp();
 
+    MapInfo* map_info = maps_->Find(regs_->pc());
     uint64_t rel_pc;
     Elf* elf;
     if (map_info == nullptr) {
       rel_pc = regs_->pc();
     } else {
+      if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
+        break;
+      }
       elf = map_info->GetElf(process_memory_, true);
       rel_pc = elf->GetRelPc(regs_->pc(), map_info);
     }
 
     if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
-        initial_map_names_to_skip->find(basename(map_info->name.c_str())) ==
-            initial_map_names_to_skip->end()) {
+        std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
+                  basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
       FillInFrame(map_info, elf, rel_pc, adjust_pc);
       // Once a frame is added, stop skipping frames.
       initial_map_names_to_skip = nullptr;
@@ -111,14 +133,14 @@
           in_device_map = true;
         } else {
           bool finished;
-          stepped =
-              elf->Step(rel_pc + map_info->elf_offset, regs_, process_memory_.get(), &finished);
+          stepped = elf->Step(rel_pc, map_info->elf_offset, regs_, process_memory_.get(), &finished);
           if (stepped && finished) {
             break;
           }
         }
       }
     }
+
     if (!stepped) {
       if (return_address_attempt) {
         // Remove the speculative frame.
@@ -138,6 +160,11 @@
     } else {
       return_address_attempt = false;
     }
+
+    // If the pc and sp didn't change, then consider everything stopped.
+    if (cur_pc == regs_->pc() && cur_sp == regs_->sp()) {
+      break;
+    }
   }
 }
 
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index 1e843c3..10be6b4 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -90,10 +90,6 @@
 
   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;
@@ -106,6 +102,9 @@
   DwarfMemory memory_;
   DwarfError last_error_;
 
+  uint32_t cie32_value_ = 0;
+  uint64_t cie64_value_ = 0;
+
   uint64_t fde_count_ = 0;
   std::unordered_map<uint64_t, DwarfFde> fde_entries_;
   std::unordered_map<uint64_t, DwarfCie> cie_entries_;
@@ -115,9 +114,24 @@
 template <typename AddressType>
 class DwarfSectionImpl : public DwarfSection {
  public:
+  struct FdeInfo {
+    FdeInfo(uint64_t offset, uint64_t start, uint64_t length)
+        : offset(offset), start(start), end(start + length) {}
+
+    uint64_t offset;
+    AddressType start;
+    AddressType end;
+  };
+
   DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
   virtual ~DwarfSectionImpl() = default;
 
+  bool Init(uint64_t offset, uint64_t size) override;
+
+  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+
+  const DwarfFde* GetFdeFromIndex(size_t index) override;
+
   bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
             Regs* regs, bool* finished) override;
 
@@ -134,6 +148,16 @@
  protected:
   bool EvalExpression(const DwarfLocation& loc, uint8_t version, Memory* regular_memory,
                       AddressType* value);
+
+  bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding);
+
+  bool AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, uint8_t encoding);
+
+  bool CreateSortedFdeList();
+
+  std::vector<FdeInfo> fdes_;
+  uint64_t entries_offset_;
+  uint64_t entries_end_;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index f246beb..294d742 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -40,7 +40,7 @@
   Elf(Memory* memory) : memory_(memory) {}
   virtual ~Elf() = default;
 
-  bool Init();
+  bool Init(bool init_gnu_debugdata);
 
   void InitGnuDebugdata();
 
@@ -50,11 +50,12 @@
 
   uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
 
-  bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
+  bool Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process_memory,
+            bool* finished);
 
   ElfInterface* CreateInterfaceFromMemory(Memory* memory);
 
-  uint64_t GetLoadBias();
+  uint64_t GetLoadBias() { return load_bias_; }
 
   bool valid() { return valid_; }
 
@@ -74,6 +75,7 @@
 
  protected:
   bool valid_ = false;
+  uint64_t load_bias_ = 0;
   std::unique_ptr<ElfInterface> interface_;
   std::unique_ptr<Memory> memory_;
   uint32_t machine_type_;
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 4fe966f..86e51b3 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -51,13 +51,14 @@
   ElfInterface(Memory* memory) : memory_(memory) {}
   virtual ~ElfInterface();
 
-  virtual bool Init() = 0;
+  virtual bool Init(uint64_t* load_bias) = 0;
 
   virtual void InitHeaders() = 0;
 
   virtual bool GetSoname(std::string* name) = 0;
 
-  virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
+  virtual bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
+                               uint64_t* offset) = 0;
 
   virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
 
@@ -66,11 +67,11 @@
   Memory* memory() { return memory_; }
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads() { return pt_loads_; }
-  uint64_t load_bias() { return load_bias_; }
-  void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; }
 
   uint64_t dynamic_offset() { return dynamic_offset_; }
   uint64_t dynamic_size() { return dynamic_size_; }
+  uint64_t eh_frame_hdr_offset() { return eh_frame_hdr_offset_; }
+  uint64_t eh_frame_hdr_size() { return eh_frame_hdr_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_; }
@@ -86,10 +87,10 @@
   void InitHeadersWithTemplate();
 
   template <typename EhdrType, typename PhdrType, typename ShdrType>
-  bool ReadAllHeaders();
+  bool ReadAllHeaders(uint64_t* load_bias);
 
   template <typename EhdrType, typename PhdrType>
-  bool ReadProgramHeaders(const EhdrType& ehdr);
+  bool ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias);
 
   template <typename EhdrType, typename ShdrType>
   bool ReadSectionHeaders(const EhdrType& ehdr);
@@ -98,21 +99,24 @@
   bool GetSonameWithTemplate(std::string* soname);
 
   template <typename SymType>
-  bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
+  bool GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, std::string* name,
+                                   uint64_t* func_offset);
 
-  virtual bool HandleType(uint64_t, uint32_t) { return false; }
+  virtual bool HandleType(uint64_t, uint32_t, uint64_t) { return false; }
 
   template <typename EhdrType>
   static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
 
   Memory* memory_;
   std::unordered_map<uint64_t, LoadInfo> pt_loads_;
-  uint64_t load_bias_ = 0;
 
   // Stored elf data.
   uint64_t dynamic_offset_ = 0;
   uint64_t dynamic_size_ = 0;
 
+  uint64_t eh_frame_hdr_offset_ = 0;
+  uint64_t eh_frame_hdr_size_ = 0;
+
   uint64_t eh_frame_offset_ = 0;
   uint64_t eh_frame_size_ = 0;
 
@@ -136,8 +140,8 @@
   ElfInterface32(Memory* memory) : ElfInterface(memory) {}
   virtual ~ElfInterface32() = default;
 
-  bool Init() override {
-    return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>();
+  bool Init(uint64_t* load_bias) override {
+    return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(load_bias);
   }
 
   void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
@@ -146,8 +150,9 @@
     return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
   }
 
-  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
-    return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
+  bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
+                       uint64_t* func_offset) override {
+    return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, load_bias, name, func_offset);
   }
 
   static void GetMaxSize(Memory* memory, uint64_t* size) {
@@ -160,8 +165,8 @@
   ElfInterface64(Memory* memory) : ElfInterface(memory) {}
   virtual ~ElfInterface64() = default;
 
-  bool Init() override {
-    return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>();
+  bool Init(uint64_t* load_bias) override {
+    return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(load_bias);
   }
 
   void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
@@ -170,8 +175,9 @@
     return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
   }
 
-  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
-    return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
+  bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
+                       uint64_t* func_offset) override {
+    return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, load_bias, name, func_offset);
   }
 
   static void GetMaxSize(Memory* memory, uint64_t* size) {
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 9d3150b..6576e4c 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -19,6 +19,8 @@
 
 #include <stdint.h>
 
+#include <functional>
+#include <string>
 #include <vector>
 
 namespace unwindstack {
@@ -63,6 +65,8 @@
 
   virtual bool SetPcFromReturnAddress(Memory* process_memory) = 0;
 
+  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) = 0;
+
   uint16_t sp_reg() { return sp_reg_; }
   uint16_t total_regs() { return total_regs_; }
 
@@ -94,6 +98,12 @@
 
   void* RawData() override { return regs_.data(); }
 
+  virtual void IterateRegisters(std::function<void(const char*, uint64_t)> fn) override {
+    for (size_t i = 0; i < regs_.size(); ++i) {
+      fn(std::to_string(i).c_str(), regs_[i]);
+    }
+  }
+
  protected:
   AddressType pc_;
   AddressType sp_;
@@ -114,6 +124,8 @@
   bool SetPcFromReturnAddress(Memory* process_memory) override;
 
   bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
 };
 
 class RegsArm64 : public RegsImpl<uint64_t> {
@@ -130,6 +142,8 @@
   bool SetPcFromReturnAddress(Memory* process_memory) override;
 
   bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
 };
 
 class RegsX86 : public RegsImpl<uint32_t> {
@@ -148,6 +162,8 @@
   bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
 
   void SetFromUcontext(x86_ucontext_t* ucontext);
+
+  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
 };
 
 class RegsX86_64 : public RegsImpl<uint64_t> {
@@ -166,6 +182,8 @@
   bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
 
   void SetFromUcontext(x86_64_ucontext_t* ucontext);
+
+  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 71703b4..37a76b2 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -21,7 +21,6 @@
 #include <sys/types.h>
 
 #include <memory>
-#include <set>
 #include <string>
 #include <vector>
 
@@ -60,7 +59,8 @@
   }
   ~Unwinder() = default;
 
-  void Unwind(std::set<std::string>* initial_map_names_to_skip = nullptr);
+  void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
+              const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
 
   size_t NumFrames() { return frames_.size(); }
 
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index 90baabe..07204bc 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -35,8 +35,8 @@
   ~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 TestSetOffset(uint64_t offset) { this->entries_offset_ = offset; }
+  void TestSetEndOffset(uint64_t offset) { this->entries_end_ = offset; }
   void TestPushFdeInfo(const typename DwarfDebugFrame<TypeParam>::FdeInfo& info) {
     this->fdes_.push_back(info);
   }
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index 21114da..53ee719 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -34,28 +34,19 @@
   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;
+  void TestSetOffset(uint64_t offset) { this->entries_offset_ = offset; }
+  void TestSetEndOffset(uint64_t offset) { this->entries_end_ = offset; }
+  void TestPushFdeInfo(const typename DwarfEhFrame<TypeParam>::FdeInfo& info) {
+    this->fdes_.push_back(info);
   }
 
-  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_; }
+  uint8_t TestGetOffset() { return this->offset_; }
+  uint8_t TestGetEndOffset() { return this->end_offset_; }
+  void TestGetFdeInfo(size_t index, typename DwarfEhFrame<TypeParam>::FdeInfo* info) {
+    *info = this->fdes_[index];
+  }
 };
 
 template <typename TypeParam>
@@ -76,248 +67,304 @@
 
 // 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);
+TYPED_TEST_P(DwarfEhFrameTest, Init32) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0);
+  this->memory_.SetData8(0x5008, 1);
+  this->memory_.SetData8(0x5009, '\0');
 
-  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());
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0x104);
+  this->memory_.SetData32(0x5108, 0x1500);
+  this->memory_.SetData32(0x510c, 0x200);
 
-  // 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());
+  this->memory_.SetData32(0x5200, 0xfc);
+  this->memory_.SetData32(0x5204, 0x204);
+  this->memory_.SetData32(0x5208, 0x2500);
+  this->memory_.SetData32(0x520c, 0x300);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0x5300, 0xfc);
+  this->memory_.SetData32(0x5304, 0);
+  this->memory_.SetData8(0x5308, 1);
+  this->memory_.SetData8(0x5309, '\0');
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5400, 0xfc);
+  this->memory_.SetData32(0x5404, 0x104);
+  this->memory_.SetData32(0x5408, 0x3500);
+  this->memory_.SetData32(0x540c, 0x400);
+
+  this->memory_.SetData32(0x5500, 0xfc);
+  this->memory_.SetData32(0x5504, 0x204);
+  this->memory_.SetData32(0x5508, 0x4500);
+  this->memory_.SetData32(0x550c, 0x500);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
+
+  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+  this->eh_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x660cU, info.start);
+  EXPECT_EQ(0x680cU, info.end);
+
+  this->eh_frame_->TestGetFdeInfo(1, &info);
+  EXPECT_EQ(0x5200U, info.offset);
+  EXPECT_EQ(0x770cU, info.start);
+  EXPECT_EQ(0x7a0cU, info.end);
+
+  this->eh_frame_->TestGetFdeInfo(2, &info);
+  EXPECT_EQ(0x5400U, info.offset);
+  EXPECT_EQ(0x890cU, info.start);
+  EXPECT_EQ(0x8d0cU, info.end);
+
+  this->eh_frame_->TestGetFdeInfo(3, &info);
+  EXPECT_EQ(0x5500U, info.offset);
+  EXPECT_EQ(0x9a0cU, info.start);
+  EXPECT_EQ(0x9f0cU, info.end);
 }
 
-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, Init32_fde_not_following_cie) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0);
+  this->memory_.SetData8(0x5008, 1);
+  this->memory_.SetData8(0x5009, '\0');
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0x1000);
+  this->memory_.SetData32(0x5108, 0x1500);
+  this->memory_.SetData32(0x510c, 0x200);
+
+  ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, 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);
+TYPED_TEST_P(DwarfEhFrameTest, Init64) {
+  // CIE 64 information.
+  this->memory_.SetData32(0x5000, 0xffffffff);
+  this->memory_.SetData64(0x5004, 0xf4);
+  this->memory_.SetData64(0x500c, 0);
+  this->memory_.SetData8(0x5014, 1);
+  this->memory_.SetData8(0x5015, '\0');
 
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
+  // FDE 64 information.
+  this->memory_.SetData32(0x5100, 0xffffffff);
+  this->memory_.SetData64(0x5104, 0xf4);
+  this->memory_.SetData64(0x510c, 0x10c);
+  this->memory_.SetData64(0x5114, 0x1500);
+  this->memory_.SetData64(0x511c, 0x200);
 
-  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
-  ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x1384U, info->pc);
-  EXPECT_EQ(0x1540U, info->offset);
+  this->memory_.SetData32(0x5200, 0xffffffff);
+  this->memory_.SetData64(0x5204, 0xf4);
+  this->memory_.SetData64(0x520c, 0x20c);
+  this->memory_.SetData64(0x5214, 0x2500);
+  this->memory_.SetData64(0x521c, 0x300);
+
+  // CIE 64 information.
+  this->memory_.SetData32(0x5300, 0xffffffff);
+  this->memory_.SetData64(0x5304, 0xf4);
+  this->memory_.SetData64(0x530c, 0);
+  this->memory_.SetData8(0x5314, 1);
+  this->memory_.SetData8(0x5315, '\0');
+
+  // FDE 64 information.
+  this->memory_.SetData32(0x5400, 0xffffffff);
+  this->memory_.SetData64(0x5404, 0xf4);
+  this->memory_.SetData64(0x540c, 0x10c);
+  this->memory_.SetData64(0x5414, 0x3500);
+  this->memory_.SetData64(0x541c, 0x400);
+
+  this->memory_.SetData32(0x5500, 0xffffffff);
+  this->memory_.SetData64(0x5504, 0xf4);
+  this->memory_.SetData64(0x550c, 0x20c);
+  this->memory_.SetData64(0x5514, 0x4500);
+  this->memory_.SetData64(0x551c, 0x500);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
+
+  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+  this->eh_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x661cU, info.start);
+  EXPECT_EQ(0x681cU, info.end);
+
+  this->eh_frame_->TestGetFdeInfo(1, &info);
+  EXPECT_EQ(0x5200U, info.offset);
+  EXPECT_EQ(0x771cU, info.start);
+  EXPECT_EQ(0x7a1cU, info.end);
+
+  this->eh_frame_->TestGetFdeInfo(2, &info);
+  EXPECT_EQ(0x5400U, info.offset);
+  EXPECT_EQ(0x891cU, info.start);
+  EXPECT_EQ(0x8d1cU, info.end);
+
+  this->eh_frame_->TestGetFdeInfo(3, &info);
+  EXPECT_EQ(0x5500U, info.offset);
+  EXPECT_EQ(0x9a1cU, info.start);
+  EXPECT_EQ(0x9f1cU, info.end);
 }
 
-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);
+TYPED_TEST_P(DwarfEhFrameTest, Init64_fde_not_following_cie) {
+  // CIE 64 information.
+  this->memory_.SetData32(0x5000, 0xffffffff);
+  this->memory_.SetData64(0x5004, 0xf4);
+  this->memory_.SetData64(0x500c, 0);
+  this->memory_.SetData8(0x5014, 1);
+  this->memory_.SetData8(0x5015, '\0');
 
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
+  // 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);
 
-  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
-  ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x3344U, info->pc);
-  EXPECT_EQ(0x3500U, info->offset);
+  ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->last_error());
 }
 
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_cached) {
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-  this->eh_frame_->TestSetEntriesOffset(0x1000);
-  this->eh_frame_->TestSetTableEntrySize(0x10);
+TYPED_TEST_P(DwarfEhFrameTest, Init_version1) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0);
+  this->memory_.SetData8(0x5008, 1);
+  // Augment string.
+  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'R', 'P', 'L', '\0'});
+  // Code alignment factor.
+  this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x80, 0x00});
+  // Data alignment factor.
+  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
+  // Return address register
+  this->memory_.SetData8(0x5014, 0x84);
+  // Augmentation length
+  this->memory_.SetMemory(0x5015, std::vector<uint8_t>{0x84, 0x00});
+  // R data.
+  this->memory_.SetData8(0x5017, DW_EH_PE_pcrel | DW_EH_PE_udata2);
 
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0x104);
+  this->memory_.SetData16(0x5108, 0x1500);
+  this->memory_.SetData16(0x510a, 0x200);
 
-  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
-  ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x344U, info->pc);
-  EXPECT_EQ(0x500U, info->offset);
+  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200));
+  ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
 
-  // 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(0x344U, info->pc);
-  EXPECT_EQ(0x500U, info->offset);
+  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
+  this->eh_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x660aU, info.start);
+  EXPECT_EQ(0x680aU, info.end);
 }
 
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetBinary_verify) {
-  this->eh_frame_->TestSetTableEntrySize(0x10);
-  this->eh_frame_->TestSetFdeCount(10);
+TYPED_TEST_P(DwarfEhFrameTest, Init_version4) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0);
+  this->memory_.SetData8(0x5008, 4);
+  // Augment string.
+  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
+  // Address size.
+  this->memory_.SetData8(0x500e, 4);
+  // Segment size.
+  this->memory_.SetData8(0x500f, 0);
+  // Code alignment factor.
+  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x80, 0x00});
+  // Data alignment factor.
+  this->memory_.SetMemory(0x5012, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
+  // Return address register
+  this->memory_.SetMemory(0x5016, std::vector<uint8_t>{0x85, 0x10});
+  // Augmentation length
+  this->memory_.SetMemory(0x5018, std::vector<uint8_t>{0x84, 0x00});
+  // L data.
+  this->memory_.SetData8(0x501a, 0x10);
+  // P data.
+  this->memory_.SetData8(0x501b, DW_EH_PE_udata4);
+  this->memory_.SetData32(0x501c, 0x100);
+  // R data.
+  this->memory_.SetData8(0x5020, DW_EH_PE_pcrel | DW_EH_PE_udata2);
 
-  typename DwarfEhFrame<TypeParam>::FdeInfo info;
-  for (size_t i = 0; i < 10; i++) {
-    info.pc = 0x1000 * (i + 1);
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0x104);
+  this->memory_.SetData16(0x5108, 0x1500);
+  this->memory_.SetData16(0x510a, 0x200);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200));
+  ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
+
+  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
+  this->eh_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x660aU, info.start);
+  EXPECT_EQ(0x680aU, info.end);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc) {
+  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
+  for (size_t i = 0; i < 9; i++) {
+    info.start = 0x1000 * (i + 1);
+    info.end = 0x1000 * (i + 2) - 0x10;
     info.offset = 0x5000 + i * 0x20;
-    this->eh_frame_->TestSetFdeInfo(i, info);
+    this->eh_frame_->TestPushFdeInfo(info);
   }
 
+  this->eh_frame_->TestSetFdeCount(0);
   uint64_t fde_offset;
-  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10));
-  // Not an error, just not found.
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
   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;
-  }
+
+  this->eh_frame_->TestSetFdeCount(9);
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
   // 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;
+    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "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;
+    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index " << i;
     EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 9))
+    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
         << "Failed at index " << i;
     EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
+        << "Failed at index " << i;
+    ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+  }
+
+  // Even number of elements.
+  this->eh_frame_->TestSetFdeCount(10);
+  info.start = 0xa000;
+  info.end = 0xaff0;
+  info.offset = 0x5120;
+  this->eh_frame_->TestPushFdeInfo(info);
+
+  for (size_t i = 0; i < 10; i++) {
+    TypeParam pc = 0x1000 * (i + 1);
+    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
+        << "Failed at index " << i;
+    ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
   }
 }
 
-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(0x344, &fde_offset));
-  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
-
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
-  EXPECT_EQ(0x500U, fde_offset);
-
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &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(0x444, &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) {
+  this->eh_frame_->TestSetOffset(0x4000);
+
   // CIE 32 information.
   this->memory_.SetData32(0xf000, 0x100);
   this->memory_.SetData32(0xf004, 0);
@@ -358,6 +405,8 @@
 }
 
 TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) {
+  this->eh_frame_->TestSetOffset(0x2000);
+
   // CIE 64 information.
   this->memory_.SetData32(0x6000, 0xffffffff);
   this->memory_.SetData64(0x6004, 0x100);
@@ -399,13 +448,9 @@
   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);
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, 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> DwarfEhFrameTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
new file mode 100644
index 0000000..1028ab9
--- /dev/null
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -0,0 +1,434 @@
+/*
+ * 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 "DwarfEhFrameWithHdr.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class MockDwarfEhFrameWithHdr : public DwarfEhFrameWithHdr<TypeParam> {
+ public:
+  MockDwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrameWithHdr<TypeParam>(memory) {}
+  ~MockDwarfEhFrameWithHdr() = 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 DwarfEhFrameWithHdr<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 DwarfEhFrameWithHdrTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_.Clear();
+    eh_frame_ = new MockDwarfEhFrameWithHdr<TypeParam>(&memory_);
+    ResetLogs();
+  }
+
+  void TearDown() override { delete eh_frame_; }
+
+  MemoryFake memory_;
+  MockDwarfEhFrameWithHdr<TypeParam>* eh_frame_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, 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(DwarfEhFrameWithHdrTest, 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(DwarfEhFrameWithHdrTest, 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(0x1384U, info->pc);
+  EXPECT_EQ(0x1540U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, 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(0x3344U, info->pc);
+  EXPECT_EQ(0x3500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, 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(0x344U, 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(0x344U, info->pc);
+  EXPECT_EQ(0x500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetBinary_verify) {
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+  this->eh_frame_->TestSetFdeCount(10);
+
+  typename DwarfEhFrameWithHdr<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(DwarfEhFrameWithHdrTest, GetFdeOffsetBinary_index_fail) {
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+  this->eh_frame_->TestSetFdeCount(10);
+
+  uint64_t fde_offset;
+  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x1000, &fde_offset, 10));
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, 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(0x344, &fde_offset));
+  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
+  EXPECT_EQ(0x500U, fde_offset);
+
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &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(0x444, &fde_offset));
+  EXPECT_EQ(0x600U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, 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(DwarfEhFrameWithHdrTest, 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(DwarfEhFrameWithHdrTest, 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(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_binary_search) {
+  this->eh_frame_->TestSetTableEntrySize(16);
+  this->eh_frame_->TestSetFdeCount(10);
+
+  typename DwarfEhFrameWithHdr<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(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_sequential_search) {
+  this->eh_frame_->TestSetFdeCount(10);
+  this->eh_frame_->TestSetTableEntrySize(0);
+
+  typename DwarfEhFrameWithHdr<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(DwarfEhFrameWithHdrTest, 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(DwarfEhFrameWithHdrTest, 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);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeFromPc_fde_not_found) {
+  this->eh_frame_->TestSetTableEntrySize(16);
+  this->eh_frame_->TestSetFdeCount(1);
+
+  typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
+  info.pc = 0x550;
+  info.offset = 0x10500;
+  this->eh_frame_->TestSetFdeInfo(0, info);
+
+  ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, GetFdeInfoFromIndex_expect_cache_fail,
+                           GetFdeInfoFromIndex_read_pcrel, GetFdeInfoFromIndex_read_datarel,
+                           GetFdeInfoFromIndex_cached, GetFdeOffsetBinary_verify,
+                           GetFdeOffsetBinary_index_fail, GetFdeOffsetSequential,
+                           GetFdeOffsetSequential_last_element, GetFdeOffsetSequential_end_check,
+                           GetFdeOffsetFromPc_fail_fde_count, GetFdeOffsetFromPc_binary_search,
+                           GetFdeOffsetFromPc_sequential_search, GetCieFde32, GetCieFde64,
+                           GetFdeFromPc_fde_not_found);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameWithHdrTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index c701a29..d54b0bf 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -42,16 +42,16 @@
 
   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 TestSetCie32Value(uint32_t value32) { this->cie32_value_ = value32; }
+
+  void TestSetCie64Value(uint64_t value64) { this->cie64_value_ = value64; }
+
   void TestSetCachedCieEntry(uint64_t offset, const DwarfCie& cie) {
     this->cie_entries_[offset] = cie;
   }
@@ -77,6 +77,8 @@
     memory_.Clear();
     section_ = new MockDwarfSectionImpl<TypeParam>(&memory_);
     ResetLogs();
+    section_->TestSetCie32Value(static_cast<uint32_t>(-1));
+    section_->TestSetCie64Value(static_cast<uint64_t>(-1));
   }
 
   void TearDown() override { delete section_; }
@@ -340,6 +342,23 @@
   EXPECT_EQ(0x10U, regs.sp());
 }
 
+TYPED_TEST_P(DwarfSectionImplTest, Eval_pc_zero) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsImplFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0;
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_TRUE(finished);
+  EXPECT_EQ(0U, regs.pc());
+  EXPECT_EQ(0x10U, regs.sp());
+}
+
 TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) {
   DwarfCie cie{.return_address_register = 5};
   RegsImplFake<TypeParam> regs(10, 9);
@@ -414,22 +433,6 @@
   EXPECT_EQ(0x80000000U, regs.pc());
 }
 
-TYPED_TEST_P(DwarfSectionImplTest, Eval_same_cfa_same_pc) {
-  DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsImplFake<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}};
-  bool finished;
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
-  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());
@@ -447,8 +450,6 @@
   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);
@@ -492,8 +493,6 @@
   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);
@@ -513,15 +512,13 @@
 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_.SetData64(0x800c, 0xffffffffffffffffULL);
   this->memory_.SetData8(0x8014, 0x1);
   this->memory_.SetData8(0x8015, '\0');
   this->memory_.SetData8(0x8016, 4);
   this->memory_.SetData8(0x8017, 8);
   this->memory_.SetData8(0x8018, 0x20);
 
-  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);
@@ -556,8 +553,6 @@
   // 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);
@@ -587,8 +582,6 @@
   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);
@@ -616,8 +609,6 @@
   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);
@@ -648,7 +639,6 @@
   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;
@@ -672,7 +662,6 @@
   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;
@@ -699,7 +688,6 @@
   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;
@@ -726,7 +714,6 @@
   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{};
@@ -854,10 +841,10 @@
     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,
+    Eval_pc_zero, Eval_return_address, Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+    GetCie_fail_should_not_cache, GetCie_32_version_check, GetCie_negative_data_alignment_factor,
+    GetCie_64_no_augment, GetCie_augment, GetCie_version_3, GetCie_version_4,
+    GetFdeFromOffset_fail_should_not_cache, GetFdeFromOffset_32_no_augment,
     GetFdeFromOffset_32_no_augment_non_zero_segment_size, GetFdeFromOffset_32_augment,
     GetFdeFromOffset_64_no_augment, GetFdeFromOffset_cached, GetCfaLocationInfo_cie_not_cached,
     GetCfaLocationInfo_cie_cached, Log);
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index 71f7f6b..b94a8a4 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -32,7 +32,7 @@
 std::deque<FunctionData> ElfInterfaceFake::functions_;
 std::deque<StepData> ElfInterfaceFake::steps_;
 
-bool ElfInterfaceFake::GetFunctionName(uint64_t, std::string* name, uint64_t* offset) {
+bool ElfInterfaceFake::GetFunctionName(uint64_t, uint64_t, std::string* name, uint64_t* offset) {
   if (functions_.empty()) {
     return false;
   }
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index 4359bca..565b13f 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -27,6 +27,8 @@
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
 
+#include "ElfInterfaceArm.h"
+
 namespace unwindstack {
 
 struct StepData {
@@ -48,6 +50,10 @@
   ElfFake(Memory* memory) : Elf(memory) { valid_ = true; }
   virtual ~ElfFake() = default;
 
+  void FakeSetValid(bool valid) { valid_ = valid; }
+
+  void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; }
+
   void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); }
 };
 
@@ -56,15 +62,14 @@
   ElfInterfaceFake(Memory* memory) : ElfInterface(memory) {}
   virtual ~ElfInterfaceFake() = default;
 
-  bool Init() override { return false; }
+  bool Init(uint64_t*) override { return false; }
   void InitHeaders() override {}
   bool GetSoname(std::string*) override { return false; }
 
-  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
+  bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override;
 
   bool Step(uint64_t, Regs*, Memory*, bool*) override;
 
-  void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; }
 
   static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
   static void FakePushStepData(const StepData data) { steps_.push_back(data); }
@@ -79,6 +84,37 @@
   static std::deque<StepData> steps_;
 };
 
+class ElfInterface32Fake : public ElfInterface32 {
+ public:
+  ElfInterface32Fake(Memory* memory) : ElfInterface32(memory) {}
+  virtual ~ElfInterface32Fake() = default;
+
+  void FakeSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+  void FakeSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+  void FakeSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+  void FakeSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+class ElfInterface64Fake : public ElfInterface64 {
+ public:
+  ElfInterface64Fake(Memory* memory) : ElfInterface64(memory) {}
+  virtual ~ElfInterface64Fake() = default;
+
+  void FakeSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+  void FakeSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+  void FakeSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+  void FakeSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+class ElfInterfaceArmFake : public ElfInterfaceArm {
+ public:
+  ElfInterfaceArmFake(Memory* memory) : ElfInterfaceArm(memory) {}
+  virtual ~ElfInterfaceArmFake() = default;
+
+  void FakeSetStartOffset(uint64_t offset) { start_offset_ = offset; }
+  void FakeSetTotalEntries(size_t entries) { total_entries_ = entries; }
+};
+
 }  // namespace unwindstack
 
 #endif  // _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 4df7e1c..4b621c9 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -25,6 +25,7 @@
 #include "ElfInterfaceArm.h"
 #include "Machine.h"
 
+#include "ElfFake.h"
 #include "MemoryFake.h"
 
 namespace unwindstack {
@@ -41,7 +42,7 @@
 };
 
 TEST_F(ElfInterfaceArmTest, GetPrel32Addr) {
-  ElfInterfaceArm interface(&memory_);
+  ElfInterfaceArmFake interface(&memory_);
   memory_.SetData32(0x1000, 0x230000);
 
   uint32_t value;
@@ -58,36 +59,36 @@
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_start_zero) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0);
-  interface.set_total_entries(10);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0);
+  interface.FakeSetTotalEntries(10);
 
   uint64_t entry_offset;
   ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset));
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_no_entries) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x100);
-  interface.set_total_entries(0);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x100);
+  interface.FakeSetTotalEntries(0);
 
   uint64_t entry_offset;
   ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset));
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_no_valid_memory) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x100);
-  interface.set_total_entries(2);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x100);
+  interface.FakeSetTotalEntries(2);
 
   uint64_t entry_offset;
   ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset));
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_ip_before_first) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(1);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(1);
   memory_.SetData32(0x1000, 0x6000);
 
   uint64_t entry_offset;
@@ -95,9 +96,9 @@
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_single_entry_negative_value) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x8000);
-  interface.set_total_entries(1);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x8000);
+  interface.FakeSetTotalEntries(1);
   memory_.SetData32(0x8000, 0x7fffff00);
 
   uint64_t entry_offset;
@@ -106,9 +107,9 @@
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_two_entries) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(2);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(2);
   memory_.SetData32(0x1000, 0x6000);
   memory_.SetData32(0x1008, 0x7000);
 
@@ -117,11 +118,10 @@
   ASSERT_EQ(0x1000U, entry_offset);
 }
 
-
 TEST_F(ElfInterfaceArmTest, FindEntry_last_check_single_entry) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(1);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(1);
   memory_.SetData32(0x1000, 0x6000);
 
   uint64_t entry_offset;
@@ -136,9 +136,9 @@
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_last_check_multiple_entries) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(2);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(2);
   memory_.SetData32(0x1000, 0x6000);
   memory_.SetData32(0x1008, 0x8000);
 
@@ -155,9 +155,9 @@
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_multiple_entries_even) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(4);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(4);
   memory_.SetData32(0x1000, 0x6000);
   memory_.SetData32(0x1008, 0x7000);
   memory_.SetData32(0x1010, 0x8000);
@@ -178,9 +178,9 @@
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_multiple_entries_odd) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(5);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(5);
   memory_.SetData32(0x1000, 0x5000);
   memory_.SetData32(0x1008, 0x6000);
   memory_.SetData32(0x1010, 0x7000);
@@ -203,9 +203,9 @@
 }
 
 TEST_F(ElfInterfaceArmTest, iterate) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(5);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(5);
   memory_.SetData32(0x1000, 0x5000);
   memory_.SetData32(0x1008, 0x6000);
   memory_.SetData32(0x1010, 0x7000);
@@ -242,56 +242,36 @@
   ASSERT_EQ(0xa020U, entries[4]);
 }
 
-TEST_F(ElfInterfaceArmTest, FindEntry_load_bias) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(2);
-  memory_.SetData32(0x1000, 0x6000);
-  memory_.SetData32(0x1008, 0x8000);
-
-  uint64_t entry_offset;
-  interface.set_load_bias(0x2000);
-  ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset));
-  ASSERT_FALSE(interface.FindEntry(0x8000, &entry_offset));
-  ASSERT_FALSE(interface.FindEntry(0x8fff, &entry_offset));
-  ASSERT_TRUE(interface.FindEntry(0x9000, &entry_offset));
-  ASSERT_EQ(0x1000U, entry_offset);
-  ASSERT_TRUE(interface.FindEntry(0xb007, &entry_offset));
-  ASSERT_EQ(0x1000U, entry_offset);
-  ASSERT_TRUE(interface.FindEntry(0xb008, &entry_offset));
-  ASSERT_EQ(0x1008U, entry_offset);
-}
-
 TEST_F(ElfInterfaceArmTest, HandleType_not_arm_exidx) {
-  ElfInterfaceArm interface(&memory_);
+  ElfInterfaceArmFake interface(&memory_);
 
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_NULL));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOAD));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_DYNAMIC));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_INTERP));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_NOTE));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_SHLIB));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_PHDR));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_TLS));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOOS));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_HIOS));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOPROC));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_HIPROC));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_EH_FRAME));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_STACK));
+  ASSERT_FALSE(interface.HandleType(0x1000, PT_NULL, 0));
+  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOAD, 0));
+  ASSERT_FALSE(interface.HandleType(0x1000, PT_DYNAMIC, 0));
+  ASSERT_FALSE(interface.HandleType(0x1000, PT_INTERP, 0));
+  ASSERT_FALSE(interface.HandleType(0x1000, PT_NOTE, 0));
+  ASSERT_FALSE(interface.HandleType(0x1000, PT_SHLIB, 0));
+  ASSERT_FALSE(interface.HandleType(0x1000, PT_PHDR, 0));
+  ASSERT_FALSE(interface.HandleType(0x1000, PT_TLS, 0));
+  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOOS, 0));
+  ASSERT_FALSE(interface.HandleType(0x1000, PT_HIOS, 0));
+  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOPROC, 0));
+  ASSERT_FALSE(interface.HandleType(0x1000, PT_HIPROC, 0));
+  ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_EH_FRAME, 0));
+  ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_STACK, 0));
 }
 
 TEST_F(ElfInterfaceArmTest, HandleType_arm_exidx) {
-  ElfInterfaceArm interface(&memory_);
+  ElfInterfaceArmFake interface(&memory_);
 
   Elf32_Phdr phdr;
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(100);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(100);
   phdr.p_vaddr = 0x2000;
   phdr.p_memsz = 0xa00;
 
   // Verify that if reads fail, we don't set the values but still get true.
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
+  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0));
   ASSERT_EQ(0x1000U, interface.start_offset());
   ASSERT_EQ(100U, interface.total_entries());
 
@@ -299,7 +279,7 @@
   memory_.SetData32(
       0x1000 + reinterpret_cast<uint64_t>(&phdr.p_vaddr) - reinterpret_cast<uint64_t>(&phdr),
       phdr.p_vaddr);
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
+  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0));
   ASSERT_EQ(0x1000U, interface.start_offset());
   ASSERT_EQ(100U, interface.total_entries());
 
@@ -307,27 +287,26 @@
   memory_.SetData32(
       0x1000 + reinterpret_cast<uint64_t>(&phdr.p_memsz) - reinterpret_cast<uint64_t>(&phdr),
       phdr.p_memsz);
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
+  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0));
   ASSERT_EQ(0x2000U, interface.start_offset());
   ASSERT_EQ(320U, interface.total_entries());
 
   // Non-zero load bias.
-  interface.set_load_bias(0x1000);
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
+  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0x1000));
   ASSERT_EQ(0x1000U, interface.start_offset());
   ASSERT_EQ(320U, interface.total_entries());
 }
 
 TEST_F(ElfInterfaceArmTest, StepExidx) {
-  ElfInterfaceArm interface(&memory_);
+  ElfInterfaceArmFake interface(&memory_);
 
   // FindEntry fails.
   bool finished;
   ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr, &finished));
 
   // ExtractEntry should fail.
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(2);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(2);
   memory_.SetData32(0x1000, 0x6000);
   memory_.SetData32(0x1008, 0x8000);
 
@@ -353,10 +332,10 @@
 }
 
 TEST_F(ElfInterfaceArmTest, StepExidx_pc_set) {
-  ElfInterfaceArm interface(&memory_);
+  ElfInterfaceArmFake interface(&memory_);
 
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(2);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(2);
   memory_.SetData32(0x1000, 0x6000);
   memory_.SetData32(0x1004, 0x808800b0);
   memory_.SetData32(0x1008, 0x8000);
@@ -379,10 +358,10 @@
 }
 
 TEST_F(ElfInterfaceArmTest, StepExidx_cant_unwind) {
-  ElfInterfaceArm interface(&memory_);
+  ElfInterfaceArmFake interface(&memory_);
 
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(1);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(1);
   memory_.SetData32(0x1000, 0x6000);
   memory_.SetData32(0x1004, 1);
 
@@ -401,10 +380,10 @@
 }
 
 TEST_F(ElfInterfaceArmTest, StepExidx_refuse_unwind) {
-  ElfInterfaceArm interface(&memory_);
+  ElfInterfaceArmFake interface(&memory_);
 
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(1);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(1);
   memory_.SetData32(0x1000, 0x6000);
   memory_.SetData32(0x1004, 0x808000b0);
 
@@ -422,4 +401,40 @@
   ASSERT_EQ(0x1234U, regs.pc());
 }
 
+TEST_F(ElfInterfaceArmTest, StepExidx_pc_zero) {
+  ElfInterfaceArmFake interface(&memory_);
+
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(1);
+  memory_.SetData32(0x1000, 0x6000);
+  // Set the pc using a pop r15 command.
+  memory_.SetData32(0x1004, 0x808800b0);
+
+  // pc value of zero.
+  process_memory_.SetData32(0x10000, 0);
+
+  RegsArm regs;
+  regs[ARM_REG_SP] = 0x10000;
+  regs[ARM_REG_LR] = 0x20000;
+  regs.set_sp(regs[ARM_REG_SP]);
+  regs.set_pc(0x1234);
+
+  bool finished;
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(finished);
+  ASSERT_EQ(0U, regs.pc());
+
+  // Now set the pc from the lr register (pop r14).
+  memory_.SetData32(0x1004, 0x808400b0);
+
+  regs[ARM_REG_SP] = 0x10000;
+  regs[ARM_REG_LR] = 0x20000;
+  regs.set_sp(regs[ARM_REG_SP]);
+  regs.set_pc(0x1234);
+
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(finished);
+  ASSERT_EQ(0U, regs.pc());
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index acb7320..e138c3a 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -25,6 +25,7 @@
 #include "DwarfEncoding.h"
 #include "ElfInterfaceArm.h"
 
+#include "ElfFake.h"
 #include "MemoryFake.h"
 
 #if !defined(PT_ARM_EXIDX)
@@ -134,7 +135,9 @@
   phdr.p_align = 0x1000;
   memory_.SetMemory(0x100, &phdr, sizeof(phdr));
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0x2000U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(1U, pt_loads.size());
@@ -190,7 +193,9 @@
   phdr.p_align = 0x1002;
   memory_.SetMemory(0x100 + 2 * sizeof(phdr), &phdr, sizeof(phdr));
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0x2000U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(3U, pt_loads.size());
@@ -257,7 +262,9 @@
   phdr.p_align = 0x1002;
   memory_.SetMemory(0x100 + 2 * (sizeof(phdr) + 100), &phdr, sizeof(phdr));
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0x2000U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(3U, pt_loads.size());
@@ -326,7 +333,9 @@
   phdr.p_align = 0x1002;
   memory_.SetMemory(0x100 + 2 * sizeof(phdr), &phdr, sizeof(phdr));
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(1U, pt_loads.size());
@@ -398,7 +407,9 @@
   memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
   phdr_offset += sizeof(phdr);
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0x2000U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(1U, pt_loads.size());
@@ -438,7 +449,9 @@
   memory_.SetData32(0x2000, 0x1000);
   memory_.SetData32(0x2008, 0x1000);
 
-  ASSERT_TRUE(elf_arm.Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf_arm.Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
 
   std::vector<uint32_t> entries;
   for (auto addr : elf_arm) {
@@ -493,7 +506,10 @@
 
   SetStringMemory(0x10010, "fake_soname.so");
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
+
   std::string name;
   ASSERT_TRUE(elf->GetSoname(&name));
   ASSERT_STREQ("fake_soname.so", name.c_str());
@@ -549,7 +565,10 @@
 
   SetStringMemory(0x10010, "fake_soname.so");
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
+
   std::string name;
   ASSERT_FALSE(elf->GetSoname(&name));
 }
@@ -603,7 +622,10 @@
 
   SetStringMemory(0x10010, "fake_soname.so");
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
+
   std::string name;
   ASSERT_FALSE(elf->GetSoname(&name));
 }
@@ -616,38 +638,14 @@
   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);
+  elf.FakeSetEhFrameOffset(0x10000);
+  elf.FakeSetEhFrameSize(0);
+  elf.FakeSetDebugFrameOffset(0);
+  elf.FakeSetDebugFrameSize(0);
 
   memory_.SetMemory(0x10000,
                     std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata2, DW_EH_PE_udata2});
@@ -661,21 +659,21 @@
 }
 
 TEST_F(ElfInterfaceTest, init_headers_eh_frame32) {
-  InitHeadersEhFrameTest<MockElfInterface32>();
+  InitHeadersEhFrameTest<ElfInterface32Fake>();
 }
 
 TEST_F(ElfInterfaceTest, init_headers_eh_frame64) {
-  InitHeadersEhFrameTest<MockElfInterface64>();
+  InitHeadersEhFrameTest<ElfInterface64Fake>();
 }
 
 template <typename ElfType>
 void ElfInterfaceTest::InitHeadersDebugFrame() {
   ElfType elf(&memory_);
 
-  elf.TestSetEhFrameOffset(0);
-  elf.TestSetEhFrameSize(0);
-  elf.TestSetDebugFrameOffset(0x5000);
-  elf.TestSetDebugFrameSize(0x200);
+  elf.FakeSetEhFrameOffset(0);
+  elf.FakeSetEhFrameSize(0);
+  elf.FakeSetDebugFrameOffset(0x5000);
+  elf.FakeSetDebugFrameSize(0x200);
 
   memory_.SetData32(0x5000, 0xfc);
   memory_.SetData32(0x5004, 0xffffffff);
@@ -694,21 +692,21 @@
 }
 
 TEST_F(ElfInterfaceTest, init_headers_debug_frame32) {
-  InitHeadersDebugFrame<MockElfInterface32>();
+  InitHeadersDebugFrame<ElfInterface32Fake>();
 }
 
 TEST_F(ElfInterfaceTest, init_headers_debug_frame64) {
-  InitHeadersDebugFrame<MockElfInterface64>();
+  InitHeadersDebugFrame<ElfInterface64Fake>();
 }
 
 template <typename ElfType>
 void ElfInterfaceTest::InitHeadersEhFrameFail() {
   ElfType elf(&memory_);
 
-  elf.TestSetEhFrameOffset(0x1000);
-  elf.TestSetEhFrameSize(0x100);
-  elf.TestSetDebugFrameOffset(0);
-  elf.TestSetDebugFrameSize(0);
+  elf.FakeSetEhFrameOffset(0x1000);
+  elf.FakeSetEhFrameSize(0x100);
+  elf.FakeSetDebugFrameOffset(0);
+  elf.FakeSetDebugFrameSize(0);
 
   elf.InitHeaders();
 
@@ -719,21 +717,21 @@
 }
 
 TEST_F(ElfInterfaceTest, init_headers_eh_frame32_fail) {
-  InitHeadersEhFrameFail<MockElfInterface32>();
+  InitHeadersEhFrameFail<ElfInterface32Fake>();
 }
 
 TEST_F(ElfInterfaceTest, init_headers_eh_frame64_fail) {
-  InitHeadersEhFrameFail<MockElfInterface64>();
+  InitHeadersEhFrameFail<ElfInterface64Fake>();
 }
 
 template <typename ElfType>
 void ElfInterfaceTest::InitHeadersDebugFrameFail() {
   ElfType elf(&memory_);
 
-  elf.TestSetEhFrameOffset(0);
-  elf.TestSetEhFrameSize(0);
-  elf.TestSetDebugFrameOffset(0x1000);
-  elf.TestSetDebugFrameSize(0x100);
+  elf.FakeSetEhFrameOffset(0);
+  elf.FakeSetEhFrameSize(0);
+  elf.FakeSetDebugFrameOffset(0x1000);
+  elf.FakeSetDebugFrameSize(0x100);
 
   elf.InitHeaders();
 
@@ -744,11 +742,11 @@
 }
 
 TEST_F(ElfInterfaceTest, init_headers_debug_frame32_fail) {
-  InitHeadersDebugFrameFail<MockElfInterface32>();
+  InitHeadersDebugFrameFail<ElfInterface32Fake>();
 }
 
 TEST_F(ElfInterfaceTest, init_headers_debug_frame64_fail) {
-  InitHeadersDebugFrameFail<MockElfInterface64>();
+  InitHeadersDebugFrameFail<ElfInterface64Fake>();
 }
 
 template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
@@ -762,7 +760,9 @@
   ehdr.e_shentsize = sizeof(Shdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
 }
 
 TEST_F(ElfInterfaceTest, init_section_headers_malformed32) {
@@ -827,7 +827,9 @@
   InitSym<Sym>(0x5000, 0x90000, 0x1000, 0x100, 0xf000, "function_one");
   InitSym<Sym>(0x6000, 0xd0000, 0x1000, 0x300, 0xf000, "function_two");
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
   EXPECT_EQ(0U, elf->debug_frame_offset());
   EXPECT_EQ(0U, elf->debug_frame_size());
   EXPECT_EQ(0U, elf->gnu_debugdata_offset());
@@ -836,10 +838,10 @@
   // Look in the first symbol table.
   std::string name;
   uint64_t name_offset;
-  ASSERT_TRUE(elf->GetFunctionName(0x90010, &name, &name_offset));
+  ASSERT_TRUE(elf->GetFunctionName(0x90010, 0, &name, &name_offset));
   EXPECT_EQ("function_one", name);
   EXPECT_EQ(16U, name_offset);
-  ASSERT_TRUE(elf->GetFunctionName(0xd0020, &name, &name_offset));
+  ASSERT_TRUE(elf->GetFunctionName(0xd0020, 0, &name, &name_offset));
   EXPECT_EQ("function_two", name);
   EXPECT_EQ(32U, name_offset);
 }
@@ -908,14 +910,44 @@
   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 = 0x300;
+  shdr.sh_addr = 0x7000;
+  shdr.sh_offset = 0x7000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = 0x800;
+  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 = 0x400;
+  shdr.sh_addr = 0x6000;
+  shdr.sh_offset = 0xa000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = 0xf00;
+  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"));
+  memory_.SetMemory(0xf300, ".eh_frame", sizeof(".eh_frame"));
+  memory_.SetMemory(0xf400, ".eh_frame_hdr", sizeof(".eh_frame_hdr"));
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
   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());
+  EXPECT_EQ(0x7000U, elf->eh_frame_offset());
+  EXPECT_EQ(0x800U, elf->eh_frame_size());
+  EXPECT_EQ(0xa000U, elf->eh_frame_hdr_offset());
+  EXPECT_EQ(0xf00U, elf->eh_frame_hdr_size());
 }
 
 TEST_F(ElfInterfaceTest, init_section_headers_offsets32) {
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 42a0246..4e48fa1 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -20,11 +20,13 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
 
+#include "ElfFake.h"
 #include "ElfTestUtils.h"
 #include "LogFake.h"
 #include "MemoryFake.h"
@@ -107,7 +109,7 @@
 TEST_F(ElfTest, invalid_memory) {
   Elf elf(memory_);
 
-  ASSERT_FALSE(elf.Init());
+  ASSERT_FALSE(elf.Init(false));
   ASSERT_FALSE(elf.valid());
 }
 
@@ -119,7 +121,7 @@
   // Corrupt the ELF signature.
   memory_->SetData32(0, 0x7f000000);
 
-  ASSERT_FALSE(elf.Init());
+  ASSERT_FALSE(elf.Init(false));
   ASSERT_FALSE(elf.valid());
   ASSERT_TRUE(elf.interface() == nullptr);
 
@@ -130,7 +132,7 @@
   ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
 
   bool finished;
-  ASSERT_FALSE(elf.Step(0, nullptr, nullptr, &finished));
+  ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished));
 }
 
 TEST_F(ElfTest, elf32_invalid_machine) {
@@ -139,7 +141,7 @@
   InitElf32(EM_PPC);
 
   ResetLogs();
-  ASSERT_FALSE(elf.Init());
+  ASSERT_FALSE(elf.Init(false));
 
   ASSERT_EQ("", GetFakeLogBuf());
   ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86: e_machine = 20\n\n",
@@ -152,7 +154,7 @@
   InitElf64(EM_PPC64);
 
   ResetLogs();
-  ASSERT_FALSE(elf.Init());
+  ASSERT_FALSE(elf.Init(false));
 
   ASSERT_EQ("", GetFakeLogBuf());
   ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64: e_machine = 21\n\n",
@@ -164,7 +166,7 @@
 
   InitElf32(EM_ARM);
 
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(false));
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_ARM), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -176,7 +178,7 @@
 
   InitElf32(EM_386);
 
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(false));
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_386), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -188,7 +190,7 @@
 
   InitElf64(EM_AARCH64);
 
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(false));
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_AARCH64), elf.machine_type());
   ASSERT_EQ(ELFCLASS64, elf.class_type());
@@ -200,7 +202,7 @@
 
   InitElf64(EM_X86_64);
 
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(false));
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_X86_64), elf.machine_type());
   ASSERT_EQ(ELFCLASS64, elf.class_type());
@@ -214,7 +216,7 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(false));
   ASSERT_TRUE(elf.interface() != nullptr);
   ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
   EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
@@ -228,7 +230,7 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(false));
   ASSERT_TRUE(elf.interface() != nullptr);
   ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
   EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
@@ -242,14 +244,11 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(true));
   ASSERT_TRUE(elf.interface() != nullptr);
-  ASSERT_TRUE(elf.gnu_debugdata_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) {
@@ -259,42 +258,109 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(true));
   ASSERT_TRUE(elf.interface() != nullptr);
-  ASSERT_TRUE(elf.gnu_debugdata_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_);
+  ElfFake elf(memory_);
 
-  ElfInterface* interface = new ElfInterface32(memory_);
-  elf.set_elf_interface(interface);
+  ElfInterfaceFake* interface = new ElfInterfaceFake(memory_);
+  elf.FakeSetInterface(interface);
 
-  elf.set_valid(true);
-  interface->set_load_bias(0);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0);
   MapInfo map_info{.start = 0x1000, .end = 0x2000};
 
   ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
 
-  interface->set_load_bias(0x3000);
+  elf.FakeSetLoadBias(0x3000);
   ASSERT_EQ(0x3101U, elf.GetRelPc(0x1101, &map_info));
 
-  elf.set_valid(false);
+  elf.FakeSetValid(false);
+  elf.FakeSetLoadBias(0);
   ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
 }
 
+TEST_F(ElfTest, step_in_signal_map) {
+  ElfFake elf(memory_);
+
+  RegsArm regs;
+  regs[13] = 0x50000;
+  regs[15] = 0x8000;
+  regs.SetFromRaw();
+
+  ElfInterfaceFake* interface = new ElfInterfaceFake(memory_);
+  elf.FakeSetInterface(interface);
+
+  memory_->SetData32(0x3000, 0xdf0027ad);
+  MemoryFake process_memory;
+  process_memory.SetData32(0x50000, 0);
+  for (size_t i = 0; i < 16; i++) {
+    process_memory.SetData32(0x500a0 + i * sizeof(uint32_t), i);
+  }
+
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0);
+  bool finished;
+  ASSERT_TRUE(elf.Step(0x1000, 0x2000, &regs, &process_memory, &finished));
+  EXPECT_FALSE(finished);
+  EXPECT_EQ(15U, regs.pc());
+  EXPECT_EQ(13U, regs.sp());
+}
+
+class ElfInterfaceMock : public ElfInterface {
+ public:
+  ElfInterfaceMock(Memory* memory) : ElfInterface(memory) {}
+  virtual ~ElfInterfaceMock() = default;
+
+  bool Init(uint64_t*) override { return false; }
+  void InitHeaders() override {}
+  bool GetSoname(std::string*) override { return false; }
+  bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override { return false; }
+  MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
+};
+
+TEST_F(ElfTest, step_in_interface) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0);
+
+  RegsArm regs;
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+  MemoryFake process_memory;
+
+  bool finished;
+  EXPECT_CALL(*interface, Step(0x1000, &regs, &process_memory, &finished))
+      .WillOnce(::testing::Return(true));
+
+  ASSERT_TRUE(elf.Step(0x1000, 0x2000, &regs, &process_memory, &finished));
+}
+
+TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0x4000);
+
+  RegsArm regs;
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+  MemoryFake process_memory;
+
+  // Invalid relative pc given load_bias.
+  bool finished;
+  ASSERT_FALSE(elf.Step(0x1000, 0x2000, &regs, &process_memory, &finished));
+
+  EXPECT_CALL(*interface, Step(0x3300, &regs, &process_memory, &finished))
+      .WillOnce(::testing::Return(true));
+
+  ASSERT_TRUE(elf.Step(0x7300, 0x2000, &regs, &process_memory, &finished));
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index efcd029..fedaf87 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -42,6 +42,8 @@
     return true;
   }
 
+  void IterateRegisters(std::function<void(const char*, uint64_t)>) override {}
+
   uint64_t GetAdjustedPc(uint64_t rel_pc, Elf*) override { return rel_pc - 2; }
 
   bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
new file mode 100644
index 0000000..c8d1d98
--- /dev/null
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -0,0 +1,167 @@
+/*
+ * 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 <utility>
+#include <type_traits>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Regs.h>
+
+#include "Machine.h"
+
+namespace unwindstack {
+
+struct Register {
+  std::string expected_name;
+  uint64_t offset;
+
+  bool operator==(const Register& rhs) const {
+    return std::tie(expected_name, offset) == std::tie(rhs.expected_name, rhs.offset);
+  }
+};
+
+template<typename T>
+class RegsIterateTest : public ::testing::Test {
+};
+
+template<typename RegsType>
+std::vector<Register> ExpectedRegisters();
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsArm>() {
+  std::vector<Register> result;
+  result.push_back({"r0", ARM_REG_R0});
+  result.push_back({"r1", ARM_REG_R1});
+  result.push_back({"r2", ARM_REG_R2});
+  result.push_back({"r3", ARM_REG_R3});
+  result.push_back({"r4", ARM_REG_R4});
+  result.push_back({"r5", ARM_REG_R5});
+  result.push_back({"r6", ARM_REG_R6});
+  result.push_back({"r7", ARM_REG_R7});
+  result.push_back({"r8", ARM_REG_R8});
+  result.push_back({"r9", ARM_REG_R9});
+  result.push_back({"r10", ARM_REG_R10});
+  result.push_back({"r11", ARM_REG_R11});
+  result.push_back({"ip", ARM_REG_R12});
+  result.push_back({"sp", ARM_REG_SP});
+  result.push_back({"lr", ARM_REG_LR});
+  result.push_back({"pc", ARM_REG_PC});
+  return result;
+}
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsArm64>() {
+  std::vector<Register> result;
+  result.push_back({"x0", ARM64_REG_R0});
+  result.push_back({"x1", ARM64_REG_R1});
+  result.push_back({"x2", ARM64_REG_R2});
+  result.push_back({"x3", ARM64_REG_R3});
+  result.push_back({"x4", ARM64_REG_R4});
+  result.push_back({"x5", ARM64_REG_R5});
+  result.push_back({"x6", ARM64_REG_R6});
+  result.push_back({"x7", ARM64_REG_R7});
+  result.push_back({"x8", ARM64_REG_R8});
+  result.push_back({"x9", ARM64_REG_R9});
+  result.push_back({"x10", ARM64_REG_R10});
+  result.push_back({"x11", ARM64_REG_R11});
+  result.push_back({"x12", ARM64_REG_R12});
+  result.push_back({"x13", ARM64_REG_R13});
+  result.push_back({"x14", ARM64_REG_R14});
+  result.push_back({"x15", ARM64_REG_R15});
+  result.push_back({"x16", ARM64_REG_R16});
+  result.push_back({"x17", ARM64_REG_R17});
+  result.push_back({"x18", ARM64_REG_R18});
+  result.push_back({"x19", ARM64_REG_R19});
+  result.push_back({"x20", ARM64_REG_R20});
+  result.push_back({"x21", ARM64_REG_R21});
+  result.push_back({"x22", ARM64_REG_R22});
+  result.push_back({"x23", ARM64_REG_R23});
+  result.push_back({"x24", ARM64_REG_R24});
+  result.push_back({"x25", ARM64_REG_R25});
+  result.push_back({"x26", ARM64_REG_R26});
+  result.push_back({"x27", ARM64_REG_R27});
+  result.push_back({"x28", ARM64_REG_R28});
+  result.push_back({"x29", ARM64_REG_R29});
+  result.push_back({"sp", ARM64_REG_SP});
+  result.push_back({"lr", ARM64_REG_LR});
+  result.push_back({"pc", ARM64_REG_PC});
+  return result;
+}
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsX86>() {
+  std::vector<Register> result;
+  result.push_back({"eax", X86_REG_EAX});
+  result.push_back({"ebx", X86_REG_EBX});
+  result.push_back({"ecx", X86_REG_ECX});
+  result.push_back({"edx", X86_REG_EDX});
+  result.push_back({"ebp", X86_REG_EBP});
+  result.push_back({"edi", X86_REG_EDI});
+  result.push_back({"esi", X86_REG_ESI});
+  result.push_back({"esp", X86_REG_ESP});
+  result.push_back({"eip", X86_REG_EIP});
+  return result;
+}
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsX86_64>() {
+  std::vector<Register> result;
+  result.push_back({"rax", X86_64_REG_RAX});
+  result.push_back({"rbx", X86_64_REG_RBX});
+  result.push_back({"rcx", X86_64_REG_RCX});
+  result.push_back({"rdx", X86_64_REG_RDX});
+  result.push_back({"r8", X86_64_REG_R8});
+  result.push_back({"r9", X86_64_REG_R9});
+  result.push_back({"r10", X86_64_REG_R10});
+  result.push_back({"r11", X86_64_REG_R11});
+  result.push_back({"r12", X86_64_REG_R12});
+  result.push_back({"r13", X86_64_REG_R13});
+  result.push_back({"r14", X86_64_REG_R14});
+  result.push_back({"r15", X86_64_REG_R15});
+  result.push_back({"rdi", X86_64_REG_RDI});
+  result.push_back({"rsi", X86_64_REG_RSI});
+  result.push_back({"rbp", X86_64_REG_RBP});
+  result.push_back({"rsp", X86_64_REG_RSP});
+  result.push_back({"rip", X86_64_REG_RIP});
+  return result;
+}
+
+using RegTypes = ::testing::Types<RegsArm, RegsArm64, RegsX86, RegsX86_64>;
+TYPED_TEST_CASE(RegsIterateTest, RegTypes);
+
+TYPED_TEST(RegsIterateTest, iterate) {
+  std::vector<Register> expected = ExpectedRegisters<TypeParam>();
+  TypeParam regs;
+  for (const auto& reg : expected) {
+    regs[reg.offset] = reg.offset;
+  }
+
+  std::vector<Register> actual;
+  regs.IterateRegisters([&actual](const char* name, uint64_t value) {
+    actual.push_back({name, value});
+  });
+
+  ASSERT_EQ(expected, actual);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 3b9f92b..2a02669 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -115,7 +115,7 @@
   RegsArm arm;
 
   // Check fence posts.
-  elf_interface_->FakeSetLoadBias(0);
+  elf_->FakeSetLoadBias(0);
   ASSERT_EQ(3U,  arm.GetAdjustedPc(0x5, elf_.get()));
   ASSERT_EQ(4U,  arm.GetAdjustedPc(0x4, elf_.get()));
   ASSERT_EQ(3U,  arm.GetAdjustedPc(0x3, elf_.get()));
@@ -123,7 +123,7 @@
   ASSERT_EQ(1U,  arm.GetAdjustedPc(0x1, elf_.get()));
   ASSERT_EQ(0U,  arm.GetAdjustedPc(0x0, elf_.get()));
 
-  elf_interface_->FakeSetLoadBias(0x100);
+  elf_->FakeSetLoadBias(0x100);
   ASSERT_EQ(0xffU,  arm.GetAdjustedPc(0xff, elf_.get()));
   ASSERT_EQ(0x103U,  arm.GetAdjustedPc(0x105, elf_.get()));
   ASSERT_EQ(0x104U,  arm.GetAdjustedPc(0x104, elf_.get()));
@@ -133,13 +133,13 @@
   ASSERT_EQ(0x100U,  arm.GetAdjustedPc(0x100, elf_.get()));
 
   // Check thumb instructions handling.
-  elf_interface_->FakeSetLoadBias(0);
+  elf_->FakeSetLoadBias(0);
   memory_->SetData32(0x2000, 0);
   ASSERT_EQ(0x2003U,  arm.GetAdjustedPc(0x2005, elf_.get()));
   memory_->SetData32(0x2000, 0xe000f000);
   ASSERT_EQ(0x2001U,  arm.GetAdjustedPc(0x2005, elf_.get()));
 
-  elf_interface_->FakeSetLoadBias(0x400);
+  elf_->FakeSetLoadBias(0x400);
   memory_->SetData32(0x2100, 0);
   ASSERT_EQ(0x2503U,  arm.GetAdjustedPc(0x2505, elf_.get()));
   memory_->SetData32(0x2100, 0xf111f111);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 4d0366c..869d118 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -95,11 +95,39 @@
     info.elf = elf;
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     maps_.FakeAddMapInfo(info);
+
+    info.name = "/fake/compressed.so";
+    info.start = 0x33000;
+    info.end = 0x34000;
+    info.flags = PROT_READ | PROT_WRITE;
+    elf = new ElfFake(nullptr);
+    info.elf = elf;
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    maps_.FakeAddMapInfo(info);
+
+    info.name = "/fake/fake.apk";
+    info.start = 0x43000;
+    info.end = 0x44000;
+    info.offset = 0x1d000;
+    info.flags = PROT_READ | PROT_WRITE;
+    elf = new ElfFake(nullptr);
+    info.elf = elf;
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    maps_.FakeAddMapInfo(info);
+
+    info.name = "/fake/fake.oat";
+    info.start = 0x53000;
+    info.end = 0x54000;
+    info.offset = 0;
+    info.flags = PROT_READ | PROT_WRITE;
+    info.elf = nullptr;
+    maps_.FakeAddMapInfo(info);
   }
 
   void SetUp() override {
     ElfInterfaceFake::FakeClear();
     regs_.FakeSetMachineType(EM_ARM);
+    regs_.FakeSetReturnAddressValid(false);
   }
 
   static MapsFake maps_;
@@ -170,6 +198,33 @@
   EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
 }
 
+TEST_F(UnwinderTest, non_zero_map_offset) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.FakeSetPc(0x43000);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x43000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake.apk", frame->map_name);
+  EXPECT_EQ(0x1d000U, frame->map_offset);
+  EXPECT_EQ(0x43000U, frame->map_start);
+  EXPECT_EQ(0x44000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
 // Verify that no attempt to continue after the step indicates it is done.
 TEST_F(UnwinderTest, no_frames_after_finished) {
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
@@ -254,8 +309,8 @@
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
-  std::set<std::string> skip_set{"libunwind.so", "libanother.so"};
-  unwinder.Unwind(&skip_set);
+  std::vector<std::string> skip_libs{"libunwind.so", "libanother.so"};
+  unwinder.Unwind(&skip_libs);
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -308,7 +363,7 @@
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
 
   regs_.FakeSetPc(0x1000);
-  regs_.FakeSetSp(0x53000);
+  regs_.FakeSetSp(0x63000);
   ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x50020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
@@ -321,7 +376,7 @@
   EXPECT_EQ(0U, frame->num);
   EXPECT_EQ(0U, frame->rel_pc);
   EXPECT_EQ(0x1000U, frame->pc);
-  EXPECT_EQ(0x53000U, frame->sp);
+  EXPECT_EQ(0x63000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/system/fake/libc.so", frame->map_name);
@@ -503,6 +558,124 @@
   EXPECT_EQ(0, frame->map_flags);
 }
 
+// Verify that an unwind stops when a frame is in given suffix.
+TEST_F(UnwinderTest, map_ignore_suffixes) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
+
+  // Fake as if code called a nullptr function.
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x43402, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x53502, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  std::vector<std::string> suffixes{"oat"};
+  unwinder.Unwind(nullptr, &suffixes);
+
+  ASSERT_EQ(2U, unwinder.NumFrames());
+  // Make sure the elf was not initialized.
+  MapInfo* map_info = maps_.Find(0x53000);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_TRUE(map_info->elf == nullptr);
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x400U, frame->rel_pc);
+  EXPECT_EQ(0x43400U, frame->pc);
+  EXPECT_EQ(0x10010U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/fake/fake.apk", frame->map_name);
+  EXPECT_EQ(0x1d000U, frame->map_offset);
+  EXPECT_EQ(0x43000U, frame->map_start);
+  EXPECT_EQ(0x44000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify that an unwind stops when the sp and pc don't change.
+TEST_F(UnwinderTest, sp_pc_do_not_change) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame4", 4));
+
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x400U, frame->rel_pc);
+  EXPECT_EQ(0x33400U, frame->pc);
+  EXPECT_EQ(0x10010U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/fake/compressed.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x33000U, frame->map_start);
+  EXPECT_EQ(0x34000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0x500U, frame->rel_pc);
+  EXPECT_EQ(0x33500U, frame->pc);
+  EXPECT_EQ(0x10020U, frame->sp);
+  EXPECT_EQ("Frame2", frame->function_name);
+  EXPECT_EQ(2U, frame->function_offset);
+  EXPECT_EQ("/fake/compressed.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x33000U, frame->map_start);
+  EXPECT_EQ(0x34000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
 // Verify format frame code.
 TEST_F(UnwinderTest, format_frame_static) {
   FrameData frame;
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 66a9439..77f3bb2 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -53,7 +53,7 @@
       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()) {
+      if (interface->GetFunctionName(pc | 1, load_bias, &name, &func_offset) && !name.empty()) {
         printf(" <%s>", name.c_str());
       }
       printf("\n");
@@ -92,8 +92,7 @@
     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()) {
+    if (interface->GetFunctionName(fde->pc_start, load_bias, &name, &func_offset) && !name.empty()) {
       printf(" <%s>", name.c_str());
     }
     printf("\n");
@@ -115,7 +114,7 @@
   }
 
   Elf elf(memory);
-  if (!elf.Init() || !elf.valid()) {
+  if (!elf.Init(true) || !elf.valid()) {
     printf("%s is not a valid elf file.\n", file);
     return 1;
   }
@@ -128,7 +127,7 @@
 
   if (interface->eh_frame() != nullptr) {
     printf("eh_frame information:\n");
-    DumpDwarfSection(interface, interface->eh_frame(), interface->load_bias());
+    DumpDwarfSection(interface, interface->eh_frame(), elf.GetLoadBias());
     printf("\n");
   } else {
     printf("\nno eh_frame information\n");
@@ -136,7 +135,7 @@
 
   if (interface->debug_frame() != nullptr) {
     printf("\ndebug_frame information:\n");
-    DumpDwarfSection(interface, interface->debug_frame(), interface->load_bias());
+    DumpDwarfSection(interface, interface->debug_frame(), elf.GetLoadBias());
     printf("\n");
   } else {
     printf("\nno debug_frame information\n");
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index dc9ae5a..697e4cd 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -66,7 +66,7 @@
   }
 
   unwindstack::Elf elf(memory);
-  if (!elf.Init() || !elf.valid()) {
+  if (!elf.Init(true) || !elf.valid()) {
     printf("%s is not a valid elf file.\n", argv[1]);
     return 1;
   }
@@ -90,7 +90,7 @@
   }
 
   std::string name;
-  uint64_t load_bias = elf.interface()->load_bias();
+  uint64_t load_bias = elf.GetLoadBias();
   if (argc == 3) {
     std::string cur_name;
     uint64_t func_offset;
diff --git a/libusbhost/include/usbhost/usbhost.h b/libusbhost/include/usbhost/usbhost.h
index a8dd673..86cc873 100644
--- a/libusbhost/include/usbhost/usbhost.h
+++ b/libusbhost/include/usbhost/usbhost.h
@@ -137,8 +137,15 @@
 /* Returns the USB product ID from the device descriptor for the USB device */
 uint16_t usb_device_get_product_id(struct usb_device *device);
 
+/* Returns a pointer to device descriptor */
 const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device);
 
+/* Returns the length in bytes read into the raw descriptors array */
+size_t usb_device_get_descriptors_length(const struct usb_device* device);
+
+/* Returns a pointer to the raw descriptors array */
+const unsigned char* usb_device_get_raw_descriptors(const struct usb_device* device);
+
 /* Returns a USB descriptor string for the given string ID.
  * Used to implement usb_device_get_manufacturer_name,
  * usb_device_get_product_name and usb_device_get_serial.
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 44b878d..77c264b 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -80,9 +80,11 @@
     int                         wddbus;
 };
 
+#define MAX_DESCRIPTORS_LENGTH 4096
+
 struct usb_device {
     char dev_name[64];
-    unsigned char desc[4096];
+    unsigned char desc[MAX_DESCRIPTORS_LENGTH];
     int desc_length;
     int fd;
     int writeable;
@@ -381,6 +383,8 @@
     return device;
 
 failed:
+    // TODO It would be more appropriate to have callers do this
+    // since this function doesn't "own" this file descriptor.
     close(fd);
     free(device);
     return NULL;
@@ -449,11 +453,18 @@
     return __le16_to_cpu(desc->idProduct);
 }
 
-const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device)
-{
+const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device* device) {
     return (struct usb_device_descriptor*)device->desc;
 }
 
+size_t usb_device_get_descriptors_length(const struct usb_device* device) {
+    return device->desc_length;
+}
+
+const unsigned char* usb_device_get_raw_descriptors(const struct usb_device* device) {
+    return device->desc;
+}
+
 char* usb_device_get_string(struct usb_device *device, int id, int timeout)
 {
     char string[256];
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 1bf5a64..4bd2a98 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -75,7 +75,7 @@
         "misc.cpp",
     ],
 
-    cflags: ["-Werror"],
+    cflags: ["-Wall", "-Werror"],
     include_dirs: ["external/safe-iop/include"],
     header_libs: [
         "libutils_headers",
@@ -97,8 +97,6 @@
     target: {
         android: {
             srcs: [
-                "Looper.cpp",
-                "ProcessCallStack.cpp",
                 "Trace.cpp",
             ],
 
@@ -115,6 +113,12 @@
                 misc_undefined: ["integer"],
             },
         },
+        linux: {
+            srcs: [
+                "Looper.cpp",
+                "ProcessCallStack.cpp",
+            ],
+        },
 
         host: {
             cflags: ["-DLIBUTILS_NATIVE=1"],
@@ -124,18 +128,8 @@
             },
         },
 
-        linux_glibc: {
-            srcs: [
-                "Looper.cpp",
-                "ProcessCallStack.cpp",
-            ],
-        },
         linux_bionic: {
             enabled: true,
-            srcs: [
-                "Looper.cpp",
-                "ProcessCallStack.cpp",
-            ],
         },
 
         darwin: {
@@ -160,6 +154,7 @@
     static_libs: ["libutils"],
     shared_libs: ["liblog"],
     srcs: ["SharedBufferTest.cpp"],
+    cflags: ["-Wall", "-Werror"],
 }
 
 subdirs = ["tests"]
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index f5f881f..8bccb0f 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -410,8 +410,7 @@
         return;
     }
 
-    int32_t old = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
-            std::memory_order_relaxed);
+    int32_t old __unused = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE, std::memory_order_relaxed);
     // A decStrong() must still happen after us.
     ALOG_ASSERT(old > INITIAL_STRONG_VALUE, "0x%x too small", old);
     refs->mBase->onFirstRef();
diff --git a/libutils/StopWatch.cpp b/libutils/StopWatch.cpp
index 219c13c..d01865e 100644
--- a/libutils/StopWatch.cpp
+++ b/libutils/StopWatch.cpp
@@ -30,10 +30,7 @@
 
 namespace android {
 
-
-StopWatch::StopWatch(const char *name, int clock, uint32_t flags)
-    :   mName(name), mClock(clock), mFlags(flags)
-{
+StopWatch::StopWatch(const char* name, int clock) : mName(name), mClock(clock) {
     reset();
 }
 
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index 6cff0f4..1086831 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -305,23 +305,22 @@
 
 char16_t* strstr16(const char16_t* src, const char16_t* target)
 {
-    const char16_t needle = *target++;
-    const size_t target_len = strlen16(target);
-    if (needle != '\0') {
-      do {
+    const char16_t needle = *target;
+    if (needle == '\0') return (char16_t*)src;
+
+    const size_t target_len = strlen16(++target);
+    do {
         do {
-          if (*src == '\0') {
-            return nullptr;
-          }
+            if (*src == '\0') {
+                return nullptr;
+            }
         } while (*src++ != needle);
-      } while (strncmp16(src, target, target_len) != 0);
-      src--;
-    }
+    } while (strncmp16(src, target, target_len) != 0);
+    src--;
 
     return (char16_t*)src;
 }
 
-
 int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2)
 {
     const char16_t* e1 = s1+n1;
diff --git a/libutils/include/utils/StopWatch.h b/libutils/include/utils/StopWatch.h
index 693dd3c..76d78d0 100644
--- a/libutils/include/utils/StopWatch.h
+++ b/libutils/include/utils/StopWatch.h
@@ -29,21 +29,18 @@
 class StopWatch
 {
 public:
-        StopWatch(  const char *name,
-                    int clock = SYSTEM_TIME_MONOTONIC,
-                    uint32_t flags = 0);
-        ~StopWatch();
-        
-        const char* name() const;
-        nsecs_t     lap();
-        nsecs_t     elapsedTime() const;
+  StopWatch(const char* name, int clock = SYSTEM_TIME_MONOTONIC);
+  ~StopWatch();
 
-        void        reset();
-        
+  const char* name() const;
+  nsecs_t lap();
+  nsecs_t elapsedTime() const;
+
+  void reset();
+
 private:
     const char*     mName;
     int             mClock;
-    uint32_t        mFlags;
     
     struct lap_t {
         nsecs_t     soFar;
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
index 6911fc5..a3e7ffe 100644
--- a/libutils/tests/Android.bp
+++ b/libutils/tests/Android.bp
@@ -34,8 +34,6 @@
     target: {
         android: {
             srcs: [
-                "Looper_test.cpp",
-                "RefBase_test.cpp",
                 "SystemClock_test.cpp",
             ],
             shared_libs: [
@@ -46,7 +44,7 @@
                 "libbase",
             ],
         },
-        linux_glibc: {
+        linux: {
             srcs: [
                 "Looper_test.cpp",
                 "RefBase_test.cpp",
@@ -79,6 +77,7 @@
     host_supported: true,
     relative_install_path: "libutils_tests",
     srcs: ["Singleton_test1.cpp"],
+    cflags: ["-Wall", "-Werror"],
 }
 
 cc_test_library {
@@ -86,5 +85,6 @@
     host_supported: true,
     relative_install_path: "libutils_tests",
     srcs: ["Singleton_test2.cpp"],
+    cflags: ["-Wall", "-Werror"],
     shared_libs: ["libutils_tests_singleton1"],
 }
diff --git a/libutils/tests/Unicode_test.cpp b/libutils/tests/Unicode_test.cpp
index d23e43a..b92eef8 100644
--- a/libutils/tests/Unicode_test.cpp
+++ b/libutils/tests/Unicode_test.cpp
@@ -15,7 +15,11 @@
  */
 
 #define LOG_TAG "Unicode_test"
-#include <utils/Log.h>
+
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <log/log.h>
 #include <utils/Unicode.h>
 
 #include <gtest/gtest.h>
@@ -119,6 +123,31 @@
             << "should return the original pointer";
 }
 
+TEST_F(UnicodeTest, strstr16EmptyTarget_bug) {
+    // In the original code when target is an empty string strlen16() would
+    // start reading the memory until a "terminating null" (that is, zero)
+    // character is found.   This happens because "*target++" in the original
+    // code would increment the pointer beyond the actual string.
+    void* memptr;
+    const size_t alignment = sysconf(_SC_PAGESIZE);
+    const size_t size = 2 * alignment;
+    ASSERT_EQ(posix_memalign(&memptr, alignment, size), 0);
+    // Fill allocated memory.
+    memset(memptr, 'A', size);
+    // Create a pointer to an "empty" string on the first page.
+    char16_t* const emptyString = (char16_t* const)((char*)memptr + alignment - 4);
+    *emptyString = (char16_t)0;
+    // Protect the second page to show that strstr16() violates that.
+    ASSERT_EQ(mprotect((char*)memptr + alignment, alignment, PROT_NONE), 0);
+    // Test strstr16(): when bug is present a segmentation fault is raised.
+    ASSERT_EQ(strstr16((char16_t*)memptr, emptyString), (char16_t*)memptr)
+        << "should not read beyond the first char16_t.";
+    // Reset protection of the second page
+    ASSERT_EQ(mprotect((char*)memptr + alignment, alignment, PROT_READ | PROT_WRITE), 0);
+    // Free allocated memory.
+    free(memptr);
+}
+
 TEST_F(UnicodeTest, strstr16SameString) {
     const char16_t* result = strstr16(kSearchString, kSearchString);
     EXPECT_EQ(kSearchString, result)
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index b624223..fec79b7 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -3,6 +3,7 @@
 cc_library {
     name: "libvndksupport",
     srcs: ["linker.c"],
+    cflags: ["-Wall", "-Werror"],
     local_include_dirs: ["include/vndksupport"],
     export_include_dirs: ["include"],
     shared_libs: ["liblog"],
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
index d06cafc..bc5620b 100644
--- a/libvndksupport/linker.c
+++ b/libvndksupport/linker.c
@@ -21,7 +21,8 @@
 #define LOG_TAG "vndksupport"
 #include <log/log.h>
 
-extern struct android_namespace_t* android_get_exported_namespace(const char*);
+__attribute__((weak)) extern struct android_namespace_t* android_get_exported_namespace(const char*);
+__attribute__((weak)) extern void* android_dlopen_ext(const char*, int, const android_dlextinfo*);
 
 static const char* namespace_name = NULL;
 
@@ -31,7 +32,9 @@
     if (vendor_namespace == NULL) {
         int name_idx = 0;
         while (namespace_names[name_idx] != NULL) {
-            vendor_namespace = android_get_exported_namespace(namespace_names[name_idx]);
+            if (android_get_exported_namespace != NULL) {
+                vendor_namespace = android_get_exported_namespace(namespace_names[name_idx]);
+            }
             if (vendor_namespace != NULL) {
                 namespace_name = namespace_names[name_idx];
                 break;
@@ -48,7 +51,10 @@
         const android_dlextinfo dlextinfo = {
             .flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = vendor_namespace,
         };
-        void* handle = android_dlopen_ext(name, flag, &dlextinfo);
+        void* handle = NULL;
+        if (android_dlopen_ext != NULL) {
+            handle = android_dlopen_ext(name, flag, &dlextinfo);
+        }
         if (!handle) {
             ALOGE("Could not load %s from %s namespace: %s.", name, namespace_name, dlerror());
         }
diff --git a/libvndksupport/tests/Android.bp b/libvndksupport/tests/Android.bp
index 3587cf8..5b467f8 100644
--- a/libvndksupport/tests/Android.bp
+++ b/libvndksupport/tests/Android.bp
@@ -17,6 +17,7 @@
     srcs: [
         "linker_test.cpp",
     ],
+    cflags: ["-Wall", "-Werror"],
 
     host_supported: false,
     shared_libs: [
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index e3faee3..075fb86 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -86,19 +86,6 @@
     },
 }
 
-// Also provide libziparchive-host until everything is switched over to using libziparchive
-cc_library {
-    name: "libziparchive-host",
-    host_supported: true,
-    device_supported: false,
-    defaults: [
-        "libziparchive_defaults",
-        "libziparchive_flags",
-    ],
-    shared_libs: ["libz"],
-    static_libs: ["libutils"],
-}
-
 // Tests.
 cc_test {
     name: "ziparchive-tests",
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 73ae68d..018b1a9 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -230,4 +230,47 @@
                                 ProcessZipEntryFunction func, void* cookie);
 #endif
 
+namespace zip_archive {
+
+class Writer {
+ public:
+  virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
+  virtual ~Writer();
+
+ protected:
+  Writer() = default;
+
+ private:
+  Writer(const Writer&) = delete;
+  void operator=(const Writer&) = delete;
+};
+
+class Reader {
+ public:
+  virtual bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const = 0;
+  virtual ~Reader();
+
+ protected:
+  Reader() = default;
+
+ private:
+  Reader(const Reader&) = delete;
+  void operator=(const Reader&) = delete;
+};
+
+/*
+ * Inflates the first |compressed_length| bytes of |reader| to a given |writer|.
+ * |crc_out| is set to the CRC32 checksum of the uncompressed data.
+ *
+ * Returns 0 on success and negative values on failure, for example if |reader|
+ * cannot supply the right amount of data, or if the number of bytes written to
+ * data does not match |uncompressed_length|.
+ *
+ * If |crc_out| is not nullptr, it is set to the crc32 checksum of the
+ * uncompressed data.
+ */
+int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
+                const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out);
+}  // namespace zip_archive
+
 #endif  // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 1be4061..91e775f 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -741,22 +741,10 @@
   return kIterationEnd;
 }
 
-class Writer {
- public:
-  virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
-  virtual ~Writer() {}
-
- protected:
-  Writer() = default;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(Writer);
-};
-
 // A Writer that writes data to a fixed size memory region.
 // The size of the memory region must be equal to the total size of
 // the data appended to it.
-class MemoryWriter : public Writer {
+class MemoryWriter : public zip_archive::Writer {
  public:
   MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {}
 
@@ -780,7 +768,7 @@
 
 // A Writer that appends data to a file |fd| at its current position.
 // The file will be truncated to the end of the written data.
-class FileWriter : public Writer {
+class FileWriter : public zip_archive::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
@@ -811,7 +799,7 @@
       // disk does not have enough space.
       result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
       if (result == -1 && errno == ENOSPC) {
-        ALOGW("Zip: unable to allocate  %" PRId64 " bytes at offset %" PRId64 " : %s",
+        ALOGW("Zip: unable to allocate %" PRId64 " bytes at offset %" PRId64 ": %s",
               static_cast<int64_t>(declared_length), static_cast<int64_t>(current_offset),
               strerror(errno));
         return std::unique_ptr<FileWriter>(nullptr);
@@ -864,6 +852,22 @@
   size_t total_bytes_written_;
 };
 
+class EntryReader : public zip_archive::Reader {
+ public:
+  EntryReader(const MappedZipFile& zip_file, const ZipEntry* entry)
+      : Reader(), zip_file_(zip_file), entry_(entry) {}
+
+  virtual bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+    return zip_file_.ReadAtOffset(buf, len, entry_->offset + offset);
+  }
+
+  virtual ~EntryReader() {}
+
+ private:
+  const MappedZipFile& zip_file_;
+  const ZipEntry* entry_;
+};
+
 // This method is using libz macros with old-style-casts
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wold-style-cast"
@@ -872,8 +876,14 @@
 }
 #pragma GCC diagnostic pop
 
-static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
-                                    Writer* writer, uint64_t* crc_out) {
+namespace zip_archive {
+
+// Moved out of line to avoid -Wweak-vtables.
+Reader::~Reader() {}
+Writer::~Writer() {}
+
+int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
+                const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out) {
   const size_t kBufSize = 32768;
   std::vector<uint8_t> read_buf(kBufSize);
   std::vector<uint8_t> write_buf(kBufSize);
@@ -914,25 +924,24 @@
 
   std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
 
-  const uint32_t uncompressed_length = entry->uncompressed_length;
-
+  const bool compute_crc = (crc_out != nullptr);
   uint64_t crc = 0;
-  uint32_t compressed_length = entry->compressed_length;
+  uint32_t remaining_bytes = compressed_length;
   do {
     /* read as much as we can */
     if (zstream.avail_in == 0) {
-      const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
-      off64_t offset = entry->offset + (entry->compressed_length - compressed_length);
+      const size_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes;
+      const uint32_t offset = (compressed_length - remaining_bytes);
       // Make sure to read at offset to ensure concurrent access to the fd.
-      if (!mapped_zip.ReadAtOffset(read_buf.data(), getSize, offset)) {
-        ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
+      if (!reader.ReadAtOffset(read_buf.data(), read_size, offset)) {
+        ALOGW("Zip: inflate read failed, getSize = %zu: %s", read_size, strerror(errno));
         return kIoError;
       }
 
-      compressed_length -= getSize;
+      remaining_bytes -= read_size;
 
       zstream.next_in = &read_buf[0];
-      zstream.avail_in = getSize;
+      zstream.avail_in = read_size;
     }
 
     /* uncompress the data */
@@ -947,9 +956,8 @@
     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 {
+        return kIoError;
+      } else if (compute_crc) {
         crc = crc32(crc, &write_buf[0], write_size);
       }
 
@@ -966,9 +974,11 @@
   // 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 (compute_crc) {
+    *crc_out = crc;
+  }
 
-  if (zstream.total_out != uncompressed_length || compressed_length != 0) {
+  if (zstream.total_out != uncompressed_length || remaining_bytes != 0) {
     ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", zstream.total_out,
           uncompressed_length);
     return kInconsistentInformation;
@@ -976,9 +986,18 @@
 
   return 0;
 }
+}  // namespace zip_archive
 
-static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry, Writer* writer,
-                                 uint64_t* crc_out) {
+static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
+                                    zip_archive::Writer* writer, uint64_t* crc_out) {
+  const EntryReader reader(mapped_zip, entry);
+
+  return zip_archive::Inflate(reader, entry->compressed_length, entry->uncompressed_length, writer,
+                              crc_out);
+}
+
+static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
+                                 zip_archive::Writer* writer, uint64_t* crc_out) {
   static const uint32_t kBufSize = 32768;
   std::vector<uint8_t> buf(kBufSize);
 
@@ -1011,7 +1030,7 @@
   return 0;
 }
 
-int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, Writer* writer) {
+int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, zip_archive::Writer* writer) {
   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
   const uint16_t method = entry->method;
 
@@ -1041,12 +1060,12 @@
 }
 
 int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size) {
-  std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
+  std::unique_ptr<zip_archive::Writer> writer(new MemoryWriter(begin, size));
   return ExtractToWriter(handle, entry, writer.get());
 }
 
 int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd) {
-  std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
+  std::unique_ptr<zip_archive::Writer> writer(FileWriter::Create(fd, entry));
   if (writer.get() == nullptr) {
     return kIoError;
   }
@@ -1079,7 +1098,7 @@
 }
 
 #if !defined(_WIN32)
-class ProcessWriter : public Writer {
+class ProcessWriter : public zip_archive::Writer {
  public:
   ProcessWriter(ProcessZipEntryFunction func, void* cookie)
       : Writer(), proc_function_(func), cookie_(cookie) {}
@@ -1134,7 +1153,7 @@
 }
 
 // Attempts to read |len| bytes into |buf| at offset |off|.
-bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) {
+bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const {
   if (has_fd_) {
     if (!android::base::ReadFullyAtOffset(fd_, buf, len, off)) {
       ALOGE("Zip: failed to read at offset %" PRId64 "\n", off);
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 174aa3f..18e0229 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -106,7 +106,7 @@
 
   off64_t GetFileLength() const;
 
-  bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off);
+  bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const;
 
  private:
   // If has_fd_ is true, fd is valid and we'll read contents of a zip archive
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 753bd44..374310b 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -766,6 +766,93 @@
   ASSERT_EQ(-1, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle));
 }
 
+class VectorReader : public zip_archive::Reader {
+ public:
+  VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
+
+  bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+    if ((offset + len) < input_.size()) {
+      return false;
+    }
+
+    memcpy(buf, &input_[offset], len);
+    return true;
+  }
+
+ private:
+  const std::vector<uint8_t>& input_;
+};
+
+class VectorWriter : public zip_archive::Writer {
+ public:
+  VectorWriter() : Writer() {}
+
+  bool Append(uint8_t* buf, size_t size) {
+    output_.insert(output_.end(), buf, buf + size);
+    return true;
+  }
+
+  std::vector<uint8_t>& GetOutput() { return output_; }
+
+ private:
+  std::vector<uint8_t> output_;
+};
+
+class BadReader : public zip_archive::Reader {
+ public:
+  BadReader() : Reader() {}
+
+  bool ReadAtOffset(uint8_t*, size_t, uint32_t) const { return false; }
+};
+
+class BadWriter : public zip_archive::Writer {
+ public:
+  BadWriter() : Writer() {}
+
+  bool Append(uint8_t*, size_t) { return false; }
+};
+
+TEST(ziparchive, Inflate) {
+  const uint32_t compressed_length = kATxtContentsCompressed.size();
+  const uint32_t uncompressed_length = kATxtContents.size();
+
+  const VectorReader reader(kATxtContentsCompressed);
+  {
+    VectorWriter writer;
+    uint64_t crc_out = 0;
+
+    int32_t ret =
+        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, &crc_out);
+    ASSERT_EQ(0, ret);
+    ASSERT_EQ(kATxtContents, writer.GetOutput());
+    ASSERT_EQ(0x950821C5u, crc_out);
+  }
+
+  {
+    VectorWriter writer;
+    int32_t ret =
+        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
+    ASSERT_EQ(0, ret);
+    ASSERT_EQ(kATxtContents, writer.GetOutput());
+  }
+
+  {
+    BadWriter writer;
+    int32_t ret =
+        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
+    ASSERT_EQ(kIoError, ret);
+  }
+
+  {
+    BadReader reader;
+    VectorWriter writer;
+    int32_t ret =
+        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
+    ASSERT_EQ(kIoError, ret);
+    ASSERT_EQ(0u, writer.GetOutput().size());
+  }
+}
+
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
 
diff --git a/logcat/Android.bp b/logcat/Android.bp
index 729c8ff..afc7a01 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -67,6 +67,7 @@
     name: "logpersist.start",
     srcs: ["logpersist"],
     init_rc: ["logcatd.rc"],
+    required: ["logcatd"],
     symlinks: ["logpersist.stop", "logpersist.cat"],
     strip: {
         none: true,
diff --git a/logcat/event.logtags b/logcat/event.logtags
index efcc817..0983676 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -140,5 +140,8 @@
 
 1397638484 snet_event_log (subtag|3) (uid|1) (message|3)
 
+# for events that go to stats log buffer
+1937006964 stats_log (atom_id|1|5),(data|4)
+
 # NOTE - the range 1000000-2000000 is reserved for partners and others who
 # want to define their own log tags without conflicting with the core platform.
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 5ef220c..ff85f54 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -457,7 +457,7 @@
                     "  -d              Dump the log and then exit (don't block)\n"
                     "  -e <expr>, --regex=<expr>\n"
                     "                  Only print lines where the log message matches <expr>\n"
-                    "                  where <expr> is a regular expression\n"
+                    "                  where <expr> is a Perl-compatible regular expression\n"
                     // Leave --head undocumented as alias for -m
                     "  -m <count>, --max-count=<count>\n"
                     "                  Quit after printing <count> lines. This is meant to be\n"
@@ -1832,11 +1832,10 @@
     }
     android::close_output(context);
     android::close_error(context);
+
     if (context->fds[1] >= 0) {
-        // NB: could be closed by the above fclose(s), ignore error.
-        int save_errno = errno;
+        // NB: this should be closed by close_output, but just in case...
         close(context->fds[1]);
-        errno = save_errno;
         context->fds[1] = -1;
     }
 
diff --git a/logcat/logcat_system.cpp b/logcat/logcat_system.cpp
index ea393bd..6dfd110 100644
--- a/logcat/logcat_system.cpp
+++ b/logcat/logcat_system.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <ctype.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
@@ -98,8 +99,12 @@
         return NULL;
     }
 
-    FILE* retval = fdopen(fd, "reb");
-    if (!retval) android_logcat_destroy(ctx);
+    int duped = dup(fd);
+    FILE* retval = fdopen(duped, "reb");
+    if (!retval) {
+        close(duped);
+        android_logcat_destroy(ctx);
+    }
     return retval;
 }
 
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 7498325..43362fb 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -212,13 +212,19 @@
     if (log_id != LOG_ID_SECURITY) {
         int prio = ANDROID_LOG_INFO;
         const char* tag = nullptr;
+        size_t tag_len = 0;
         if (log_id == LOG_ID_EVENTS) {
             tag = tagToName(elem->getTag());
+            if (tag) {
+                tag_len = strlen(tag);
+            }
         } else {
             prio = *msg;
             tag = msg + 1;
+            tag_len = strnlen(tag, len - 1);
         }
-        if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+        if (!__android_log_is_loggable_len(prio, tag, tag_len,
+                                           ANDROID_LOG_VERBOSE)) {
             // Log traffic received to total
             wrlock();
             stats.addTotal(elem);
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index 0bd4008..14b45b1 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -43,9 +43,10 @@
         name_set = true;
     }
 
+    // + 1 to ensure null terminator if MAX_PAYLOAD buffer is received
     char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time) +
-                LOGGER_ENTRY_MAX_PAYLOAD];
-    struct iovec iov = { buffer, sizeof(buffer) };
+                LOGGER_ENTRY_MAX_PAYLOAD + 1];
+    struct iovec iov = { buffer, sizeof(buffer) - 1 };
 
     alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
     struct msghdr hdr = {
@@ -55,13 +56,16 @@
     int socket = cli->getSocket();
 
     // To clear the entire buffer is secure/safe, but this contributes to 1.68%
-    // overhead under logging load. We are safe because we check counts.
+    // overhead under logging load. We are safe because we check counts, but
+    // still need to clear null terminator
     // memset(buffer, 0, sizeof(buffer));
     ssize_t n = recvmsg(socket, &hdr, 0);
     if (n <= (ssize_t)(sizeof(android_log_header_t))) {
         return false;
     }
 
+    buffer[n] = 0;
+
     struct ucred* cred = NULL;
 
     struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index fcd45bd..ff7e762 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -105,7 +105,7 @@
                 android::base::WriteStringToFd(
                     formatEntry_locked(it.first, AID_ROOT), fd);
             }
-            TEMP_FAILURE_RETRY(close(fd));
+            close(fd);
         }
     }
 
@@ -601,7 +601,7 @@
 
     std::string ret = formatEntry_locked(tag, uid, false);
     android::base::WriteStringToFd(ret, fd);
-    TEMP_FAILURE_RETRY(close(fd));
+    close(fd);
 
     size_t size = 0;
     file2watermark_const_iterator iwater;
@@ -625,7 +625,7 @@
 
     std::string ret = formatEntry_locked(tag, uid, false);
     android::base::WriteStringToFd(ret, fd);
-    TEMP_FAILURE_RETRY(close(fd));
+    close(fd);
 
     size_t size = 0;
     file2watermark_const_iterator iwater;
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 860c1f1..560092e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -62,7 +62,6 @@
   cameraserver \
   cnd \
   debuggerd \
-  debuggerd64 \
   dex2oat \
   drmserver \
   fingerprintd \
@@ -242,7 +241,7 @@
 else # if _enforce_vndk_at_runtime is not true
 
 LOCAL_MODULE := ld.config.txt
-ifeq ($(PRODUCT_FULL_TREBLE)|$(SANITIZE_TARGET),true|)
+ifeq ($(PRODUCT_TREBLE_LINKER_NAMESPACES)|$(SANITIZE_TARGET),true|)
 LOCAL_SRC_FILES := etc/ld.config.txt
 else
 LOCAL_SRC_FILES := etc/ld.config.legacy.txt
diff --git a/rootdir/etc/ld.config.txt.in b/rootdir/etc/ld.config.txt.in
index 394f0e1..1b274d6 100644
--- a/rootdir/etc/ld.config.txt.in
+++ b/rootdir/etc/ld.config.txt.in
@@ -51,7 +51,7 @@
 namespace.sphal.isolated = true
 namespace.sphal.visible = true
 namespace.sphal.search.paths = /vendor/${LIB}/egl:/vendor/${LIB}/hw:/vendor/${LIB}
-namespace.sphal.permitted.paths = /vendor/${LIB}:/system/${LIB}/vndk-sp/hw
+namespace.sphal.permitted.paths = /vendor/${LIB}:/system/${LIB}/vndk-sp${VNDK_VER}/hw
 
 namespace.sphal.asan.search.paths = /data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}:/vendor/${LIB}
 namespace.sphal.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}
@@ -79,10 +79,10 @@
 ###############################################################################
 namespace.rs.isolated = true
 namespace.rs.visible = true
-namespace.rs.search.paths = /vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/vendor/${LIB}
+namespace.rs.search.paths = /vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}
 namespace.rs.permitted.paths = /vendor/${LIB}:/data
 
-namespace.rs.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/vendor/${LIB}:/vendor/${LIB}
+namespace.rs.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/data/asan/vendor/${LIB}:/vendor/${LIB}
 namespace.rs.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data
 
 namespace.rs.links = default,vndk
@@ -96,10 +96,10 @@
 ###############################################################################
 namespace.vndk.isolated = true
 namespace.vndk.visible = true
-namespace.vndk.search.paths = /vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp
+namespace.vndk.search.paths = /vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
 namespace.vndk.permitted.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl
 
-namespace.vndk.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
 namespace.vndk.asan.permitted.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl
 
 # When these NDK libs are required inside this namespace, then it is redirected
@@ -125,11 +125,11 @@
 namespace.default.isolated = true
 namespace.default.visible = true
 
-namespace.default.search.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl:/vendor/${LIB}:/vendor/${LIB}/vndk:/system/${LIB}/vndk:/vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp
-namespace.default.permitted.paths = /vendor:/system/${LIB}/vndk:/system/${LIB}/vndk-sp
+namespace.default.search.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl:/vendor/${LIB}:/vendor/${LIB}/vndk${VNDK_VER}:/system/${LIB}/vndk${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.permitted.paths = /vendor:/system/${LIB}/vndk${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
 
-namespace.default.asan.search.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/vendor/${LIB}/vndk:/vendor/${LIB}/vndk:/data/asan/system/${LIB}/vndk:/system/${LIB}/vndk:/data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp
-namespace.default.asan.permitted.paths = /data/asan/vendor:/vendor:/data/asan/system/${LIB}/vndk:/system/${LIB}/vndk:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp
+namespace.default.asan.search.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/vendor/${LIB}/vndk${VNDK_VER}:/vendor/${LIB}/vndk${VNDK_VER}:/data/asan/system/${LIB}/vndk${VNDK_VER}:/system/${LIB}/vndk${VNDK_VER}:/data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.asan.permitted.paths = /data/asan/vendor:/vendor:/data/asan/system/${LIB}/vndk${VNDK_VER}:/system/${LIB}/vndk${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
 
 namespace.default.links = system
 namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 89f24cb..a3213cd 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -169,9 +169,6 @@
     mkdir /dev/cpuset/foreground
     copy /dev/cpuset/cpus /dev/cpuset/foreground/cpus
     copy /dev/cpuset/mems /dev/cpuset/foreground/mems
-    mkdir /dev/cpuset/foreground/boost
-    copy /dev/cpuset/cpus /dev/cpuset/foreground/boost/cpus
-    copy /dev/cpuset/mems /dev/cpuset/foreground/boost/mems
     mkdir /dev/cpuset/background
     copy /dev/cpuset/cpus /dev/cpuset/background/cpus
     copy /dev/cpuset/mems /dev/cpuset/background/mems
@@ -190,13 +187,11 @@
     # change permissions for all cpusets we'll touch at runtime
     chown system system /dev/cpuset
     chown system system /dev/cpuset/foreground
-    chown system system /dev/cpuset/foreground/boost
     chown system system /dev/cpuset/background
     chown system system /dev/cpuset/system-background
     chown system system /dev/cpuset/top-app
     chown system system /dev/cpuset/tasks
     chown system system /dev/cpuset/foreground/tasks
-    chown system system /dev/cpuset/foreground/boost/tasks
     chown system system /dev/cpuset/background/tasks
     chown system system /dev/cpuset/system-background/tasks
     chown system system /dev/cpuset/top-app/tasks
@@ -205,7 +200,6 @@
     chmod 0775 /dev/cpuset/system-background
 
     chmod 0664 /dev/cpuset/foreground/tasks
-    chmod 0664 /dev/cpuset/foreground/boost/tasks
     chmod 0664 /dev/cpuset/background/tasks
     chmod 0664 /dev/cpuset/system-background/tasks
     chmod 0664 /dev/cpuset/top-app/tasks
@@ -439,6 +433,7 @@
     mkdir /data/misc/vold 0700 root root
     mkdir /data/misc/boottrace 0771 system shell
     mkdir /data/misc/update_engine 0700 root root
+    mkdir /data/misc/update_engine_log 02750 root log
     mkdir /data/misc/trace 0700 root root
     # profile file layout
     mkdir /data/misc/profiles 0771 system system
@@ -717,7 +712,7 @@
     shutdown critical
 
 service healthd /system/bin/healthd
-    class core
+    class hal
     critical
     group root system wakelock
 
diff --git a/sdcard/fuse.cpp b/sdcard/fuse.cpp
index 95559d7..10d0f04 100644
--- a/sdcard/fuse.cpp
+++ b/sdcard/fuse.cpp
@@ -323,7 +323,7 @@
 
     /* Root always has access; access for any other UIDs should always
      * be controlled through packages.list. */
-    if (hdr->uid == 0) {
+    if (hdr->uid == AID_ROOT) {
         return true;
     }
 
diff --git a/storaged/Android.bp b/storaged/Android.bp
index 6aef8c8..35868bb 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -18,10 +18,14 @@
     name: "storaged_defaults",
 
     shared_libs: [
+        "android.hardware.health@1.0",
+        "android.hardware.health@2.0",
         "libbase",
-        "libbatteryservice",
         "libbinder",
         "libcutils",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
         "liblog",
         "libprotobuf-cpp-lite",
         "libsysutils",
@@ -42,6 +46,12 @@
 
     defaults: ["storaged_defaults"],
 
+    aidl: {
+        export_aidl_headers: true,
+        local_include_dirs: ["binder"],
+        include_dirs: ["frameworks/native/aidl/binder"],
+    },
+
     srcs: [
         "storaged.cpp",
         "storaged_diskstats.cpp",
@@ -49,7 +59,10 @@
         "storaged_service.cpp",
         "storaged_utils.cpp",
         "storaged_uid_monitor.cpp",
+        "uid_info.cpp",
         "storaged.proto",
+        "binder/android/os/IStoraged.aidl",
+        "binder/android/os/storaged/IStoragedPrivate.aidl",
     ],
 
     logtags: ["EventLogTags.logtags"],
@@ -86,4 +99,4 @@
     srcs: ["tests/storaged_test.cpp"],
 
     static_libs: ["libstoraged"],
-}
\ No newline at end of file
+}
diff --git a/storaged/binder/android/os/IStoraged.aidl b/storaged/binder/android/os/IStoraged.aidl
new file mode 100644
index 0000000..f81e904
--- /dev/null
+++ b/storaged/binder/android/os/IStoraged.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+/** {@hide} */
+interface IStoraged {
+    void onUserStarted(int userId);
+    void onUserStopped(int userId);
+}
\ No newline at end of file
diff --git a/storaged/binder/android/os/storaged/IStoragedPrivate.aidl b/storaged/binder/android/os/storaged/IStoragedPrivate.aidl
new file mode 100644
index 0000000..9c888e3
--- /dev/null
+++ b/storaged/binder/android/os/storaged/IStoragedPrivate.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package android.os.storaged;
+
+import android.os.storaged.UidInfo;
+
+/** {@hide} */
+interface IStoragedPrivate {
+    UidInfo[] dumpUids();
+    int[] dumpPerfHistory();
+}
\ No newline at end of file
diff --git a/storaged/binder/android/os/storaged/UidInfo.aidl b/storaged/binder/android/os/storaged/UidInfo.aidl
new file mode 100644
index 0000000..440f386
--- /dev/null
+++ b/storaged/binder/android/os/storaged/UidInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+package android.os.storaged;
+
+parcelable UidInfo cpp_header "include/uid_info.h";
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index f5c78f9..e5dd70d 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -27,7 +27,9 @@
 #include <vector>
 
 #include <batteryservice/IBatteryPropertiesListener.h>
-#include <batteryservice/IBatteryPropertiesRegistrar.h>
+#include <utils/Mutex.h>
+
+#include <android/hardware/health/2.0/IHealth.h>
 
 #define FRIEND_TEST(test_case_name, test_name) \
 friend class test_case_name##_##test_name##_Test
@@ -48,6 +50,7 @@
 #include "storaged_info.h"
 #include "storaged_uid_monitor.h"
 #include "storaged.pb.h"
+#include "uid_info.h"
 
 using namespace std;
 using namespace android;
@@ -56,8 +59,8 @@
 #define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )
 #define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )
 #define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO ( 3600 )
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT (300)
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO (3600)
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT ( 300 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO ( 3600 )
 
 // UID IO threshold in bytes
 #define DEFAULT_PERIODIC_CHORES_UID_IO_THRESHOLD ( 1024 * 1024 * 1024ULL )
@@ -70,22 +73,28 @@
     int event_time_check_usec;  // check how much cputime spent in event loop
 };
 
-class storaged_t : public BnBatteryPropertiesListener,
-                   public IBinder::DeathRecipient {
-private:
+class storaged_t : public android::hardware::health::V2_0::IHealthInfoCallback,
+                   public android::hardware::hidl_death_recipient {
+  private:
     time_t mTimer;
     storaged_config mConfig;
     disk_stats_monitor mDsm;
     uid_monitor mUidm;
     time_t mStarttime;
-    sp<IBatteryPropertiesRegistrar> battery_properties;
+    sp<android::hardware::health::V2_0::IHealth> health;
     unique_ptr<storage_info_t> storage_info;
     static const uint32_t crc_init;
-    static const string proto_file;
-    storaged_proto::StoragedProto proto;
+    unordered_map<userid_t, bool> proto_loaded;
+    void load_proto(userid_t user_id);
+    void prepare_proto(userid_t user_id, StoragedProto* proto);
+    void flush_proto(userid_t user_id, StoragedProto* proto);
+    void flush_proto_user_system(StoragedProto* proto);
+    string proto_path(userid_t user_id) {
+        return string("/data/misc_ce/") + to_string(user_id) +
+               "/storaged/storaged.proto";
+    }
 public:
     storaged_t(void);
-    ~storaged_t() {}
     void event(void);
     void event_checked(void);
     void pause(void) {
@@ -96,33 +105,36 @@
         return mStarttime;
     }
 
-    unordered_map<uint32_t, struct uid_info> get_uids(void) {
+    unordered_map<uint32_t, uid_info> get_uids(void) {
         return mUidm.get_uid_io_stats();
     }
 
-    vector<vector<uint32_t>> get_perf_history(void) {
+    vector<int> get_perf_history(void) {
         return storage_info->get_perf_history();
     }
 
     map<uint64_t, struct uid_records> get_uid_records(
             double hours, uint64_t threshold, bool force_report) {
-        return mUidm.dump(hours, threshold, force_report,
-                          proto.mutable_uid_io_usage());
+        return mUidm.dump(hours, threshold, force_report);
     }
+
     void update_uid_io_interval(int interval) {
         if (interval >= DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT) {
             mConfig.periodic_chores_interval_uid_io = interval;
         }
     }
 
-    void init_battery_service();
-    virtual void batteryPropertiesChanged(struct BatteryProperties props);
-    void binderDied(const wp<IBinder>& who);
+    void add_user_ce(userid_t user_id);
+    void remove_user_ce(userid_t user_id);
+
+    void init_health_service();
+    virtual ::android::hardware::Return<void> healthInfoChanged(
+        const ::android::hardware::health::V1_0::HealthInfo& info);
+    void serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who);
 
     void report_storage_info();
 
-    void load_proto();
-    void flush_proto();
+    void flush_protos(unordered_map<int, StoragedProto>* protos);
 };
 
 // Eventlog tag
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h
index 8b07862..b1efac2 100644
--- a/storaged/include/storaged_info.h
+++ b/storaged/include/storaged_info.h
@@ -21,6 +21,8 @@
 
 #include <chrono>
 
+#include <utils/Mutex.h>
+
 #include "storaged.h"
 #include "storaged.pb.h"
 
@@ -28,6 +30,7 @@
 friend class test_case_name##_##test_name##_Test
 
 using namespace std;
+using namespace android;
 using namespace chrono;
 using namespace storaged_proto;
 
@@ -51,13 +54,12 @@
     uint32_t nr_days;
     vector<uint32_t> weekly_perf;
     uint32_t nr_weeks;
-    sem_t si_lock;
+    Mutex si_mutex;
 
     storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0),
         userdata_total_kb(0), userdata_free_kb(0), nr_samples(0),
         daily_perf(WEEK_TO_DAYS, 0), nr_days(0),
         weekly_perf(YEAR_TO_WEEKS, 0), nr_weeks(0) {
-            sem_init(&si_lock, 0, 1);
             day_start_tp = system_clock::now();
             day_start_tp -= chrono::seconds(duration_cast<chrono::seconds>(
                 day_start_tp.time_since_epoch()).count() % DAY_TO_SEC);
@@ -66,13 +68,13 @@
     storage_info_t* s_info;
 public:
     static storage_info_t* get_storage_info();
-    virtual ~storage_info_t() { sem_destroy(&si_lock); }
+    virtual ~storage_info_t() {};
     virtual void report() {};
-    void init(const IOPerfHistory& perf_history);
+    void load_perf_history_proto(const IOPerfHistory& perf_history);
     void refresh(IOPerfHistory* perf_history);
     void update_perf_history(uint32_t bw,
                              const time_point<system_clock>& tp);
-    vector<vector<uint32_t>> get_perf_history(void);
+    vector<int> get_perf_history();
 };
 
 class emmc_info_t : public storage_info_t {
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
index b7fe5b8..05c3b94 100644
--- a/storaged/include/storaged_service.h
+++ b/storaged/include/storaged_service.h
@@ -19,47 +19,37 @@
 
 #include <vector>
 
-#include <binder/IInterface.h>
-#include <binder/IBinder.h>
+#include <binder/BinderService.h>
 
-#include "storaged.h"
+#include "android/os/BnStoraged.h"
+#include "android/os/storaged/BnStoragedPrivate.h"
 
 using namespace std;
-using namespace android;
+using namespace android::os;
+using namespace android::os::storaged;
 
-// Interface
-class IStoraged : public IInterface {
+class StoragedService : public BinderService<StoragedService>, public BnStoraged {
+private:
+    void dumpUidRecordsDebug(int fd, const vector<struct uid_record>& entries);
+    void dumpUidRecords(int fd, const vector<struct uid_record>& entries);
 public:
-    enum {
-        DUMPUIDS = IBinder::FIRST_CALL_TRANSACTION,
-        DUMPPERF,
-    };
-    // Request the service to run the test function
-    virtual vector<struct uid_info> dump_uids(const char* option) = 0;
-    virtual vector<vector<uint32_t>> dump_perf_history(const char* option) = 0;
+    static status_t start();
+    static char const* getServiceName() { return "storaged"; }
+    virtual status_t dump(int fd, const Vector<String16> &args) override;
 
-    DECLARE_META_INTERFACE(Storaged);
+    binder::Status onUserStarted(int32_t userId);
+    binder::Status onUserStopped(int32_t userId);
 };
 
-// Client
-class BpStoraged : public BpInterface<IStoraged> {
+class StoragedPrivateService : public BinderService<StoragedPrivateService>, public BnStoragedPrivate {
 public:
-    BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
-    virtual vector<struct uid_info> dump_uids(const char* option);
-    virtual vector<vector<uint32_t>> dump_perf_history(const char* option);
+    static status_t start();
+    static char const* getServiceName() { return "storaged_pri"; }
+
+    binder::Status dumpUids(vector<UidInfo>* _aidl_return);
+    binder::Status dumpPerfHistory(vector<int32_t>* _aidl_return);
 };
 
-// Server
-class BnStoraged : public BnInterface<IStoraged> {
-    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
-};
-
-class Storaged : public BnStoraged {
-    virtual vector<struct uid_info> dump_uids(const char* option);
-    virtual vector<vector<uint32_t>> dump_perf_history(const char* option);
-    virtual status_t dump(int fd, const Vector<String16>& args);
-};
-
-sp<IStoraged> get_storaged_service();
+sp<IStoragedPrivate> get_storaged_pri_service();
 
 #endif /* _STORAGED_SERVICE_H_ */
\ No newline at end of file
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
index d2c7105..3a718fa 100644
--- a/storaged/include/storaged_uid_monitor.h
+++ b/storaged/include/storaged_uid_monitor.h
@@ -23,128 +23,103 @@
 #include <unordered_map>
 #include <vector>
 
+#include <cutils/multiuser.h>
+#include <utils/Mutex.h>
+
 #include "storaged.pb.h"
+#include "uid_info.h"
 
+#define FRIEND_TEST(test_case_name, test_name) \
+friend class test_case_name##_##test_name##_Test
+
+using namespace std;
 using namespace storaged_proto;
+using namespace android;
+using namespace android::os::storaged;
 
-enum uid_stat_t {
-    FOREGROUND = 0,
-    BACKGROUND = 1,
-    UID_STATS = 2
+class uid_info : public UidInfo {
+public:
+    bool parse_uid_io_stats(string&& s);
 };
 
-enum charger_stat_t {
-    CHARGER_OFF = 0,
-    CHARGER_ON = 1,
-    CHARGER_STATS = 2
-};
-
-enum io_type_t {
-    READ = 0,
-    WRITE = 1,
-    IO_TYPES = 2
-};
-
-struct io_stats {
-    uint64_t rchar;                 // characters read
-    uint64_t wchar;                 // characters written
-    uint64_t read_bytes;            // bytes read (from storage layer)
-    uint64_t write_bytes;           // bytes written (to storage layer)
-    uint64_t fsync;                 // number of fsync syscalls
-};
-
-struct task_info {
-    std::string comm;
-    pid_t pid;
-    struct io_stats io[UID_STATS];
-    bool parse_task_io_stats(std::string&& s);
-};
-
-struct uid_info {
-    uint32_t uid;                   // user id
-    std::string name;               // package name
-    struct io_stats io[UID_STATS];    // [0]:foreground [1]:background
-    std::unordered_map<uint32_t, task_info> tasks; // mapped from pid
-    bool parse_uid_io_stats(std::string&& s);
-};
-
-struct io_usage {
+class io_usage {
+public:
+    io_usage() : bytes{{{0}}} {};
     uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];
     bool is_zero() const;
+    io_usage& operator+= (const io_usage& stats) {
+        for (int i = 0; i < IO_TYPES; i++) {
+            for (int j = 0; j < UID_STATS; j++) {
+                for (int k = 0; k < CHARGER_STATS; k++) {
+                    bytes[i][j][k] += stats.bytes[i][j][k];
+                }
+            }
+        }
+        return *this;
+    }
 };
 
 struct uid_io_usage {
-    struct io_usage uid_ios;
+    userid_t user_id;
+    io_usage uid_ios;
     // mapped from task comm to task io usage
-    std::map<std::string, struct io_usage> task_ios;
+    map<string, io_usage> task_ios;
 };
 
 struct uid_record {
-    std::string name;
+    string name;
     struct uid_io_usage ios;
 };
 
 struct uid_records {
     uint64_t start_ts;
-    std::vector<struct uid_record> entries;
-};
-
-class lock_t {
-    sem_t* mSem;
-public:
-    lock_t(sem_t* sem) {
-        mSem = sem;
-        sem_wait(mSem);
-    }
-    ~lock_t() {
-        sem_post(mSem);
-    }
+    vector<struct uid_record> entries;
 };
 
 class uid_monitor {
 private:
+    FRIEND_TEST(storaged_test, uid_monitor);
     // last dump from /proc/uid_io/stats, uid -> uid_info
-    std::unordered_map<uint32_t, struct uid_info> last_uid_io_stats;
+    unordered_map<uint32_t, uid_info> last_uid_io_stats;
     // current io usage for next report, app name -> uid_io_usage
-    std::unordered_map<std::string, struct uid_io_usage> curr_io_stats;
+    unordered_map<string, struct uid_io_usage> curr_io_stats;
     // io usage records, end timestamp -> {start timestamp, vector of records}
-    std::map<uint64_t, struct uid_records> io_history;
+    map<uint64_t, struct uid_records> io_history;
     // charger ON/OFF
     charger_stat_t charger_stat;
     // protects curr_io_stats, last_uid_io_stats, records and charger_stat
-    sem_t um_lock;
+    Mutex uidm_mutex;
     // start time for IO records
     uint64_t start_ts;
     // true if UID_IO_STATS_PATH is accessible
     const bool enable;
 
     // reads from /proc/uid_io/stats
-    std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats_locked();
+    unordered_map<uint32_t, uid_info> get_uid_io_stats_locked();
     // flushes curr_io_stats to records
     void add_records_locked(uint64_t curr_ts);
     // updates curr_io_stats and set last_uid_io_stats
     void update_curr_io_stats_locked();
-    // restores io_history from protobuf
-    void load_uid_io_proto(const UidIOUsage& proto);
     // writes io_history to protobuf
-    void update_uid_io_proto(UidIOUsage* proto);
+    void update_uid_io_proto(unordered_map<int, StoragedProto>* protos);
 
 public:
     uid_monitor();
-    ~uid_monitor();
     // called by storaged main thread
-    void init(charger_stat_t stat, const UidIOUsage& proto);
+    void init(charger_stat_t stat);
     // called by storaged -u
-    std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats();
+    unordered_map<uint32_t, uid_info> get_uid_io_stats();
     // called by dumpsys
-    std::map<uint64_t, struct uid_records> dump(
-        double hours, uint64_t threshold, bool force_report,
-        UidIOUsage* uid_io_proto);
+    map<uint64_t, struct uid_records> dump(
+        double hours, uint64_t threshold, bool force_report);
     // called by battery properties listener
     void set_charger_state(charger_stat_t stat);
     // called by storaged periodic_chore or dump with force_report
     bool enabled() { return enable; };
-    void report(UidIOUsage* proto);
+    void report(unordered_map<int, StoragedProto>* protos);
+    // restores io_history from protobuf
+    void load_uid_io_proto(const UidIOUsage& proto);
+    void clear_user_history(userid_t user_id);
 };
 
 #endif /* _STORAGED_UID_MONITOR_H_ */
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
index 3b595b7..62cb12d 100644
--- a/storaged/include/storaged_utils.h
+++ b/storaged/include/storaged_utils.h
@@ -24,6 +24,8 @@
 
 #include "storaged.h"
 
+using namespace android::os::storaged;
+
 // Diskstats
 bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats);
 struct disk_perf get_disk_perf(struct disk_stats* stats);
@@ -31,10 +33,11 @@
 void add_disk_stats(struct disk_stats* src, struct disk_stats* dst);
 
 // UID I/O
-void sort_running_uids_info(std::vector<struct uid_info> &uids);
+map<string, io_usage> merge_io_usage(const vector<uid_record>& entries);
+void sort_running_uids_info(std::vector<UidInfo> &uids);
 
 // Logging
-void log_console_running_uids_info(const std::vector<struct uid_info>& uids, bool flag_dump_task);
-void log_console_perf_history(const vector<vector<uint32_t>>& perf_history);
+void log_console_running_uids_info(const std::vector<UidInfo>& uids, bool flag_dump_task);
+void log_console_perf_history(const vector<int>& perf_history);
 
 #endif /* _STORAGED_UTILS_H_ */
diff --git a/storaged/include/uid_info.h b/storaged/include/uid_info.h
new file mode 100644
index 0000000..4398a0d
--- /dev/null
+++ b/storaged/include/uid_info.h
@@ -0,0 +1,75 @@
+/*
+ * 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 _UID_INFO_H_
+#define _UID_INFO_H_
+
+#include <string>
+#include <unordered_map>
+
+namespace android {
+namespace os {
+namespace storaged {
+
+enum uid_stat_t {
+    FOREGROUND = 0,
+    BACKGROUND = 1,
+    UID_STATS = 2
+};
+
+enum charger_stat_t {
+    CHARGER_OFF = 0,
+    CHARGER_ON = 1,
+    CHARGER_STATS = 2
+};
+
+enum io_type_t {
+    READ = 0,
+    WRITE = 1,
+    IO_TYPES = 2
+};
+
+struct io_stats {
+    uint64_t rchar;                 // characters read
+    uint64_t wchar;                 // characters written
+    uint64_t read_bytes;            // bytes read (from storage layer)
+    uint64_t write_bytes;           // bytes written (to storage layer)
+    uint64_t fsync;                 // number of fsync syscalls
+};
+
+class task_info {
+public:
+    std::string comm;
+    pid_t pid;
+    io_stats io[UID_STATS];
+    bool parse_task_io_stats(std::string&& s);
+};
+
+class UidInfo : public Parcelable {
+public:
+    uint32_t uid;                     // user id
+    std::string name;                 // package name
+    io_stats io[UID_STATS];           // [0]:foreground [1]:background
+    std::unordered_map<uint32_t, task_info> tasks; // mapped from pid
+
+    status_t writeToParcel(Parcel* parcel) const override;
+    status_t readFromParcel(const Parcel* parcel) override;
+};
+
+} // namespace storaged
+} // namespace os
+} // namespace android
+
+#endif /*  _UID_INFO_H_ */
\ No newline at end of file
diff --git a/storaged/main.cpp b/storaged/main.cpp
index adc550a..c1b1329 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -43,22 +43,22 @@
 #include <storaged_utils.h>
 
 using namespace std;
+using namespace android;
 
-sp<storaged_t> storaged;
+sp<storaged_t> storaged_sp;
 
 // Function of storaged's main thread
 void* storaged_main(void* /* unused */) {
-    storaged = new storaged_t();
+    storaged_sp = new storaged_t();
 
-    storaged->load_proto();
-    storaged->init_battery_service();
-    storaged->report_storage_info();
+    storaged_sp->init_health_service();
+    storaged_sp->report_storage_info();
 
     LOG_TO(SYSTEM, INFO) << "storaged: Start";
 
     for (;;) {
-        storaged->event_checked();
-        storaged->pause();
+        storaged_sp->event_checked();
+        storaged_sp->pause();
     }
     return NULL;
 }
@@ -82,39 +82,33 @@
     for (;;) {
         int opt_idx = 0;
         static struct option long_options[] = {
-            {"start",       no_argument,        0, 's'},
-            {"kill",        no_argument,        0, 'k'},
-            {"uid",         no_argument,        0, 'u'},
-            {"task",        no_argument,        0, 't'},
-            {"perf",        no_argument,        0, 'p'},
-            {"help",        no_argument,        0, 'h'}
+            {"perf",        no_argument,    nullptr, 'p'},
+            {"start",       no_argument,    nullptr, 's'},
+            {"task",        no_argument,    nullptr, 't'},
+            {"uid",         no_argument,    nullptr, 'u'},
+            {nullptr,       0,              nullptr,  0}
         };
-        opt = getopt_long(argc, argv, ":skhutp", long_options, &opt_idx);
+        opt = getopt_long(argc, argv, ":pstu", long_options, &opt_idx);
         if (opt == -1) {
             break;
         }
 
         switch (opt) {
+        case 'p':
+            flag_dump_perf = true;
+            break;
         case 's':
             flag_main_service = true;
             break;
-        case 'u':
-            flag_dump_uid = true;
-            break;
         case 't':
             flag_dump_task = true;
             break;
-        case 'p':
-            flag_dump_perf = true;
+        case 'u':
+            flag_dump_uid = true;
             break;
-        case 'h':
+        default:
             help_message();
             return 0;
-        case '?':
-        default:
-            fprintf(stderr, "no supported option\n");
-            help_message();
-            return -1;
         }
     }
 
@@ -137,7 +131,12 @@
             return -1;
         }
 
-        defaultServiceManager()->addService(String16("storaged"), new Storaged());
+        if (StoragedService::start() != android::OK ||
+            StoragedPrivateService::start() != android::OK) {
+            PLOG_TO(SYSTEM, ERROR) << "Failed to start storaged service";
+            return -1;
+        }
+
         android::ProcessState::self()->startThreadPool();
         IPCThreadState::self()->joinThreadPool();
         pthread_join(storaged_main_thread, NULL);
@@ -145,31 +144,33 @@
         return 0;
     }
 
-    sp<IStoraged> storaged_service = get_storaged_service();
+    sp<IStoragedPrivate> storaged_service = get_storaged_pri_service();
     if (storaged_service == NULL) {
         fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
         return -1;
     }
 
     if (flag_dump_uid || flag_dump_task) {
-        vector<struct uid_info> res = storaged_service->dump_uids(NULL);
-        if (res.size() == 0) {
-            fprintf(stderr, "UID I/O is not readable in this version of kernel.\n");
+        vector<UidInfo> uid_io;
+        binder::Status status = storaged_service->dumpUids(&uid_io);
+        if (!status.isOk() || uid_io.size() == 0) {
+            fprintf(stderr, "UID I/O info is not available.\n");
             return 0;
         }
 
-        sort_running_uids_info(res);
-        log_console_running_uids_info(res, flag_dump_task);
+        sort_running_uids_info(uid_io);
+        log_console_running_uids_info(uid_io, flag_dump_task);
     }
 
     if (flag_dump_perf) {
-        vector<vector<uint32_t>> res = storaged_service->dump_perf_history(NULL);
-        if (res.size() == 0) {
-            fprintf(stderr, "I/O perf history is empty.\n");
+        vector<int> perf_history;
+        binder::Status status = storaged_service->dumpPerfHistory(&perf_history);
+        if (!status.isOk() || perf_history.size() == 0) {
+            fprintf(stderr, "I/O perf history is not available.\n");
             return 0;
         }
 
-        log_console_perf_history(res);
+        log_console_perf_history(perf_history);
     }
 
     return 0;
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 1794fb5..915c095 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "storaged"
 
+#include <dirent.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <time.h>
@@ -27,12 +28,13 @@
 #include <sstream>
 #include <string>
 
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <batteryservice/BatteryServiceConstants.h>
-#include <batteryservice/IBatteryPropertiesRegistrar.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
 #include <cutils/properties.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hwbinder/IPCThreadState.h>
 #include <log/log.h>
 
 #include <storaged.h>
@@ -45,60 +47,86 @@
 
 namespace {
 
-const uint32_t benchmark_unit_size = 16 * 1024;  // 16KB
+/*
+ * The system user is the initial user that is implicitly created on first boot
+ * and hosts most of the system services. Keep this in sync with
+ * frameworks/base/core/java/android/os/UserManager.java
+ */
+constexpr int USER_SYSTEM = 0;
 
-}
+constexpr uint32_t benchmark_unit_size = 16 * 1024;  // 16KB
+
+}  // namespace
 
 const uint32_t storaged_t::crc_init = 0x5108A4ED; /* STORAGED */
-const std::string storaged_t::proto_file =
-    "/data/misc/storaged/storaged.proto";
 
-sp<IBatteryPropertiesRegistrar> get_battery_properties_service() {
-    sp<IServiceManager> sm = defaultServiceManager();
-    if (sm == NULL) return NULL;
+using android::hardware::health::V1_0::BatteryStatus;
+using android::hardware::health::V1_0::toString;
+using android::hardware::health::V1_0::HealthInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hardware::interfacesEqual;
+using android::hardware::Return;
+using android::hidl::manager::V1_0::IServiceManager;
 
-    sp<IBinder> binder = sm->getService(String16("batteryproperties"));
-    if (binder == NULL) return NULL;
-
-    sp<IBatteryPropertiesRegistrar> battery_properties =
-        interface_cast<IBatteryPropertiesRegistrar>(binder);
-
-    return battery_properties;
+static sp<IHealth> get_health_service() {
+    for (auto&& instanceName : {"default", "backup"}) {
+        auto ret = IHealth::getService(instanceName);
+        if (ret != nullptr) {
+            return ret;
+        }
+        LOG_TO(SYSTEM, INFO) << "health: storaged: cannot get " << instanceName << " service";
+    }
+    return nullptr;
 }
 
-inline charger_stat_t is_charger_on(int64_t prop) {
-    return (prop == BATTERY_STATUS_CHARGING || prop == BATTERY_STATUS_FULL) ?
+inline charger_stat_t is_charger_on(BatteryStatus prop) {
+    return (prop == BatteryStatus::CHARGING || prop == BatteryStatus::FULL) ?
         CHARGER_ON : CHARGER_OFF;
 }
 
-void storaged_t::batteryPropertiesChanged(struct BatteryProperties props) {
+Return<void> storaged_t::healthInfoChanged(const HealthInfo& props) {
     mUidm.set_charger_state(is_charger_on(props.batteryStatus));
+    return android::hardware::Void();
 }
 
-void storaged_t::init_battery_service() {
+void storaged_t::init_health_service() {
     if (!mUidm.enabled())
         return;
 
-    battery_properties = get_battery_properties_service();
-    if (battery_properties == NULL) {
-        LOG_TO(SYSTEM, WARNING) << "failed to find batteryproperties service";
+    health = get_health_service();
+    if (health == NULL) {
+        LOG_TO(SYSTEM, WARNING) << "health: failed to find IHealth service";
         return;
     }
 
-    struct BatteryProperty val;
-    battery_properties->getProperty(BATTERY_PROP_BATTERY_STATUS, &val);
-    mUidm.init(is_charger_on(val.valueInt64), proto.uid_io_usage());
+    BatteryStatus status = BatteryStatus::UNKNOWN;
+    auto ret = health->getChargeStatus([&](Result r, BatteryStatus v) {
+        if (r != Result::SUCCESS) {
+            LOG_TO(SYSTEM, WARNING)
+                << "health: cannot get battery status " << toString(r);
+            return;
+        }
+        if (v == BatteryStatus::UNKNOWN) {
+            LOG_TO(SYSTEM, WARNING) << "health: invalid battery status";
+        }
+        status = v;
+    });
+    if (!ret.isOk()) {
+        LOG_TO(SYSTEM, WARNING) << "health: get charge status transaction error "
+            << ret.description();
+    }
 
+    mUidm.init(is_charger_on(status));
     // register listener after init uid_monitor
-    battery_properties->registerListener(this);
-    IInterface::asBinder(battery_properties)->linkToDeath(this);
+    health->registerCallback(this);
+    health->linkToDeath(this, 0 /* cookie */);
 }
 
-void storaged_t::binderDied(const wp<IBinder>& who) {
-    if (battery_properties != NULL &&
-        IInterface::asBinder(battery_properties) == who) {
-        LOG_TO(SYSTEM, ERROR) << "batteryproperties service died, exiting";
-        IPCThreadState::self()->stopProcess();
+void storaged_t::serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who) {
+    if (health != NULL && interfacesEqual(health, who.promote())) {
+        LOG_TO(SYSTEM, ERROR) << "health service died, exiting";
+        android::hardware::IPCThreadState::self()->stopProcess();
         exit(1);
     } else {
         LOG_TO(SYSTEM, ERROR) << "unknown service died";
@@ -106,7 +134,6 @@
 }
 
 void storaged_t::report_storage_info() {
-    storage_info->init(proto.perf_history());
     storage_info->report();
 }
 
@@ -137,53 +164,75 @@
     mTimer = 0;
 }
 
-void storaged_t::load_proto() {
-    std::ifstream in(proto_file,
-        std::ofstream::in | std::ofstream::binary);
+void storaged_t::add_user_ce(userid_t user_id) {
+    load_proto(user_id);
+    proto_loaded[user_id] = true;
+}
 
-    if (!in.good()) {
-        PLOG_TO(SYSTEM, INFO) << "Open " << proto_file << " failed";
-        return;
-    }
+void storaged_t::remove_user_ce(userid_t user_id) {
+    proto_loaded[user_id] = false;
+    mUidm.clear_user_history(user_id);
+    RemoveFileIfExists(proto_path(user_id), nullptr);
+}
+
+void storaged_t::load_proto(userid_t user_id) {
+    string proto_file = proto_path(user_id);
+    ifstream in(proto_file, ofstream::in | ofstream::binary);
+
+    if (!in.good()) return;
 
     stringstream ss;
     ss << in.rdbuf();
+    StoragedProto proto;
     proto.ParseFromString(ss.str());
 
     uint32_t crc = proto.crc();
     proto.set_crc(crc_init);
-    std::string proto_str = proto.SerializeAsString();
+    string proto_str = proto.SerializeAsString();
     uint32_t computed_crc = crc32(crc_init,
         reinterpret_cast<const Bytef*>(proto_str.c_str()),
         proto_str.size());
 
     if (crc != computed_crc) {
         LOG_TO(SYSTEM, WARNING) << "CRC mismatch in " << proto_file;
-        proto.Clear();
+        return;
+    }
+
+    mUidm.load_uid_io_proto(proto.uid_io_usage());
+
+    if (user_id == USER_SYSTEM) {
+        storage_info->load_perf_history_proto(proto.perf_history());
     }
 }
 
-void storaged_t::flush_proto() {
-    proto.set_version(1);
-    proto.set_crc(crc_init);
-    while (proto.ByteSize() < 128 * 1024) {
-        proto.add_padding(0xFEEDBABE);
+void storaged_t:: prepare_proto(userid_t user_id, StoragedProto* proto) {
+    proto->set_version(3);
+    proto->set_crc(crc_init);
+
+    if (user_id == USER_SYSTEM) {
+        while (proto->ByteSize() < 128 * 1024) {
+            proto->add_padding(0xFEEDBABE);
+        }
     }
-    std::string proto_str = proto.SerializeAsString();
-    proto.set_crc(crc32(crc_init,
+
+    string proto_str = proto->SerializeAsString();
+    proto->set_crc(crc32(crc_init,
         reinterpret_cast<const Bytef*>(proto_str.c_str()),
         proto_str.size()));
-    proto_str = proto.SerializeAsString();
+}
 
+void storaged_t::flush_proto_user_system(StoragedProto* proto) {
+    string proto_str = proto->SerializeAsString();
     const char* data = proto_str.data();
     uint32_t size = proto_str.size();
     ssize_t ret;
     time_point<steady_clock> start, end;
 
-    std::string tmp_file = proto_file + "_tmp";
+    string proto_file = proto_path(USER_SYSTEM);
+    string tmp_file = proto_file + "_tmp";
     unique_fd fd(TEMP_FAILURE_RETRY(open(tmp_file.c_str(),
-                 O_DIRECT | O_SYNC | O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC,
-                 S_IRUSR | S_IWUSR)));
+                O_DIRECT | O_SYNC | O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC,
+                S_IRUSR | S_IWUSR)));
     if (fd == -1) {
         PLOG_TO(SYSTEM, ERROR) << "Faied to open tmp file: " << tmp_file;
         return;
@@ -221,7 +270,39 @@
     rename(tmp_file.c_str(), proto_file.c_str());
 }
 
+void storaged_t::flush_proto(userid_t user_id, StoragedProto* proto) {
+    prepare_proto(user_id, proto);
+
+    if (user_id == USER_SYSTEM) {
+        flush_proto_user_system(proto);
+        return;
+    }
+
+    string proto_file = proto_path(user_id);
+    string tmp_file = proto_file + "_tmp";
+    if (!WriteStringToFile(proto->SerializeAsString(), tmp_file,
+                           S_IRUSR | S_IWUSR)) {
+        return;
+    }
+
+    /* Atomically replace existing proto file to reduce chance of data loss. */
+    rename(tmp_file.c_str(), proto_file.c_str());
+}
+
+void storaged_t::flush_protos(unordered_map<int, StoragedProto>* protos) {
+    for (auto& it : *protos) {
+        /*
+         * Don't flush proto if we haven't attempted to load it from file.
+         */
+        if (proto_loaded[it.first]) {
+            flush_proto(it.first, &it.second);
+        }
+    }
+}
+
 void storaged_t::event(void) {
+    unordered_map<int, StoragedProto> protos;
+
     if (mDsm.enabled()) {
         mDsm.update();
         if (!(mTimer % mConfig.periodic_chores_interval_disk_stats_publish)) {
@@ -229,15 +310,16 @@
         }
     }
 
-    if (mUidm.enabled() &&
-        !(mTimer % mConfig.periodic_chores_interval_uid_io)) {
-        mUidm.report(proto.mutable_uid_io_usage());
+    if (!(mTimer % mConfig.periodic_chores_interval_uid_io)) {
+        mUidm.report(&protos);
     }
 
-    storage_info->refresh(proto.mutable_perf_history());
+    if (storage_info) {
+        storage_info->refresh(protos[USER_SYSTEM].mutable_perf_history());
+    }
 
     if (!(mTimer % mConfig.periodic_chores_interval_flush_proto)) {
-        flush_proto();
+        flush_protos(&protos);
     }
 
     mTimer += mConfig.periodic_chores_interval_unit;
diff --git a/storaged/storaged.proto b/storaged/storaged.proto
index 05c1f91..9dcd79e 100644
--- a/storaged/storaged.proto
+++ b/storaged/storaged.proto
@@ -22,8 +22,9 @@
 
 message UidRecord {
   optional string uid_name = 1;
-  optional IOUsage uid_io = 2;
-  repeated TaskIOUsage task_io = 3;
+  optional uint32 user_id = 2;
+  optional IOUsage uid_io = 3;
+  repeated TaskIOUsage task_io = 4;
 }
 
 message UidIORecords {
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
index bd4022b..1840d05 100644
--- a/storaged/storaged.rc
+++ b/storaged/storaged.rc
@@ -1,7 +1,3 @@
-on post-fs-data
-    mkdir /data/misc/storaged 0700 root root
-    restorecon /data/misc/storaged
-
 service storaged /system/bin/storaged
     class main
     priority 10
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index c5552f6..036d7e1 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -66,8 +66,10 @@
     return new storage_info_t;
 }
 
-void storage_info_t::init(const IOPerfHistory& perf_history)
+void storage_info_t::load_perf_history_proto(const IOPerfHistory& perf_history)
 {
+    Mutex::Autolock _l(si_mutex);
+
     if (!perf_history.has_day_start_sec() ||
         perf_history.daily_perf_size() > (int)daily_perf.size() ||
         perf_history.weekly_perf_size() > (int)weekly_perf.size()) {
@@ -76,7 +78,7 @@
     }
 
     day_start_tp = {};
-    day_start_tp += seconds(perf_history.day_start_sec());
+    day_start_tp += chrono::seconds(perf_history.day_start_sec());
 
     nr_samples = perf_history.nr_samples();
     for (auto bw : perf_history.recent_perf()) {
@@ -107,11 +109,11 @@
     userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10;
     userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10;
 
-    unique_ptr<lock_t> lock(new lock_t(&si_lock));
+    Mutex::Autolock _l(si_mutex);
 
     perf_history->Clear();
     perf_history->set_day_start_sec(
-        duration_cast<seconds>(day_start_tp.time_since_epoch()).count());
+        duration_cast<chrono::seconds>(day_start_tp.time_since_epoch()).count());
     for (const uint32_t& bw : recent_perf) {
         perf_history->add_recent_perf(bw);
     }
@@ -136,10 +138,10 @@
 void storage_info_t::update_perf_history(uint32_t bw,
                                          const time_point<system_clock>& tp)
 {
-    unique_ptr<lock_t> lock(new lock_t(&si_lock));
+    Mutex::Autolock _l(si_mutex);
 
     if (tp > day_start_tp &&
-        duration_cast<seconds>(tp - day_start_tp).count() < DAY_TO_SEC) {
+        duration_cast<chrono::seconds>(tp - day_start_tp).count() < DAY_TO_SEC) {
         if (nr_samples >= recent_perf.size()) {
             recent_perf.push_back(bw);
         } else {
@@ -155,7 +157,7 @@
     uint32_t daily_avg_bw = accumulate(recent_perf.begin(),
         recent_perf.begin() + nr_samples, 0) / nr_samples;
 
-    day_start_tp = tp - seconds(duration_cast<seconds>(
+    day_start_tp = tp - chrono::seconds(duration_cast<chrono::seconds>(
         tp.time_since_epoch()).count() % DAY_TO_SEC);
 
     nr_samples = 0;
@@ -180,28 +182,32 @@
     weekly_perf[nr_weeks++] = week_avg_bw;
 }
 
-vector<vector<uint32_t>> storage_info_t::get_perf_history()
+vector<int> storage_info_t::get_perf_history()
 {
-    unique_ptr<lock_t> lock(new lock_t(&si_lock));
+    Mutex::Autolock _l(si_mutex);
 
-    vector<vector<uint32_t>> ret(3);
+    vector<int> ret(3 + recent_perf.size() + daily_perf.size() + weekly_perf.size());
 
-    ret[0].resize(recent_perf.size());
+    ret[0] = recent_perf.size();
+    ret[1] = daily_perf.size();
+    ret[2] = weekly_perf.size();
+
+    int start = 3;
     for (size_t i = 0; i < recent_perf.size(); i++) {
         int idx = (recent_perf.size() + nr_samples - 1 - i) % recent_perf.size();
-        ret[0][i] = recent_perf[idx];
+        ret[start + i] = recent_perf[idx];
     }
 
-    ret[1].resize(daily_perf.size());
+    start += recent_perf.size();
     for (size_t i = 0; i < daily_perf.size(); i++) {
         int idx = (daily_perf.size() + nr_days - 1 - i) % daily_perf.size();
-        ret[1][i] = daily_perf[idx];
+        ret[start + i] = daily_perf[idx];
     }
 
-    ret[2].resize(weekly_perf.size());
+    start += daily_perf.size();
     for (size_t i = 0; i < weekly_perf.size(); i++) {
         int idx = (weekly_perf.size() + nr_weeks - 1 - i) % weekly_perf.size();
-        ret[2][i] = weekly_perf[idx];
+        ret[start + i] = weekly_perf[idx];
     }
 
     return ret;
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index e4ba380..3c790e6 100644
--- a/storaged/storaged_service.cpp
+++ b/storaged/storaged_service.cpp
@@ -30,126 +30,69 @@
 #include <private/android_filesystem_config.h>
 
 #include <storaged.h>
+#include <storaged_utils.h>
 #include <storaged_service.h>
 
 using namespace std;
 using namespace android::base;
 
-extern sp<storaged_t> storaged;
+extern sp<storaged_t> storaged_sp;
 
-vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) {
-    Parcel data, reply;
-    data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
+status_t StoragedService::start() {
+    return BinderService<StoragedService>::publish();
+}
 
-    remote()->transact(DUMPUIDS, data, &reply);
+void StoragedService::dumpUidRecords(int fd, const vector<uid_record>& entries) {
+    map<string, io_usage> merged_entries = merge_io_usage(entries);
+    for (const auto& rec : merged_entries) {
+        dprintf(fd, "%s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+                " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+                rec.first.c_str(),
+                rec.second.bytes[READ][FOREGROUND][CHARGER_OFF],
+                rec.second.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+                rec.second.bytes[READ][BACKGROUND][CHARGER_OFF],
+                rec.second.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+                rec.second.bytes[READ][FOREGROUND][CHARGER_ON],
+                rec.second.bytes[WRITE][FOREGROUND][CHARGER_ON],
+                rec.second.bytes[READ][BACKGROUND][CHARGER_ON],
+                rec.second.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+    }
+}
 
-    uint32_t res_size = reply.readInt32();
-    vector<struct uid_info> res(res_size);
-    for (auto&& uid : res) {
-        uid.uid = reply.readInt32();
-        uid.name = reply.readCString();
-        reply.read(&uid.io, sizeof(uid.io));
+void StoragedService::dumpUidRecordsDebug(int fd, const vector<uid_record>& entries) {
+    for (const auto& record : entries) {
+        const io_usage& uid_usage = record.ios.uid_ios;
+        dprintf(fd, "%s_%d %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+                " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+                record.name.c_str(), record.ios.user_id,
+                uid_usage.bytes[READ][FOREGROUND][CHARGER_OFF],
+                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+                uid_usage.bytes[READ][BACKGROUND][CHARGER_OFF],
+                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+                uid_usage.bytes[READ][FOREGROUND][CHARGER_ON],
+                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_ON],
+                uid_usage.bytes[READ][BACKGROUND][CHARGER_ON],
+                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);
 
-        uint32_t tasks_size = reply.readInt32();
-        for (uint32_t i = 0; i < tasks_size; i++) {
-            struct task_info task;
-            task.pid = reply.readInt32();
-            task.comm = reply.readCString();
-            reply.read(&task.io, sizeof(task.io));
-            uid.tasks[task.pid] = task;
+        for (const auto& task_it : record.ios.task_ios) {
+            const io_usage& task_usage = task_it.second;
+            const string& comm = task_it.first;
+            dprintf(fd, "-> %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+                    " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+                    comm.c_str(),
+                    task_usage.bytes[READ][FOREGROUND][CHARGER_OFF],
+                    task_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+                    task_usage.bytes[READ][BACKGROUND][CHARGER_OFF],
+                    task_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+                    task_usage.bytes[READ][FOREGROUND][CHARGER_ON],
+                    task_usage.bytes[WRITE][FOREGROUND][CHARGER_ON],
+                    task_usage.bytes[READ][BACKGROUND][CHARGER_ON],
+                    task_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);
         }
     }
-    return res;
 }
 
-vector<vector<uint32_t>> BpStoraged::dump_perf_history(const char* /*option*/) {
-    Parcel data, reply;
-    data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
-
-    remote()->transact(DUMPPERF, data, &reply);
-
-    vector<vector<uint32_t>> res(3);
-    uint32_t size = reply.readUint32();
-    res[0].resize(size);
-    for (uint32_t i = 0; i < size; i++) {
-        res[0][i] = reply.readUint32();
-    }
-    size = reply.readUint32();
-    res[1].resize(size);
-    for (uint32_t i = 0; i < size; i++) {
-        res[1][i] = reply.readUint32();
-    }
-    size = reply.readUint32();
-    res[2].resize(size);
-    for (uint32_t i = 0; i < size; i++) {
-        res[2][i] = reply.readUint32();
-    }
-    return res;
-}
-
-IMPLEMENT_META_INTERFACE(Storaged, "Storaged");
-
-status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
-    switch(code) {
-        case DUMPUIDS: {
-                if (!data.checkInterface(this))
-                    return BAD_TYPE;
-                vector<struct uid_info> res = dump_uids(NULL);
-                reply->writeInt32(res.size());
-                for (const auto& uid : res) {
-                    reply->writeInt32(uid.uid);
-                    reply->writeCString(uid.name.c_str());
-                    reply->write(&uid.io, sizeof(uid.io));
-
-                    reply->writeInt32(uid.tasks.size());
-                    for (const auto& task_it : uid.tasks) {
-                        reply->writeInt32(task_it.first);
-                        reply->writeCString(task_it.second.comm.c_str());
-                        reply->write(&task_it.second.io, sizeof(task_it.second.io));
-                    }
-                }
-                return NO_ERROR;
-            }
-            break;
-        case DUMPPERF: {
-            if (!data.checkInterface(this))
-                return BAD_TYPE;
-            vector<vector<uint32_t>> res = dump_perf_history(NULL);
-            reply->writeUint32(res[0].size());
-            for (const auto& item : res[0]) {
-                reply->writeUint32(item);
-            }
-            reply->writeUint32(res[1].size());
-            for (const auto& item : res[1]) {
-                reply->writeUint32(item);
-            }
-            reply->writeUint32(res[2].size());
-            for (const auto& item : res[2]) {
-                reply->writeUint32(item);
-            }
-            return NO_ERROR;
-        }
-        break;
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
-    }
-}
-
-vector<struct uid_info> Storaged::dump_uids(const char* /* option */) {
-    vector<struct uid_info> uids_v;
-    unordered_map<uint32_t, struct uid_info> uids_m = storaged->get_uids();
-
-    for (const auto& it : uids_m) {
-        uids_v.push_back(it.second);
-    }
-    return uids_v;
-}
-
-vector<vector<uint32_t>> Storaged::dump_perf_history(const char* /* option */) {
-    return storaged->get_perf_history();
-}
-
-status_t Storaged::dump(int fd, const Vector<String16>& args) {
+status_t StoragedService::dump(int fd, const Vector<String16>& args) {
     IPCThreadState* self = IPCThreadState::self();
     const int pid = self->getCallingPid();
     const int uid = self->getCallingUid();
@@ -198,8 +141,8 @@
     }
 
     uint64_t last_ts = 0;
-    const map<uint64_t, struct uid_records>& records =
-                storaged->get_uid_records(hours, threshold, force_report);
+    map<uint64_t, struct uid_records> records =
+                storaged_sp->get_uid_records(hours, threshold, force_report);
     for (const auto& it : records) {
         if (last_ts != it.second.start_ts) {
             dprintf(fd, "%" PRIu64, it.second.start_ts);
@@ -207,54 +150,61 @@
         dprintf(fd, ",%" PRIu64 "\n", it.first);
         last_ts = it.first;
 
-        for (const auto& record : it.second.entries) {
-            const struct io_usage& uid_usage = record.ios.uid_ios;
-            dprintf(fd, "%s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
-                    " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
-                record.name.c_str(),
-                uid_usage.bytes[READ][FOREGROUND][CHARGER_OFF],
-                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF],
-                uid_usage.bytes[READ][BACKGROUND][CHARGER_OFF],
-                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF],
-                uid_usage.bytes[READ][FOREGROUND][CHARGER_ON],
-                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_ON],
-                uid_usage.bytes[READ][BACKGROUND][CHARGER_ON],
-                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);
-            if (debug) {
-                for (const auto& task_it : record.ios.task_ios) {
-                    const struct io_usage& task_usage = task_it.second;
-                    const string& comm = task_it.first;
-                    dprintf(fd, "-> %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
-                            " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
-                        comm.c_str(),
-                        task_usage.bytes[READ][FOREGROUND][CHARGER_OFF],
-                        task_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF],
-                        task_usage.bytes[READ][BACKGROUND][CHARGER_OFF],
-                        task_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF],
-                        task_usage.bytes[READ][FOREGROUND][CHARGER_ON],
-                        task_usage.bytes[WRITE][FOREGROUND][CHARGER_ON],
-                        task_usage.bytes[READ][BACKGROUND][CHARGER_ON],
-                        task_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);
-                }
-            }
+        if (!debug) {
+            dumpUidRecords(fd, it.second.entries);
+        } else {
+            dumpUidRecordsDebug(fd, it.second.entries);
         }
     }
 
     if (time_window) {
-        storaged->update_uid_io_interval(time_window);
+        storaged_sp->update_uid_io_interval(time_window);
     }
 
     return NO_ERROR;
 }
 
-sp<IStoraged> get_storaged_service() {
+binder::Status StoragedService::onUserStarted(int32_t userId) {
+    storaged_sp->add_user_ce(userId);
+    return binder::Status::ok();
+}
+
+binder::Status StoragedService::onUserStopped(int32_t userId) {
+    storaged_sp->remove_user_ce(userId);
+    return binder::Status::ok();
+}
+
+status_t StoragedPrivateService::start() {
+    return BinderService<StoragedPrivateService>::publish();
+}
+
+binder::Status StoragedPrivateService::dumpUids(
+        vector<::android::os::storaged::UidInfo>* _aidl_return) {
+    unordered_map<uint32_t, uid_info> uids_m = storaged_sp->get_uids();
+
+    for (const auto& it : uids_m) {
+        UidInfo uinfo;
+        uinfo.uid = it.second.uid;
+        uinfo.name = it.second.name;
+        uinfo.tasks = it.second.tasks;
+        memcpy(&uinfo.io, &it.second.io, sizeof(uinfo.io));
+        _aidl_return->push_back(uinfo);
+    }
+    return binder::Status::ok();
+}
+
+binder::Status StoragedPrivateService::dumpPerfHistory(
+        vector<int32_t>* _aidl_return) {
+    *_aidl_return = storaged_sp->get_perf_history();
+    return binder::Status::ok();
+}
+
+sp<IStoragedPrivate> get_storaged_pri_service() {
     sp<IServiceManager> sm = defaultServiceManager();
     if (sm == NULL) return NULL;
 
-    sp<IBinder> binder = sm->getService(String16("storaged"));
+    sp<IBinder> binder = sm->getService(String16("storaged_pri"));
     if (binder == NULL) return NULL;
 
-    sp<IStoraged> storaged = interface_cast<IStoraged>(binder);
-
-    return storaged;
+    return interface_cast<IStoragedPrivate>(binder);
 }
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
index 9295ff2..5745782 100644
--- a/storaged/storaged_uid_monitor.cpp
+++ b/storaged/storaged_uid_monitor.cpp
@@ -38,6 +38,7 @@
 using namespace android;
 using namespace android::base;
 using namespace android::content::pm;
+using namespace android::os::storaged;
 using namespace storaged_proto;
 
 namespace {
@@ -47,9 +48,9 @@
 
 } // namepsace
 
-std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats()
+std::unordered_map<uint32_t, uid_info> uid_monitor::get_uid_io_stats()
 {
-    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+    Mutex::Autolock _l(uidm_mutex);
     return get_uid_io_stats_locked();
 };
 
@@ -151,9 +152,9 @@
 
 } // namespace
 
-std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats_locked()
+std::unordered_map<uint32_t, uid_info> uid_monitor::get_uid_io_stats_locked()
 {
-    std::unordered_map<uint32_t, struct uid_info> uid_io_stats;
+    std::unordered_map<uint32_t, uid_info> uid_io_stats;
     std::string buffer;
     if (!ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
         PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
@@ -161,7 +162,7 @@
     }
 
     std::vector<std::string> io_stats = Split(std::move(buffer), "\n");
-    struct uid_info u;
+    uid_info u;
     vector<int> uids;
     vector<std::string*> uid_names;
 
@@ -183,7 +184,7 @@
                 uid_io_stats[u.uid].name = last_uid_io_stats[u.uid].name;
             }
         } else {
-            struct task_info t;
+            task_info t;
             if (!t.parse_task_io_stats(std::move(io_stats[i])))
                 continue;
             uid_io_stats[u.uid].tasks[t.pid] = t;
@@ -226,6 +227,7 @@
         struct uid_record record = {};
         record.name = p.first;
         if (!p.second.uid_ios.is_zero()) {
+            record.ios.user_id = p.second.user_id;
             record.ios.uid_ios = p.second.uid_ios;
             for (const auto& p_task : p.second.task_ios) {
                 if (!p_task.second.is_zero())
@@ -255,13 +257,13 @@
 }
 
 std::map<uint64_t, struct uid_records> uid_monitor::dump(
-    double hours, uint64_t threshold, bool force_report, UidIOUsage* uid_io_proto)
+    double hours, uint64_t threshold, bool force_report)
 {
     if (force_report) {
-        report(uid_io_proto);
+        report(nullptr);
     }
 
-    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+    Mutex::Autolock _l(uidm_mutex);
 
     std::map<uint64_t, struct uid_records> dump_records;
     uint64_t first_ts = 0;
@@ -301,20 +303,21 @@
 
 void uid_monitor::update_curr_io_stats_locked()
 {
-    std::unordered_map<uint32_t, struct uid_info> uid_io_stats =
+    std::unordered_map<uint32_t, uid_info> uid_io_stats =
         get_uid_io_stats_locked();
     if (uid_io_stats.empty()) {
         return;
     }
 
     for (const auto& it : uid_io_stats) {
-        const struct uid_info& uid = it.second;
-
+        const uid_info& uid = it.second;
         if (curr_io_stats.find(uid.name) == curr_io_stats.end()) {
-          curr_io_stats[uid.name] = {};
+            curr_io_stats[uid.name] = {};
         }
 
         struct uid_io_usage& usage = curr_io_stats[uid.name];
+        usage.user_id = multiuser_get_user_id(uid.uid);
+
         int64_t fg_rd_delta = uid.io[FOREGROUND].read_bytes -
             last_uid_io_stats[uid.uid].io[FOREGROUND].read_bytes;
         int64_t bg_rd_delta = uid.io[BACKGROUND].read_bytes -
@@ -334,7 +337,7 @@
             (bg_wr_delta < 0) ? 0 : bg_wr_delta;
 
         for (const auto& task_it : uid.tasks) {
-            const struct task_info& task = task_it.second;
+            const task_info& task = task_it.second;
             const pid_t pid = task_it.first;
             const std::string& comm = task_it.second.comm;
             int64_t task_fg_rd_delta = task.io[FOREGROUND].read_bytes -
@@ -346,7 +349,7 @@
             int64_t task_bg_wr_delta = task.io[BACKGROUND].write_bytes -
                 last_uid_io_stats[uid.uid].tasks[pid].io[BACKGROUND].write_bytes;
 
-            struct io_usage& task_usage = usage.task_ios[comm];
+            io_usage& task_usage = usage.task_ios[comm];
             task_usage.bytes[READ][FOREGROUND][charger_stat] +=
                 (task_fg_rd_delta < 0) ? 0 : task_fg_rd_delta;
             task_usage.bytes[READ][BACKGROUND][charger_stat] +=
@@ -361,19 +364,23 @@
     last_uid_io_stats = uid_io_stats;
 }
 
-void uid_monitor::report(UidIOUsage* proto)
+void uid_monitor::report(unordered_map<int, StoragedProto>* protos)
 {
-    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+    if (!enabled()) return;
+
+    Mutex::Autolock _l(uidm_mutex);
 
     update_curr_io_stats_locked();
     add_records_locked(time(NULL));
 
-    update_uid_io_proto(proto);
+    if (protos) {
+        update_uid_io_proto(protos);
+    }
 }
 
 namespace {
 
-void set_io_usage_proto(IOUsage* usage_proto, const struct io_usage& usage)
+void set_io_usage_proto(IOUsage* usage_proto, const io_usage& usage)
 {
     usage_proto->set_rd_fg_chg_on(usage.bytes[READ][FOREGROUND][CHARGER_ON]);
     usage_proto->set_rd_fg_chg_off(usage.bytes[READ][FOREGROUND][CHARGER_OFF]);
@@ -385,7 +392,7 @@
     usage_proto->set_wr_bg_chg_off(usage.bytes[WRITE][BACKGROUND][CHARGER_OFF]);
 }
 
-void get_io_usage_proto(struct io_usage* usage, const IOUsage& io_proto)
+void get_io_usage_proto(io_usage* usage, const IOUsage& io_proto)
 {
     usage->bytes[READ][FOREGROUND][CHARGER_ON] = io_proto.rd_fg_chg_on();
     usage->bytes[READ][FOREGROUND][CHARGER_OFF] = io_proto.rd_fg_chg_off();
@@ -399,31 +406,37 @@
 
 } // namespace
 
-void uid_monitor::update_uid_io_proto(UidIOUsage* uid_io_proto)
+void uid_monitor::update_uid_io_proto(unordered_map<int, StoragedProto>* protos)
 {
-    uid_io_proto->Clear();
-
     for (const auto& item : io_history) {
         const uint64_t& end_ts = item.first;
         const struct uid_records& recs = item.second;
-
-        UidIOItem* item_proto = uid_io_proto->add_uid_io_items();
-        item_proto->set_end_ts(end_ts);
-
-        UidIORecords* recs_proto = item_proto->mutable_records();
-        recs_proto->set_start_ts(recs.start_ts);
+        unordered_map<userid_t, UidIOItem*> user_items;
 
         for (const auto& entry : recs.entries) {
+            userid_t user_id = entry.ios.user_id;
+            UidIOItem* item_proto = user_items[user_id];
+            if (item_proto == nullptr) {
+                item_proto = (*protos)[user_id].mutable_uid_io_usage()
+                             ->add_uid_io_items();
+                user_items[user_id] = item_proto;
+            }
+            item_proto->set_end_ts(end_ts);
+
+            UidIORecords* recs_proto = item_proto->mutable_records();
+            recs_proto->set_start_ts(recs.start_ts);
+
             UidRecord* rec_proto = recs_proto->add_entries();
             rec_proto->set_uid_name(entry.name);
+            rec_proto->set_user_id(user_id);
 
             IOUsage* uid_io_proto = rec_proto->mutable_uid_io();
-            const struct io_usage& uio_ios = entry.ios.uid_ios;
+            const io_usage& uio_ios = entry.ios.uid_ios;
             set_io_usage_proto(uid_io_proto, uio_ios);
 
             for (const auto& task_io : entry.ios.task_ios) {
                 const std::string& task_name = task_io.first;
-                const struct io_usage& task_ios = task_io.second;
+                const io_usage& task_ios = task_io.second;
 
                 TaskIOUsage* task_io_proto = rec_proto->add_task_io();
                 task_io_proto->set_task_name(task_name);
@@ -433,8 +446,34 @@
     }
 }
 
+void uid_monitor::clear_user_history(userid_t user_id)
+{
+    Mutex::Autolock _l(uidm_mutex);
+
+    for (auto& item : io_history) {
+        vector<uid_record>* entries = &item.second.entries;
+        entries->erase(
+            remove_if(entries->begin(), entries->end(),
+                [user_id](const uid_record& rec) {
+                    return rec.ios.user_id == user_id;}),
+            entries->end());
+    }
+
+    for (auto it = io_history.begin(); it != io_history.end(); ) {
+        if (it->second.entries.empty()) {
+            it = io_history.erase(it);
+        } else {
+            it++;
+        }
+    }
+}
+
 void uid_monitor::load_uid_io_proto(const UidIOUsage& uid_io_proto)
 {
+    if (!enabled()) return;
+
+    Mutex::Autolock _l(uidm_mutex);
+
     for (const auto& item_proto : uid_io_proto.uid_io_items()) {
         const UidIORecords& records_proto = item_proto.records();
         struct uid_records* recs = &io_history[item_proto.end_ts()];
@@ -443,6 +482,7 @@
         for (const auto& rec_proto : records_proto.entries()) {
             struct uid_record record;
             record.name = rec_proto.uid_name();
+            record.ios.user_id = rec_proto.user_id();
             get_io_usage_proto(&record.ios.uid_ios, rec_proto.uid_io());
 
             for (const auto& task_io_proto : rec_proto.task_io()) {
@@ -457,7 +497,7 @@
 
 void uid_monitor::set_charger_state(charger_stat_t stat)
 {
-    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+    Mutex::Autolock _l(uidm_mutex);
 
     if (charger_stat == stat) {
         return;
@@ -467,23 +507,14 @@
     charger_stat = stat;
 }
 
-void uid_monitor::init(charger_stat_t stat, const UidIOUsage& proto)
+void uid_monitor::init(charger_stat_t stat)
 {
     charger_stat = stat;
 
-    load_uid_io_proto(proto);
-
     start_ts = time(NULL);
     last_uid_io_stats = get_uid_io_stats();
 }
 
 uid_monitor::uid_monitor()
-    : enable(!access(UID_IO_STATS_PATH, R_OK))
-{
-    sem_init(&um_lock, 0, 1);
-}
-
-uid_monitor::~uid_monitor()
-{
-    sem_destroy(&um_lock);
+    : enable(!access(UID_IO_STATS_PATH, R_OK)) {
 }
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
index fcd2484..4fd4bc9 100644
--- a/storaged/storaged_utils.cpp
+++ b/storaged/storaged_utils.cpp
@@ -42,7 +42,7 @@
 #include <storaged.h>
 #include <storaged_utils.h>
 
-bool cmp_uid_info(struct uid_info l, struct uid_info r) {
+bool cmp_uid_info(const UidInfo& l, const UidInfo& r) {
     // Compare background I/O first.
     for (int i = UID_STATS - 1; i >= 0; i--) {
         uint64_t l_bytes = l.io[i].read_bytes + l.io[i].write_bytes;
@@ -61,12 +61,12 @@
     return l.name < r.name;
 }
 
-void sort_running_uids_info(std::vector<struct uid_info> &uids) {
+void sort_running_uids_info(std::vector<UidInfo> &uids) {
     std::sort(uids.begin(), uids.end(), cmp_uid_info);
 }
 
 // Logging functions
-void log_console_running_uids_info(const std::vector<struct uid_info>& uids, bool flag_dump_task) {
+void log_console_running_uids_info(const std::vector<UidInfo>& uids, bool flag_dump_task) {
     printf("name/uid fg_rchar fg_wchar fg_rbytes fg_wbytes "
            "bg_rchar bg_wchar bg_rbytes bg_wbytes fg_fsync bg_fsync\n");
 
@@ -79,7 +79,7 @@
             uid.io[0].fsync, uid.io[1].fsync);
         if (flag_dump_task) {
             for (const auto& task_it : uid.tasks) {
-                const struct task_info& task = task_it.second;
+                const task_info& task = task_it.second;
                 printf("-> %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
                         " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
                     task.comm.c_str(),
@@ -92,25 +92,41 @@
     fflush(stdout);
 }
 
-void log_console_perf_history(const vector<vector<uint32_t>>& perf_history) {
-    if (perf_history.size() != 3) {
+void log_console_perf_history(const vector<int>& perf_history) {
+    if (perf_history.size() < 3 ||
+        perf_history.size() != perf_history[0] +
+                               perf_history[1] +
+                               perf_history[2] + (size_t)3) {
         return;
     }
 
     printf("\nI/O perf history (KB/s) :  most_recent  <---------  least_recent \n");
 
     std::stringstream line;
-    std::copy(perf_history[0].begin(), perf_history[0].end(),
+    int start = 3;
+    int end = 3 + perf_history[0];
+    std::copy(perf_history.begin() + start, perf_history.begin() + end,
               std::ostream_iterator<int>(line, " "));
     printf("last 24 hours : %s\n", line.str().c_str());
 
     line.str("");
-    std::copy(perf_history[1].begin(), perf_history[1].end(),
+    start = end;
+    end += perf_history[1];
+    std::copy(perf_history.begin() + start, perf_history.begin() + end,
               std::ostream_iterator<int>(line, " "));
     printf("last 7 days   : %s\n", line.str().c_str());
 
     line.str("");
-    std::copy(perf_history[2].begin(), perf_history[2].end(),
+    start = end;
+    std::copy(perf_history.begin() + start, perf_history.end(),
               std::ostream_iterator<int>(line, " "));
     printf("last 52 weeks : %s\n", line.str().c_str());
-}
\ No newline at end of file
+}
+
+map<string, io_usage> merge_io_usage(const vector<uid_record>& entries) {
+    map<string, io_usage> merged_entries;
+    for (const auto& record : entries) {
+        merged_entries[record.name] += record.ios.uid_ios;
+    }
+    return merged_entries;
+}
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
index 5ae1c91..6a5fc61 100644
--- a/storaged/tests/storaged_test.cpp
+++ b/storaged/tests/storaged_test.cpp
@@ -33,6 +33,7 @@
 
 using namespace std;
 using namespace chrono;
+using namespace storaged_proto;
 
 namespace {
 
@@ -376,32 +377,219 @@
     for (int i = 0; i < 75; i++) {
         tp += hours(5);
         stp = {};
-        stp += duration_cast<seconds>(tp.time_since_epoch());
+        stp += duration_cast<chrono::seconds>(tp.time_since_epoch());
         si.update_perf_history((i + 1) * 5, stp);
     }
 
-    vector<vector<uint32_t>> history = si.get_perf_history();
-    EXPECT_EQ(history.size(), 3UL);
-    EXPECT_EQ(history[0].size(), 4UL);
-    EXPECT_EQ(history[1].size(), 7UL);    // 7 days
-    EXPECT_EQ(history[2].size(), 52UL);   // 52 weeks
+    vector<int> history = si.get_perf_history();
+    EXPECT_EQ(history.size(), 66UL);
+    size_t i = 0;
+    EXPECT_EQ(history[i++], 4);
+    EXPECT_EQ(history[i++], 7);    // 7 days
+    EXPECT_EQ(history[i++], 52);   // 52 weeks
     // last 24 hours
-    EXPECT_EQ(history[0][0], 375UL);
-    EXPECT_EQ(history[0][1], 370UL);
-    EXPECT_EQ(history[0][2], 365UL);
-    EXPECT_EQ(history[0][3], 360UL);
+    EXPECT_EQ(history[i++], 375);
+    EXPECT_EQ(history[i++], 370);
+    EXPECT_EQ(history[i++], 365);
+    EXPECT_EQ(history[i++], 360);
     // daily average of last 7 days
-    EXPECT_EQ(history[1][0], 347UL);
-    EXPECT_EQ(history[1][1], 325UL);
-    EXPECT_EQ(history[1][2], 300UL);
-    EXPECT_EQ(history[1][3], 275UL);
-    EXPECT_EQ(history[1][4], 250UL);
-    EXPECT_EQ(history[1][5], 227UL);
-    EXPECT_EQ(history[1][6], 205UL);
+    EXPECT_EQ(history[i++], 347);
+    EXPECT_EQ(history[i++], 325);
+    EXPECT_EQ(history[i++], 300);
+    EXPECT_EQ(history[i++], 275);
+    EXPECT_EQ(history[i++], 250);
+    EXPECT_EQ(history[i++], 227);
+    EXPECT_EQ(history[i++], 205);
     // weekly average of last 52 weeks
-    EXPECT_EQ(history[2][0], 251UL);
-    EXPECT_EQ(history[2][1], 83UL);
-    for (int i = 2; i < 52; i++) {
-        EXPECT_EQ(history[2][i], 0UL);
+    EXPECT_EQ(history[i++], 251);
+    EXPECT_EQ(history[i++], 83);
+    for (; i < history.size(); i++) {
+        EXPECT_EQ(history[i], 0);
     }
 }
+
+TEST(storaged_test, uid_monitor) {
+    uid_monitor uidm;
+
+    uidm.io_history[200] = {
+        .start_ts = 100,
+        .entries = {
+            { "app1", {
+                .user_id = 0,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+              }
+            },
+            { "app2", {
+                .user_id = 0,
+                .uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF] = 1000,
+              }
+            },
+            { "app1", {
+                .user_id = 1,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+                .uid_ios.bytes[READ][FOREGROUND][CHARGER_ON] = 1000,
+              }
+            },
+        },
+    };
+
+    uidm.io_history[300] = {
+        .start_ts = 200,
+        .entries = {
+            { "app1", {
+                .user_id = 1,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_OFF] = 1000,
+              }
+            },
+            { "app3", {
+                .user_id = 0,
+                .uid_ios.bytes[READ][BACKGROUND][CHARGER_OFF] = 1000,
+              }
+            },
+        },
+    };
+
+    unordered_map<int, StoragedProto> protos;
+
+    uidm.update_uid_io_proto(&protos);
+
+    EXPECT_EQ(protos.size(), 2U);
+    EXPECT_EQ(protos.count(0), 1UL);
+    EXPECT_EQ(protos.count(1), 1UL);
+
+    EXPECT_EQ(protos[0].uid_io_usage().uid_io_items_size(), 2);
+    const UidIOItem& user_0_item_0 = protos[0].uid_io_usage().uid_io_items(0);
+    EXPECT_EQ(user_0_item_0.end_ts(), 200UL);
+    EXPECT_EQ(user_0_item_0.records().start_ts(), 100UL);
+    EXPECT_EQ(user_0_item_0.records().entries_size(), 2);
+    EXPECT_EQ(user_0_item_0.records().entries(0).uid_name(), "app1");
+    EXPECT_EQ(user_0_item_0.records().entries(0).user_id(), 0UL);
+    EXPECT_EQ(user_0_item_0.records().entries(0).uid_io().wr_fg_chg_on(), 1000UL);
+    EXPECT_EQ(user_0_item_0.records().entries(1).uid_name(), "app2");
+    EXPECT_EQ(user_0_item_0.records().entries(1).user_id(), 0UL);
+    EXPECT_EQ(user_0_item_0.records().entries(1).uid_io().rd_fg_chg_off(), 1000UL);
+    const UidIOItem& user_0_item_1 = protos[0].uid_io_usage().uid_io_items(1);
+    EXPECT_EQ(user_0_item_1.end_ts(), 300UL);
+    EXPECT_EQ(user_0_item_1.records().start_ts(), 200UL);
+    EXPECT_EQ(user_0_item_1.records().entries_size(), 1);
+    EXPECT_EQ(user_0_item_1.records().entries(0).uid_name(), "app3");
+    EXPECT_EQ(user_0_item_1.records().entries(0).user_id(), 0UL);
+    EXPECT_EQ(user_0_item_1.records().entries(0).uid_io().rd_bg_chg_off(), 1000UL);
+
+    EXPECT_EQ(protos[1].uid_io_usage().uid_io_items_size(), 2);
+    const UidIOItem& user_1_item_0 = protos[1].uid_io_usage().uid_io_items(0);
+    EXPECT_EQ(user_1_item_0.end_ts(), 200UL);
+    EXPECT_EQ(user_1_item_0.records().start_ts(), 100UL);
+    EXPECT_EQ(user_1_item_0.records().entries_size(), 1);
+    EXPECT_EQ(user_1_item_0.records().entries(0).uid_name(), "app1");
+    EXPECT_EQ(user_1_item_0.records().entries(0).user_id(), 1UL);
+    EXPECT_EQ(user_1_item_0.records().entries(0).uid_io().rd_fg_chg_on(), 1000UL);
+    EXPECT_EQ(user_1_item_0.records().entries(0).uid_io().wr_fg_chg_on(), 1000UL);
+    const UidIOItem& user_1_item_1 = protos[1].uid_io_usage().uid_io_items(1);
+    EXPECT_EQ(user_1_item_1.end_ts(), 300UL);
+    EXPECT_EQ(user_1_item_1.records().start_ts(), 200UL);
+    EXPECT_EQ(user_1_item_1.records().entries_size(), 1);
+    EXPECT_EQ(user_1_item_1.records().entries(0).uid_name(), "app1");
+    EXPECT_EQ(user_1_item_1.records().entries(0).user_id(), 1UL);
+    EXPECT_EQ(user_1_item_1.records().entries(0).uid_io().wr_fg_chg_off(), 1000UL);
+
+    uidm.io_history.clear();
+
+    uidm.io_history[300] = {
+        .start_ts = 200,
+        .entries = {
+            { "app1", {
+                .user_id = 0,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+              }
+            },
+        },
+    };
+
+    uidm.io_history[400] = {
+        .start_ts = 300,
+        .entries = {
+            { "app1", {
+                .user_id = 0,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+              }
+            },
+        },
+    };
+
+    uidm.load_uid_io_proto(protos[0].uid_io_usage());
+    uidm.load_uid_io_proto(protos[1].uid_io_usage());
+
+    EXPECT_EQ(uidm.io_history.size(), 3UL);
+    EXPECT_EQ(uidm.io_history.count(200), 1UL);
+    EXPECT_EQ(uidm.io_history.count(300), 1UL);
+    EXPECT_EQ(uidm.io_history.count(400), 1UL);
+
+    EXPECT_EQ(uidm.io_history[200].start_ts, 100UL);
+    const vector<struct uid_record>& entries_0 = uidm.io_history[200].entries;
+    EXPECT_EQ(entries_0.size(), 3UL);
+    EXPECT_EQ(entries_0[0].name, "app1");
+    EXPECT_EQ(entries_0[0].ios.user_id, 0UL);
+    EXPECT_EQ(entries_0[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+    EXPECT_EQ(entries_0[1].name, "app2");
+    EXPECT_EQ(entries_0[1].ios.user_id, 0UL);
+    EXPECT_EQ(entries_0[1].ios.uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF], 1000UL);
+    EXPECT_EQ(entries_0[2].name, "app1");
+    EXPECT_EQ(entries_0[2].ios.user_id, 1UL);
+    EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+    EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[READ][FOREGROUND][CHARGER_ON], 1000UL);
+
+    EXPECT_EQ(uidm.io_history[300].start_ts, 200UL);
+    const vector<struct uid_record>& entries_1 = uidm.io_history[300].entries;
+    EXPECT_EQ(entries_1.size(), 3UL);
+    EXPECT_EQ(entries_1[0].name, "app1");
+    EXPECT_EQ(entries_1[0].ios.user_id, 0UL);
+    EXPECT_EQ(entries_1[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+    EXPECT_EQ(entries_1[1].name, "app3");
+    EXPECT_EQ(entries_1[1].ios.user_id, 0UL);
+    EXPECT_EQ(entries_1[1].ios.uid_ios.bytes[READ][BACKGROUND][CHARGER_OFF], 1000UL);
+    EXPECT_EQ(entries_1[2].name, "app1");
+    EXPECT_EQ(entries_1[2].ios.user_id, 1UL);
+    EXPECT_EQ(entries_1[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_OFF], 1000UL);
+
+    EXPECT_EQ(uidm.io_history[400].start_ts, 300UL);
+    const vector<struct uid_record>& entries_2 = uidm.io_history[400].entries;
+    EXPECT_EQ(entries_2.size(), 1UL);
+    EXPECT_EQ(entries_2[0].name, "app1");
+    EXPECT_EQ(entries_2[0].ios.user_id, 0UL);
+    EXPECT_EQ(entries_2[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+
+    map<string, io_usage> merged_entries_0 = merge_io_usage(entries_0);
+    EXPECT_EQ(merged_entries_0.size(), 2UL);
+    EXPECT_EQ(merged_entries_0.count("app1"), 1UL);
+    EXPECT_EQ(merged_entries_0.count("app2"), 1UL);
+    EXPECT_EQ(merged_entries_0["app1"].bytes[READ][FOREGROUND][CHARGER_ON], 1000UL);
+    EXPECT_EQ(merged_entries_0["app1"].bytes[WRITE][FOREGROUND][CHARGER_ON], 2000UL);
+    EXPECT_EQ(merged_entries_0["app2"].bytes[READ][FOREGROUND][CHARGER_OFF], 1000UL);
+
+    map<string, io_usage> merged_entries_1 = merge_io_usage(entries_1);
+    EXPECT_EQ(merged_entries_1.size(), 2UL);
+    EXPECT_EQ(merged_entries_1.count("app1"), 1UL);
+    EXPECT_EQ(merged_entries_1.count("app3"), 1UL);
+    EXPECT_EQ(merged_entries_1["app1"].bytes[WRITE][FOREGROUND][CHARGER_OFF], 1000UL);
+    EXPECT_EQ(merged_entries_1["app1"].bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+    EXPECT_EQ(merged_entries_1["app3"].bytes[READ][BACKGROUND][CHARGER_OFF], 1000UL);
+
+    map<string, io_usage> merged_entries_2 = merge_io_usage(entries_2);
+    EXPECT_EQ(merged_entries_2.size(), 1UL);
+    EXPECT_EQ(merged_entries_2.count("app1"), 1UL);
+    EXPECT_EQ(merged_entries_2["app1"].bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+
+    uidm.clear_user_history(0);
+
+    EXPECT_EQ(uidm.io_history.size(), 2UL);
+    EXPECT_EQ(uidm.io_history.count(200), 1UL);
+    EXPECT_EQ(uidm.io_history.count(300), 1UL);
+
+    EXPECT_EQ(uidm.io_history[200].entries.size(), 1UL);
+    EXPECT_EQ(uidm.io_history[300].entries.size(), 1UL);
+
+    uidm.clear_user_history(1);
+
+    EXPECT_EQ(uidm.io_history.size(), 0UL);
+}
diff --git a/storaged/uid_info.cpp b/storaged/uid_info.cpp
new file mode 100644
index 0000000..58e3fd2
--- /dev/null
+++ b/storaged/uid_info.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 <binder/Parcel.h>
+
+#include "uid_info.h"
+
+using namespace android;
+using namespace android::os::storaged;
+
+status_t UidInfo::writeToParcel(Parcel* parcel) const {
+    parcel->writeInt32(uid);
+    parcel->writeCString(name.c_str());
+    parcel->write(&io, sizeof(io));
+
+    parcel->writeInt32(tasks.size());
+    for (const auto& task_it : tasks) {
+        parcel->writeInt32(task_it.first);
+        parcel->writeCString(task_it.second.comm.c_str());
+        parcel->write(&task_it.second.io, sizeof(task_it.second.io));
+    }
+    return NO_ERROR;
+}
+
+status_t UidInfo::readFromParcel(const Parcel* parcel) {
+    uid = parcel->readInt32();
+    name = parcel->readCString();
+    parcel->read(&io, sizeof(io));
+
+    uint32_t tasks_size = parcel->readInt32();
+    for (uint32_t i = 0; i < tasks_size; i++) {
+        task_info task;
+        task.pid = parcel->readInt32();
+        task.comm = parcel->readCString();
+        parcel->read(&task.io, sizeof(task.io));
+        tasks[task.pid] = task;
+    }
+    return NO_ERROR;
+}
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 94029d8..c4795a7 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -59,3 +59,12 @@
 $(INPUT_H_LABELS_H): $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/generate-input.h-labels.py $(UAPI_INPUT_EVENT_CODES_H)
 $(INPUT_H_LABELS_H):
 	$(transform-generated-source)
+
+# We only want 'r' on userdebug and eng builds.
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := r.c
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/upstream-netbsd/include/
+LOCAL_MODULE := r
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_EXECUTABLE)
diff --git a/toolbox/r.c b/toolbox/r.c
new file mode 100644
index 0000000..b96cdb2
--- /dev/null
+++ b/toolbox/r.c
@@ -0,0 +1,102 @@
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#if __LP64__
+#define strtoptr strtoull
+#else
+#define strtoptr strtoul
+#endif
+
+static int usage()
+{
+    fprintf(stderr,"r [-b|-s] <address> [<value>]\n");
+    return -1;
+}
+
+int main(int argc, char *argv[])
+{
+    if(argc < 2) return usage();
+
+    int width = 4;
+    if(!strcmp(argv[1], "-b")) {
+        width = 1;
+        argc--;
+        argv++;
+    } else if(!strcmp(argv[1], "-s")) {
+        width = 2;
+        argc--;
+        argv++;
+    }
+
+    if(argc < 2) return usage();
+    uintptr_t addr = strtoptr(argv[1], 0, 16);
+
+    uintptr_t endaddr = 0;
+    char* end = strchr(argv[1], '-');
+    if (end)
+        endaddr = strtoptr(end + 1, 0, 16);
+
+    if (!endaddr)
+        endaddr = addr + width - 1;
+
+    if (endaddr <= addr) {
+        fprintf(stderr, "end address <= start address\n");
+        return -1;
+    }
+
+    bool set = false;
+    uint32_t value = 0;
+    if(argc > 2) {
+        set = true;
+        value = strtoul(argv[2], 0, 16);
+    }
+
+    int fd = open("/dev/mem", O_RDWR | O_SYNC);
+    if(fd < 0) {
+        fprintf(stderr,"cannot open /dev/mem\n");
+        return -1;
+    }
+
+    off64_t mmap_start = addr & ~(PAGE_SIZE - 1);
+    size_t mmap_size = endaddr - mmap_start + 1;
+    mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+
+    void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE,
+                        MAP_SHARED, fd, mmap_start);
+
+    if(page == MAP_FAILED){
+        fprintf(stderr,"cannot mmap region\n");
+        return -1;
+    }
+
+    while (addr <= endaddr) {
+        switch(width){
+        case 4: {
+            uint32_t* x = (uint32_t*) (((uintptr_t) page) + (addr & 4095));
+            if(set) *x = value;
+            fprintf(stderr,"%08"PRIxPTR": %08x\n", addr, *x);
+            break;
+        }
+        case 2: {
+            uint16_t* x = (uint16_t*) (((uintptr_t) page) + (addr & 4095));
+            if(set) *x = value;
+            fprintf(stderr,"%08"PRIxPTR": %04x\n", addr, *x);
+            break;
+        }
+        case 1: {
+            uint8_t* x = (uint8_t*) (((uintptr_t) page) + (addr & 4095));
+            if(set) *x = value;
+            fprintf(stderr,"%08"PRIxPTR": %02x\n", addr, *x);
+            break;
+        }
+        }
+        addr += width;
+    }
+    return 0;
+}
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 0820fa0..322a63d 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -31,6 +31,7 @@
         "trusty_keymaster_ipc.cpp",
         "trusty_keymaster_main.cpp",
     ],
+    cflags: ["-Wall", "-Werror"],
     shared_libs: [
         "libcrypto",
         "libcutils",
diff --git a/trusty/keymaster/trusty_keymaster_main.cpp b/trusty/keymaster/trusty_keymaster_main.cpp
index 9c2ae2d..ed78b7f 100644
--- a/trusty/keymaster/trusty_keymaster_main.cpp
+++ b/trusty/keymaster/trusty_keymaster_main.cpp
@@ -289,7 +289,6 @@
     std::unique_ptr<const uint8_t[]> deleter(key.key_material);
 
     printf("=== Signing with imported ECDSA key ===\n");
-    keymaster_ec_sign_params_t sign_params = {DIGEST_NONE};
     size_t message_len = 30 /* arbitrary */;
     std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     memset(message.get(), 'a', message_len);
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
index 88d6240..c48deed 100644
--- a/trusty/libtrusty/Android.bp
+++ b/trusty/libtrusty/Android.bp
@@ -22,6 +22,7 @@
 
     srcs: ["trusty.c"],
     export_include_dirs: ["include"],
+    cflags: ["-Wall", "-Werror"],
 
     shared_libs: ["liblog"],
 }
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp
index 25a3cb0..1e8467f 100644
--- a/trusty/libtrusty/tipc-test/Android.bp
+++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -25,4 +25,5 @@
         "liblog",
     ],
     gtest: false,
+    cflags: ["-Wall", "-Werror"],
 }