libnetutil: Check dhcp respose packet length am: 61f25d4a36 am: cee6d38c55 am: 29d054046f am: c29049f4f8 am: 3de04e9840 am: 19702dc499 am: 8191e9ba87 am: 804323c195 am: 4f5f00d879 am: 3a1ff1ad71 am: bab7b1ecd9
am: cdf0fc6018

Change-Id: If9bc99849e62fe38cdf52180ff76390c452e0784
diff --git a/.clang-format-2 b/.clang-format-2
index aab4665..41591ce 100644
--- a/.clang-format-2
+++ b/.clang-format-2
@@ -1,4 +1,5 @@
 BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: Inline
 ColumnLimit: 100
 CommentPragmas: NOLINT:.*
 DerivePointerAlignment: false
diff --git a/.clang-format-4 b/.clang-format-4
index 1497447..ae4a451 100644
--- a/.clang-format-4
+++ b/.clang-format-4
@@ -1,5 +1,6 @@
 BasedOnStyle: Google
 AccessModifierOffset: -2
+AllowShortFunctionsOnASingleLine: Inline
 ColumnLimit: 100
 CommentPragmas: NOLINT:.*
 DerivePointerAlignment: false
diff --git a/adb/Android.bp b/adb/Android.bp
new file mode 100644
index 0000000..41f7b89
--- /dev/null
+++ b/adb/Android.bp
@@ -0,0 +1,51 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+python_binary_host {
+  name: "adb_integration_test_adb",
+  main: "test_adb.py",
+  srcs: [
+    "test_adb.py",
+  ],
+  libs: [
+    "adb_py",
+  ],
+  version: {
+    py2: {
+      enabled: true,
+    },
+    py3: {
+      enabled: false,
+    },
+  },
+}
+
+python_binary_host {
+  name: "adb_integration_test_device",
+  main: "test_device.py",
+  srcs: [
+    "test_device.py",
+  ],
+  libs: [
+    "adb_py",
+  ],
+  version: {
+    py2: {
+      enabled: true,
+    },
+    py3: {
+      enabled: false,
+    },
+  },
+}
diff --git a/adb/Android.mk b/adb/Android.mk
index e841205..6ed01fa 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -5,23 +5,27 @@
 
 LOCAL_PATH:= $(call my-dir)
 
+include $(LOCAL_PATH)/../platform_tools_tool_version.mk
+
 adb_host_sanitize :=
 adb_target_sanitize :=
 
-adb_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
-
 ADB_COMMON_CFLAGS := \
     -Wall -Wextra -Werror \
     -Wno-unused-parameter \
     -Wno-missing-field-initializers \
     -Wvla \
-    -DADB_REVISION='"$(adb_version)"' \
+    -DADB_VERSION="\"$(tool_version)\"" \
+
+ADB_COMMON_posix_CFLAGS := \
+    -Wexit-time-destructors \
+    -Wthread-safety \
 
 ADB_COMMON_linux_CFLAGS := \
-    -Wexit-time-destructors \
+    $(ADB_COMMON_posix_CFLAGS) \
 
 ADB_COMMON_darwin_CFLAGS := \
-    -Wexit-time-destructors \
+    $(ADB_COMMON_posix_CFLAGS) \
 
 # Define windows.h and tchar.h Unicode preprocessor symbols so that
 # CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the
@@ -81,12 +85,14 @@
 
 LIBADB_darwin_SRC_FILES := \
     sysdeps_unix.cpp \
+    sysdeps/posix/network.cpp \
     client/usb_dispatch.cpp \
     client/usb_libusb.cpp \
     client/usb_osx.cpp \
 
 LIBADB_linux_SRC_FILES := \
     sysdeps_unix.cpp \
+    sysdeps/posix/network.cpp \
     client/usb_dispatch.cpp \
     client/usb_libusb.cpp \
     client/usb_linux.cpp \
@@ -123,12 +129,13 @@
     $(LIBADB_SRC_FILES) \
     adbd_auth.cpp \
     jdwp_service.cpp \
+    sysdeps/posix/network.cpp \
 
 LOCAL_SANITIZE := $(adb_target_sanitize)
 
 # Even though we're building a static library (and thus there's no link step for
 # this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase
+LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libqemu_pipe libbase
 
 LOCAL_WHOLE_STATIC_LIBRARIES := libadbd_usb
 
@@ -217,15 +224,15 @@
 LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS)
 LOCAL_SRC_FILES_windows := $(LIBADB_TEST_windows_SRCS)
 LOCAL_SANITIZE := $(adb_host_sanitize)
-LOCAL_SHARED_LIBRARIES := libbase
 LOCAL_STATIC_LIBRARIES := \
     libadb \
+    libbase \
     libcrypto_utils \
     libcrypto \
     libcutils \
     libdiagnose_usb \
     libmdnssd \
-    libgmock_host
+    libgmock_host \
 
 LOCAL_STATIC_LIBRARIES_linux := libusb
 LOCAL_STATIC_LIBRARIES_darwin := libusb
@@ -293,7 +300,7 @@
     libcrypto \
     libdiagnose_usb \
     liblog \
-    libmdnssd
+    libmdnssd \
 
 # Don't use libcutils on Windows.
 LOCAL_STATIC_LIBRARIES_darwin := libcutils
@@ -360,7 +367,9 @@
 LOCAL_STRIP_MODULE := keep_symbols
 LOCAL_STATIC_LIBRARIES := \
     libadbd \
+    libavb_user \
     libbase \
+    libqemu_pipe \
     libbootloader_message \
     libfs_mgr \
     libfec \
@@ -379,4 +388,9 @@
 
 include $(BUILD_EXECUTABLE)
 
+# adb integration test
+# =========================================================
+$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_adb.BUILT))
+$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_device.BUILT))
+
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 577e9b9..bfb1144 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -31,6 +31,8 @@
 #include <time.h>
 
 #include <chrono>
+#include <condition_variable>
+#include <mutex>
 #include <string>
 #include <thread>
 #include <vector>
@@ -48,6 +50,7 @@
 #include "adb_io.h"
 #include "adb_listeners.h"
 #include "adb_utils.h"
+#include "sysdeps/chrono.h"
 #include "transport.h"
 
 #if !ADB_HOST
@@ -59,10 +62,12 @@
 
 std::string adb_version() {
     // Don't change the format of this --- it's parsed by ddmlib.
-    return android::base::StringPrintf("Android Debug Bridge version %d.%d.%d\n"
-                                       "Revision %s\n",
-                                       ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
-                                       ADB_REVISION);
+    return android::base::StringPrintf(
+        "Android Debug Bridge version %d.%d.%d\n"
+        "Version %s\n"
+        "Installed as %s\n",
+        ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_VERSION,
+        android::base::GetExecutablePath().c_str());
 }
 
 void fatal(const char *fmt, ...) {
@@ -251,6 +256,19 @@
     send_packet(cp, t);
 }
 
+#if ADB_HOST
+
+void SendConnectOnHost(atransport* t) {
+    // Send an empty message before A_CNXN message. This is because the data toggle of the ep_out on
+    // host and ep_in on device may not be the same.
+    apacket* p = get_apacket();
+    CHECK(p);
+    send_packet(p, t);
+    send_connect(t);
+}
+
+#endif
+
 // qual_overwrite is used to overwrite a qualifier string.  dst is a
 // pointer to a char pointer.  It is assumed that if *dst is non-NULL, it
 // was malloc'ed and needs to freed.  *dst will be set to a dup of src.
@@ -297,29 +315,25 @@
     const std::string& type = pieces[0];
     if (type == "bootloader") {
         D("setting connection_state to kCsBootloader");
-        t->connection_state = kCsBootloader;
-        update_transports();
+        t->SetConnectionState(kCsBootloader);
     } else if (type == "device") {
         D("setting connection_state to kCsDevice");
-        t->connection_state = kCsDevice;
-        update_transports();
+        t->SetConnectionState(kCsDevice);
     } else if (type == "recovery") {
         D("setting connection_state to kCsRecovery");
-        t->connection_state = kCsRecovery;
-        update_transports();
+        t->SetConnectionState(kCsRecovery);
     } else if (type == "sideload") {
         D("setting connection_state to kCsSideload");
-        t->connection_state = kCsSideload;
-        update_transports();
+        t->SetConnectionState(kCsSideload);
     } else {
         D("setting connection_state to kCsHost");
-        t->connection_state = kCsHost;
+        t->SetConnectionState(kCsHost);
     }
 }
 
 static void handle_new_connection(atransport* t, apacket* p) {
-    if (t->connection_state != kCsOffline) {
-        t->connection_state = kCsOffline;
+    if (t->GetConnectionState() != kCsOffline) {
+        t->SetConnectionState(kCsOffline);
         handle_offline(t);
     }
 
@@ -338,6 +352,8 @@
         send_auth_request(t);
     }
 #endif
+
+    update_transports();
 }
 
 void handle_packet(apacket *p, atransport *t)
@@ -353,10 +369,10 @@
         if (p->msg.arg0){
             send_packet(p, t);
 #if ADB_HOST
-            send_connect(t);
+            SendConnectOnHost(t);
 #endif
         } else {
-            t->connection_state = kCsOffline;
+            t->SetConnectionState(kCsOffline);
             handle_offline(t);
             send_packet(p, t);
         }
@@ -370,7 +386,9 @@
         switch (p->msg.arg0) {
 #if ADB_HOST
             case ADB_AUTH_TOKEN:
-                t->connection_state = kCsUnauthorized;
+                if (t->GetConnectionState() == kCsOffline) {
+                    t->SetConnectionState(kCsUnauthorized);
+                }
                 send_auth_response(p->data, p->msg.data_length, t);
                 break;
 #else
@@ -389,7 +407,7 @@
                 break;
 #endif
             default:
-                t->connection_state = kCsOffline;
+                t->SetConnectionState(kCsOffline);
                 handle_offline(t);
                 break;
         }
@@ -505,8 +523,8 @@
     if (!_try_make_handle_noninheritable(h)) {
         // Show the handle value to give us a clue in case we have problems
         // with pseudo-handle values.
-        fprintf(stderr, "Cannot make handle 0x%p non-inheritable: %s\n",
-                h, android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        fprintf(stderr, "adb: cannot make handle 0x%p non-inheritable: %s\n", h,
+                android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return false;
     }
 
@@ -521,7 +539,7 @@
     HANDLE pipe_read_raw = NULL;
     HANDLE pipe_write_raw = NULL;
     if (!CreatePipe(&pipe_read_raw, &pipe_write_raw, sa, 0)) {
-        fprintf(stderr, "Cannot create pipe: %s\n",
+        fprintf(stderr, "adb: CreatePipe failed: %s\n",
                 android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return false;
     }
@@ -552,7 +570,8 @@
     std::unique_ptr<FILE, decltype(&fclose)> stream(nullptr, fclose);
 
     if (original_fd == -1) {
-        fprintf(stderr, "Failed to get file descriptor for %s: %s\n", output_name, strerror(errno));
+        fprintf(stderr, "adb: failed to get file descriptor for %s: %s\n", output_name,
+                strerror(errno));
         return EXIT_FAILURE;
     }
 
@@ -564,7 +583,7 @@
         // call this function if subprocesses may be started concurrently.
         const int fd = dup(original_fd);
         if (fd == -1) {
-            fprintf(stderr, "Failed to duplicate file descriptor for %s: %s\n", output_name,
+            fprintf(stderr, "adb: failed to duplicate file descriptor for %s: %s\n", output_name,
                     strerror(errno));
             return EXIT_FAILURE;
         }
@@ -572,7 +591,7 @@
         // Note that although we call fdopen() below with a binary flag, it may not adhere to that
         // flag, so we have to set the mode manually.
         if (_setmode(fd, _O_BINARY) == -1) {
-            fprintf(stderr, "Failed to set binary mode for duplicate of %s: %s\n", output_name,
+            fprintf(stderr, "adb: failed to set binary mode for duplicate of %s: %s\n", output_name,
                     strerror(errno));
             unix_close(fd);
             return EXIT_FAILURE;
@@ -580,7 +599,7 @@
 
         stream.reset(fdopen(fd, "wb"));
         if (stream.get() == nullptr) {
-            fprintf(stderr, "Failed to open duplicate stream for %s: %s\n", output_name,
+            fprintf(stderr, "adb: failed to open duplicate stream for %s: %s\n", output_name,
                     strerror(errno));
             unix_close(fd);
             return EXIT_FAILURE;
@@ -589,7 +608,7 @@
         // Unbuffer the stream because it will be buffered by default and we want subprocess output
         // to be shown immediately.
         if (setvbuf(stream.get(), NULL, _IONBF, 0) == -1) {
-            fprintf(stderr, "Failed to unbuffer %s: %s\n", output_name, strerror(errno));
+            fprintf(stderr, "adb: failed to unbuffer %s: %s\n", output_name, strerror(errno));
             return EXIT_FAILURE;
         }
 
@@ -606,7 +625,7 @@
             if (err == ERROR_BROKEN_PIPE) {
                 return EXIT_SUCCESS;
             } else {
-                fprintf(stderr, "Failed to read from %s: %s\n", output_name,
+                fprintf(stderr, "adb: failed to read from %s: %s\n", output_name,
                         android::base::SystemErrorCodeToString(err).c_str());
                 return EXIT_FAILURE;
             }
@@ -617,8 +636,8 @@
             // fwrite() actually calls adb_fwrite() which can write UTF-8 to the console.
             const size_t bytes_written = fwrite(buf, 1, bytes_read, stream.get());
             if (bytes_written != bytes_read) {
-                fprintf(stderr, "Only wrote %zu of %lu bytes to %s\n", bytes_written, bytes_read,
-                        output_name);
+                fprintf(stderr, "adb: error: only wrote %zu of %lu bytes to %s\n", bytes_written,
+                        bytes_read, output_name);
                 return EXIT_FAILURE;
             }
         }
@@ -661,7 +680,7 @@
             FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING,
             FILE_ATTRIBUTE_NORMAL, NULL));
     if (nul_read.get() == INVALID_HANDLE_VALUE) {
-        fprintf(stderr, "Cannot open 'nul': %s\n",
+        fprintf(stderr, "adb: CreateFileW 'nul' failed: %s\n",
                 android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return -1;
     }
@@ -723,8 +742,7 @@
         // If this fires, either handle values are larger than 32-bits or else
         // there is a bug in our casting.
         // https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203%28v=vs.85%29.aspx
-        fprintf(stderr, "Cannot fit pipe handle value into 32-bits: 0x%p\n",
-                ack_write.get());
+        fprintf(stderr, "adb: cannot fit pipe handle value into 32-bits: 0x%p\n", ack_write.get());
         return -1;
     }
 
@@ -734,7 +752,7 @@
                                                    arraysize(program_path));
     if ((module_result >= arraysize(program_path)) || (module_result == 0)) {
         // String truncation or some other error.
-        fprintf(stderr, "Cannot get executable path: %s\n",
+        fprintf(stderr, "adb: cannot get executable path: %s\n",
                 android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return -1;
     }
@@ -759,7 +777,7 @@
             NULL,                    /* use parent's starting directory */
             &startup,                 /* startup info, i.e. std handles */
             &pinfo )) {
-        fprintf(stderr, "Cannot create process: %s\n",
+        fprintf(stderr, "adb: CreateProcessW failed: %s\n",
                 android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return -1;
     }
@@ -789,7 +807,7 @@
             _beginthreadex(NULL, 0, _redirect_stdout_thread, stdout_read.get(),
                            0, NULL)));
     if (stdout_thread.get() == nullptr) {
-        fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
+        fprintf(stderr, "adb: cannot create thread: %s\n", strerror(errno));
         return -1;
     }
     stdout_read.release();  // Transfer ownership to new thread
@@ -798,7 +816,7 @@
             _beginthreadex(NULL, 0, _redirect_stderr_thread, stderr_read.get(),
                            0, NULL)));
     if (stderr_thread.get() == nullptr) {
-        fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
+        fprintf(stderr, "adb: cannot create thread: %s\n", strerror(errno));
         return -1;
     }
     stderr_read.release();  // Transfer ownership to new thread
@@ -843,22 +861,20 @@
     if (wait_result == WAIT_TIMEOUT) {
         // Threads did not finish after waiting a little while. Perhaps the
         // server didn't close pipes, or it is hung.
-        fprintf(stderr, "Timed-out waiting for threads to finish reading from "
-                "ADB Server\n");
+        fprintf(stderr, "adb: timed out waiting for threads to finish reading from ADB server\n");
         // Process handles are signaled when the process exits, so if we wait
         // on the handle for 0 seconds and it returns 'timeout', that means that
         // the process is still running.
         if (WaitForSingleObject(process_handle.get(), 0) == WAIT_TIMEOUT) {
             // We could TerminateProcess(), but that seems somewhat presumptive.
-            fprintf(stderr, "ADB Server is running: process id %lu\n",
-                    pinfo.dwProcessId);
+            fprintf(stderr, "adb: server is running with process id %lu\n", pinfo.dwProcessId);
         }
         return -1;
     }
 
     if (wait_result != WAIT_OBJECT_0) {
-        fprintf(stderr, "Unexpected result waiting for threads: %lu: %s\n",
-                wait_result, android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        fprintf(stderr, "adb: unexpected result waiting for threads: %lu: %s\n", wait_result,
+                android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return -1;
     }
 
@@ -892,7 +908,7 @@
         int result = execl(path.c_str(), "adb", "-L", socket_spec.c_str(), "fork-server", "server",
                            "--reply-fd", reply_fd, NULL);
         // this should not return
-        fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
+        fprintf(stderr, "adb: execl returned %d: %s\n", result, strerror(errno));
     } else  {
         // parent side of the fork
 
@@ -1032,26 +1048,21 @@
     SendProtocolString(fd, s);
     return 0;
 }
-#endif
 
 int handle_host_request(const char* service, TransportType type,
                         const char* serial, int reply_fd, asocket* s) {
     if (strcmp(service, "kill") == 0) {
         fprintf(stderr, "adb server killed by remote request\n");
         fflush(stdout);
+
+        // Send a reply even though we don't read it anymore, so that old versions
+        // of adb that do read it don't spew error messages.
         SendOkay(reply_fd);
 
-        // On Windows, if the process exits with open sockets that
-        // shutdown(SD_SEND) has not been called on, TCP RST segments will be
-        // sent to the peers which will cause their next recv() to error-out
-        // with WSAECONNRESET. In the case of this code, that means the client
-        // may not read the OKAY sent above.
-        adb_shutdown(reply_fd);
-
+        // Rely on process exit to close the socket for us.
         android::base::quick_exit(0);
     }
 
-#if ADB_HOST
     // "transport:" is used for switching transport with a specified serial number
     // "transport-usb:" is used for switching transport to the only USB transport
     // "transport-local:" is used for switching transport to the only local transport
@@ -1096,16 +1107,10 @@
     if (!strcmp(service, "reconnect-offline")) {
         std::string response;
         close_usb_devices([&response](const atransport* transport) {
-            switch (transport->connection_state) {
+            switch (transport->GetConnectionState()) {
                 case kCsOffline:
                 case kCsUnauthorized:
-                    response += "reconnecting ";
-                    if (transport->serial) {
-                        response += transport->serial;
-                    } else {
-                        response += "<unknown>";
-                    }
-                    response += "\n";
+                    response += "reconnecting " + transport->serial_name() + "\n";
                     return true;
                 default:
                     return false;
@@ -1129,17 +1134,16 @@
         return 0;
     }
 
-#if ADB_HOST
     if (!strcmp(service, "host-features")) {
         FeatureSet features = supported_features();
         // Abuse features to report libusb status.
         if (should_use_libusb()) {
             features.insert(kFeatureLibusb);
         }
+        features.insert(kFeaturePushSync);
         SendOkay(reply_fd, FeatureSetToString(features));
         return 0;
     }
-#endif
 
     // remove TCP transport
     if (!strncmp(service, "disconnect:", 11)) {
@@ -1209,15 +1213,63 @@
     }
 
     if (!strcmp(service, "reconnect")) {
-        if (s->transport != nullptr) {
-            kick_transport(s->transport);
+        std::string response;
+        atransport* t = acquire_one_transport(type, serial, nullptr, &response, true);
+        if (t != nullptr) {
+            kick_transport(t);
+            response =
+                "reconnecting " + t->serial_name() + " [" + t->connection_state_name() + "]\n";
         }
-        return SendOkay(reply_fd, "done");
+        return SendOkay(reply_fd, response);
     }
-#endif // ADB_HOST
 
     int ret = handle_forward_request(service, type, serial, reply_fd);
     if (ret >= 0)
       return ret - 1;
     return -1;
 }
+
+static auto& init_mutex = *new std::mutex();
+static auto& init_cv = *new std::condition_variable();
+static bool device_scan_complete = false;
+static bool transports_ready = false;
+
+void update_transport_status() {
+    bool result = iterate_transports([](const atransport* t) {
+        if (t->type == kTransportUsb && t->online != 1) {
+            return false;
+        }
+        return true;
+    });
+
+    bool ready;
+    {
+        std::lock_guard<std::mutex> lock(init_mutex);
+        transports_ready = result;
+        ready = transports_ready && device_scan_complete;
+    }
+
+    if (ready) {
+        init_cv.notify_all();
+    }
+}
+
+void adb_notify_device_scan_complete() {
+    {
+        std::lock_guard<std::mutex> lock(init_mutex);
+        if (device_scan_complete) {
+            return;
+        }
+
+        device_scan_complete = true;
+    }
+
+    update_transport_status();
+}
+
+void adb_wait_for_device_initialization() {
+    std::unique_lock<std::mutex> lock(init_mutex);
+    init_cv.wait_for(lock, 3s, []() { return device_scan_complete && transports_ready; });
+}
+
+#endif  // ADB_HOST
diff --git a/adb/adb.h b/adb/adb.h
index aea5fb8..d6b2b81 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -139,7 +139,7 @@
 int get_available_local_transport_index();
 #endif
 int  init_socket_transport(atransport *t, int s, int port, int local);
-void init_usb_transport(atransport *t, usb_handle *usb, ConnectionState state);
+void init_usb_transport(atransport* t, usb_handle* usb);
 
 std::string getEmulatorSerialString(int console_port);
 #if ADB_HOST
@@ -222,7 +222,24 @@
 void handle_offline(atransport *t);
 
 void send_connect(atransport *t);
+#if ADB_HOST
+void SendConnectOnHost(atransport* t);
+#endif
 
 void parse_banner(const std::string&, atransport* t);
 
+// On startup, the adb server needs to wait until all of the connected devices are ready.
+// To do this, we need to know when the scan has identified all of the potential new transports, and
+// when each transport becomes ready.
+// TODO: Do this for mDNS as well, instead of just USB?
+
+// We've found all of the transports we potentially care about.
+void adb_notify_device_scan_complete();
+
+// One or more transports have changed status, check to see if we're ready.
+void update_transport_status();
+
+// Wait until device scan has completed and every transport is ready, or a timeout elapses.
+void adb_wait_for_device_initialization();
+
 #endif
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index ef52189..e533a00 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -28,12 +28,15 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <condition_variable>
+#include <mutex>
 #include <string>
 #include <thread>
 #include <vector>
 
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
 #include <cutils/sockets.h>
 
 #include "adb_io.h"
@@ -120,9 +123,9 @@
     return false;
 }
 
-int _adb_connect(const std::string& service, std::string* error) {
+static int _adb_connect(const std::string& service, std::string* error) {
     D("_adb_connect: %s", service.c_str());
-    if (service.empty() || service.size() > MAX_PAYLOAD_V1) {
+    if (service.empty() || service.size() > MAX_PAYLOAD) {
         *error = android::base::StringPrintf("bad service name length (%zd)",
                                              service.size());
         return -1;
@@ -136,8 +139,7 @@
         return -2;
     }
 
-    if ((memcmp(&service[0],"host",4) != 0 || service == "host:reconnect") &&
-        switch_socket_transport(fd, error)) {
+    if (memcmp(&service[0], "host", 4) != 0 && switch_socket_transport(fd, error)) {
         return -1;
     }
 
@@ -147,42 +149,58 @@
         return -1;
     }
 
-    if (service != "reconnect") {
-        if (!adb_status(fd, error)) {
-            adb_close(fd);
-            return -1;
-        }
+    if (!adb_status(fd, error)) {
+        adb_close(fd);
+        return -1;
     }
 
     D("_adb_connect: return fd %d", fd);
     return fd;
 }
 
+bool adb_kill_server() {
+    D("adb_kill_server");
+    std::string reason;
+    int fd = socket_spec_connect(__adb_server_socket_spec, &reason);
+    if (fd < 0) {
+        fprintf(stderr, "cannot connect to daemon at %s: %s\n", __adb_server_socket_spec,
+                reason.c_str());
+        return true;
+    }
+
+    if (!SendProtocolString(fd, "host:kill")) {
+        fprintf(stderr, "error: write failure during connection: %s\n", strerror(errno));
+        return false;
+    }
+
+    ReadOrderlyShutdown(fd);
+    return true;
+}
+
 int adb_connect(const std::string& service, std::string* error) {
     // first query the adb server's version
     int fd = _adb_connect("host:version", error);
 
     D("adb_connect: service %s", service.c_str());
     if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) {
-        fprintf(stderr,"** Cannot start server on remote host\n");
+        fprintf(stderr, "* cannot start server on remote host\n");
         // error is the original network connection error
         return fd;
     } else if (fd == -2) {
-        fprintf(stdout, "* daemon not running. starting it now at %s *\n", __adb_server_socket_spec);
+        fprintf(stderr, "* daemon not running; starting now at %s\n", __adb_server_socket_spec);
     start_server:
         if (launch_server(__adb_server_socket_spec)) {
-            fprintf(stderr,"* failed to start daemon *\n");
+            fprintf(stderr, "* failed to start daemon\n");
             // launch_server() has already printed detailed error info, so just
             // return a generic error string about the overall adb_connect()
             // that the caller requested.
             *error = "cannot connect to daemon";
             return -1;
         } else {
-            fprintf(stdout,"* daemon started successfully *\n");
+            fprintf(stderr, "* daemon started successfully\n");
         }
-        // Give the server some time to start properly and detect devices.
-        std::this_thread::sleep_for(3s);
-        // fall through to _adb_connect
+        // The server will wait until it detects all of its connected devices before acking.
+        // Fall through to _adb_connect.
     } else {
         // If a server is already running, check its version matches.
         int version = ADB_SERVER_VERSION - 1;
@@ -213,20 +231,9 @@
         }
 
         if (version != ADB_SERVER_VERSION) {
-            printf("adb server version (%d) doesn't match this client (%d); killing...\n",
-                   version, ADB_SERVER_VERSION);
-            fd = _adb_connect("host:kill", error);
-            if (fd >= 0) {
-                ReadOrderlyShutdown(fd);
-                adb_close(fd);
-            } else {
-                // If we couldn't connect to the server or had some other error,
-                // report it, but still try to start the server.
-                fprintf(stderr, "error: %s\n", error->c_str());
-            }
-
-            /* XXX can we better detect its death? */
-            std::this_thread::sleep_for(2s);
+            fprintf(stderr, "adb server version (%d) doesn't match this client (%d); killing...\n",
+                    version, ADB_SERVER_VERSION);
+            adb_kill_server();
             goto start_server;
         }
     }
@@ -240,7 +247,7 @@
     if (fd == -1) {
         D("_adb_connect error: %s", error->c_str());
     } else if(fd == -2) {
-        fprintf(stderr,"** daemon still not running\n");
+        fprintf(stderr, "* daemon still not running\n");
     }
     D("adb_connect: return fd %d", fd);
 
diff --git a/adb/adb_client.h b/adb/adb_client.h
index d07c1e9..fabec00 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -26,7 +26,9 @@
 // Connect to adb, connect to the named service, and return a valid fd for
 // interacting with that service upon success or a negative number on failure.
 int adb_connect(const std::string& service, std::string* _Nonnull error);
-int _adb_connect(const std::string& service, std::string* _Nonnull error);
+
+// Kill the currently running adb server, if it exists.
+bool adb_kill_server();
 
 // Connect to adb, connect to the named service, returns true if the connection
 // succeeded AND the service returned OKAY. Outputs any returned error otherwise.
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index ca8729e..38e3116 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -31,7 +31,7 @@
 
 bool SendProtocolString(int fd, const std::string& s) {
     unsigned int length = s.size();
-    if (length > MAX_PAYLOAD_V1 - 4) {
+    if (length > MAX_PAYLOAD - 4) {
         errno = EMSGSIZE;
         return false;
     }
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 18b1492..30cb29b 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -19,8 +19,12 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <algorithm>
+#include <list>
+
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
 #include <cutils/sockets.h>
 
 #include "socket_spec.h"
@@ -64,8 +68,9 @@
 
 // listener_list retains ownership of all created alistener objects. Removing an alistener from
 // this list will cause it to be deleted.
+static auto& listener_list_mutex = *new std::mutex();
 typedef std::list<std::unique_ptr<alistener>> ListenerList;
-static ListenerList& listener_list = *new ListenerList();
+static ListenerList& listener_list GUARDED_BY(listener_list_mutex) = *new ListenerList();
 
 static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
     if (ev & FDE_READ) {
@@ -108,7 +113,8 @@
 }
 
 // Called as a transport disconnect function. |arg| is the raw alistener*.
-static void listener_disconnect(void* arg, atransport*) {
+static void listener_disconnect(void* arg, atransport*) EXCLUDES(listener_list_mutex) {
+    std::lock_guard<std::mutex> lock(listener_list_mutex);
     for (auto iter = listener_list.begin(); iter != listener_list.end(); ++iter) {
         if (iter->get() == arg) {
             (*iter)->transport = nullptr;
@@ -119,7 +125,8 @@
 }
 
 // Write the list of current listeners (network redirections) into a string.
-std::string format_listeners() {
+std::string format_listeners() EXCLUDES(listener_list_mutex) {
+    std::lock_guard<std::mutex> lock(listener_list_mutex);
     std::string result;
     for (auto& l : listener_list) {
         // Ignore special listeners like those for *smartsocket*
@@ -135,7 +142,9 @@
     return result;
 }
 
-InstallStatus remove_listener(const char* local_name, atransport* transport) {
+InstallStatus remove_listener(const char* local_name, atransport* transport)
+    EXCLUDES(listener_list_mutex) {
+    std::lock_guard<std::mutex> lock(listener_list_mutex);
     for (auto iter = listener_list.begin(); iter != listener_list.end(); ++iter) {
         if (local_name == (*iter)->local_name) {
             listener_list.erase(iter);
@@ -145,7 +154,8 @@
     return INSTALL_STATUS_LISTENER_NOT_FOUND;
 }
 
-void remove_all_listeners() {
+void remove_all_listeners() EXCLUDES(listener_list_mutex) {
+    std::lock_guard<std::mutex> lock(listener_list_mutex);
     auto iter = listener_list.begin();
     while (iter != listener_list.end()) {
         // Never remove smart sockets.
@@ -157,9 +167,18 @@
     }
 }
 
+void close_smartsockets() EXCLUDES(listener_list_mutex) {
+    std::lock_guard<std::mutex> lock(listener_list_mutex);
+    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));
+}
+
 InstallStatus install_listener(const std::string& local_name, const char* connect_to,
                                atransport* transport, int no_rebind, int* resolved_tcp_port,
-                               std::string* error) {
+                               std::string* error) EXCLUDES(listener_list_mutex) {
+    std::lock_guard<std::mutex> lock(listener_list_mutex);
     for (auto& l : listener_list) {
         if (local_name == l->local_name) {
             // Can't repurpose a smartsocket.
@@ -212,7 +231,7 @@
 
     if (transport) {
         listener->disconnect.opaque = listener.get();
-        listener->disconnect.func   = listener_disconnect;
+        listener->disconnect.func = listener_disconnect;
         transport->AddDisconnect(&listener->disconnect);
     }
 
diff --git a/adb/adb_listeners.h b/adb/adb_listeners.h
index 8eba00a..70a2ee1 100644
--- a/adb/adb_listeners.h
+++ b/adb/adb_listeners.h
@@ -41,4 +41,6 @@
 InstallStatus remove_listener(const char* local_name, atransport* transport);
 void remove_all_listeners(void);
 
+void close_smartsockets();
+
 #endif /* __ADB_LISTENERS_H */
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index c369d60..eac923d 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -155,7 +155,7 @@
     }
 #endif
 
-#if !defined(_WIN32)
+#if ADB_HOST && !defined(_WIN32)
     // adb historically ignored $ANDROID_LOG_TAGS but passed it through to logcat.
     // If set, move it out of the way so that libbase logging doesn't try to parse it.
     std::string log_tags;
@@ -168,7 +168,7 @@
 
     android::base::InitLogging(argv, &AdbLogger);
 
-#if !defined(_WIN32)
+#if ADB_HOST && !defined(_WIN32)
     // Put $ANDROID_LOG_TAGS back so we can pass it to logcat.
     if (!log_tags.empty()) setenv("ANDROID_LOG_TAGS", log_tags.c_str(), 1);
 #endif
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index aaffa29..fc6560c 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -58,6 +58,9 @@
 void adb_trace_init(char**);
 void adb_trace_enable(AdbTrace trace_tag);
 
+// Include <atomic> before stdatomic.h (introduced in cutils/trace.h) to avoid compile error.
+#include <atomic>
+
 #define ATRACE_TAG ATRACE_TAG_ADB
 #include <cutils/trace.h>
 #include <utils/Trace.h>
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 31d3dc6..6f2403d 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -267,8 +267,8 @@
     adb_close(fd);
 }
 
-int usage(const char* fmt, ...) {
-    fprintf(stderr, "adb: ");
+int syntax_error(const char* fmt, ...) {
+    fprintf(stderr, "adb: usage: ");
 
     va_list ap;
     va_start(ap, fmt);
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index e0ad103..11c0ec9 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -17,11 +17,14 @@
 #ifndef _ADB_UTILS_H_
 #define _ADB_UTILS_H_
 
+#include <condition_variable>
+#include <mutex>
 #include <string>
+#include <vector>
 
 #include <android-base/macros.h>
 
-int usage(const char*, ...);
+int syntax_error(const char*, ...);
 
 void close_stdin();
 
@@ -53,4 +56,37 @@
 bool forward_targets_are_valid(const std::string& source, const std::string& dest,
                                std::string* error);
 
+// A thread-safe blocking queue.
+template <typename T>
+class BlockingQueue {
+    std::mutex mutex;
+    std::condition_variable cv;
+    std::vector<T> queue;
+
+  public:
+    void Push(const T& t) {
+        {
+            std::unique_lock<std::mutex> lock(mutex);
+            queue.push_back(t);
+        }
+        cv.notify_one();
+    }
+
+    template <typename Fn>
+    void PopAll(Fn fn) {
+        std::vector<T> popped;
+
+        {
+            std::unique_lock<std::mutex> lock(mutex);
+            cv.wait(lock, [this]() { return !queue.empty(); });
+            popped = std::move(queue);
+            queue.clear();
+        }
+
+        for (const T& t : popped) {
+            fn(t);
+        }
+    }
+};
+
 #endif
diff --git a/adb/adbd_auth.cpp b/adb/adbd_auth.cpp
index b5f87be..3488ad1 100644
--- a/adb/adbd_auth.cpp
+++ b/adb/adbd_auth.cpp
@@ -217,8 +217,8 @@
     send_packet(p, t);
 }
 
-void adbd_auth_verified(atransport *t)
-{
+void adbd_auth_verified(atransport* t) {
+    LOG(INFO) << "adb client authorized";
     handle_online(t);
     send_connect(t);
 }
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index b3e391b..372a3b4 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -137,7 +137,7 @@
             SetSrcFile(&line[strlen(BUGZ_OK_PREFIX)]);
         } else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
             const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
-            fprintf(stderr, "Device failed to take a zipped bugreport: %s\n", error_message);
+            fprintf(stderr, "adb: device failed to take a zipped bugreport: %s\n", error_message);
             status_ = -1;
         } else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) {
             // progress_line should have the following format:
@@ -196,7 +196,7 @@
 };
 
 int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
-    if (argc > 2) return usage("usage: adb bugreport [PATH]");
+    if (argc > 2) return syntax_error("adb bugreport [PATH]");
 
     // Gets bugreportz version.
     std::string bugz_stdout, bugz_stderr;
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 606203c..62798cd 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -92,6 +92,16 @@
 }
 #endif
 
+void adb_server_cleanup() {
+    // Upon exit, we want to clean up in the following order:
+    //   1. close_smartsockets, so that we don't get any new clients
+    //   2. kick_all_transports, to avoid writing only part of a packet to a transport.
+    //   3. usb_cleanup, to tear down the USB stack.
+    close_smartsockets();
+    kick_all_transports();
+    usb_cleanup();
+}
+
 int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply_fd) {
 #if defined(_WIN32)
     // adb start-server starts us up with stdout and stderr hooked up to
@@ -111,12 +121,18 @@
     SetConsoleCtrlHandler(ctrlc_handler, TRUE);
 #else
     signal(SIGINT, [](int) {
-        android::base::quick_exit(0);
+        fdevent_run_on_main_thread([]() { android::base::quick_exit(0); });
     });
 #endif
 
-    init_transport_registration();
+    if (is_daemon) {
+        close_stdin();
+        setup_daemon_logging();
+    }
 
+    android::base::at_quick_exit(adb_server_cleanup);
+
+    init_transport_registration();
     init_mdns_transport_discovery();
 
     usb_init();
@@ -137,11 +153,6 @@
         std::this_thread::sleep_for(100ms);
     }
 
-    if (is_daemon) {
-        close_stdin();
-        setup_daemon_logging();
-    }
-
     adb_auth_init();
 
     if (is_daemon) {
@@ -156,33 +167,38 @@
         }
 #endif
 
-        // Inform our parent that we are up and running.
+        // Wait for the USB scan to complete before notifying the parent that we're up.
+        // We need to perform this in a thread, because we would otherwise block the event loop.
+        std::thread notify_thread([ack_reply_fd]() {
+            adb_wait_for_device_initialization();
 
-        // Any error output written to stderr now goes to adb.log. We could
-        // keep around a copy of the stderr fd and use that to write any errors
-        // encountered by the following code, but that is probably overkill.
+            // Any error output written to stderr now goes to adb.log. We could
+            // keep around a copy of the stderr fd and use that to write any errors
+            // encountered by the following code, but that is probably overkill.
 #if defined(_WIN32)
-        const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
-        const CHAR ack[] = "OK\n";
-        const DWORD bytes_to_write = arraysize(ack) - 1;
-        DWORD written = 0;
-        if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
-            fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
-                  android::base::SystemErrorCodeToString(GetLastError()).c_str());
-        }
-        if (written != bytes_to_write) {
-            fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes",
-                  bytes_to_write, written);
-        }
-        CloseHandle(ack_reply_handle);
+            const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
+            const CHAR ack[] = "OK\n";
+            const DWORD bytes_to_write = arraysize(ack) - 1;
+            DWORD written = 0;
+            if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
+                fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
+                      android::base::SystemErrorCodeToString(GetLastError()).c_str());
+            }
+            if (written != bytes_to_write) {
+                fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes", bytes_to_write,
+                      written);
+            }
+            CloseHandle(ack_reply_handle);
 #else
-        // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
-        // "OKAY".
-        if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
-            fatal_errno("error writing ACK to fd %d", ack_reply_fd);
-        }
-        unix_close(ack_reply_fd);
+            // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
+            // "OKAY".
+            if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
+                fatal_errno("error writing ACK to fd %d", ack_reply_fd);
+            }
+            unix_close(ack_reply_fd);
 #endif
+        });
+        notify_thread.detach();
     }
 
     D("Event loop starting");
diff --git a/adb/client/usb_dispatch.cpp b/adb/client/usb_dispatch.cpp
index bfc8e16..ce57731 100644
--- a/adb/client/usb_dispatch.cpp
+++ b/adb/client/usb_dispatch.cpp
@@ -27,6 +27,14 @@
     }
 }
 
+void usb_cleanup() {
+    if (should_use_libusb()) {
+        libusb::usb_cleanup();
+    } else {
+        native::usb_cleanup();
+    }
+}
+
 int usb_write(usb_handle* h, const void* data, int len) {
     return should_use_libusb()
                ? libusb::usb_write(reinterpret_cast<libusb::usb_handle*>(h), data, len)
@@ -48,3 +56,9 @@
     should_use_libusb() ? libusb::usb_kick(reinterpret_cast<libusb::usb_handle*>(h))
                         : native::usb_kick(reinterpret_cast<native::usb_handle*>(h));
 }
+
+size_t usb_get_max_packet_size(usb_handle* h) {
+    return should_use_libusb()
+               ? libusb::usb_get_max_packet_size(reinterpret_cast<libusb::usb_handle*>(h))
+               : native::usb_get_max_packet_size(reinterpret_cast<native::usb_handle*>(h));
+}
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 7adb262..e7f44c6 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -37,6 +37,7 @@
 #include <android-base/strings.h>
 
 #include "adb.h"
+#include "adb_utils.h"
 #include "transport.h"
 #include "usb.h"
 
@@ -62,12 +63,11 @@
 using unique_device_handle = std::unique_ptr<libusb_device_handle, DeviceHandleDeleter>;
 
 struct transfer_info {
-    transfer_info(const char* name, uint16_t zero_mask) :
-        name(name),
-        transfer(libusb_alloc_transfer(0)),
-        zero_mask(zero_mask)
-    {
-    }
+    transfer_info(const char* name, uint16_t zero_mask, bool is_bulk_out)
+        : name(name),
+          transfer(libusb_alloc_transfer(0)),
+          is_bulk_out(is_bulk_out),
+          zero_mask(zero_mask) {}
 
     ~transfer_info() {
         libusb_free_transfer(transfer);
@@ -75,6 +75,7 @@
 
     const char* name;
     libusb_transfer* transfer;
+    bool is_bulk_out;
     bool transfer_complete;
     std::condition_variable cv;
     std::mutex mutex;
@@ -91,17 +92,17 @@
 struct usb_handle : public ::usb_handle {
     usb_handle(const std::string& device_address, const std::string& serial,
                unique_device_handle&& device_handle, uint8_t interface, uint8_t bulk_in,
-               uint8_t bulk_out, size_t zero_mask)
+               uint8_t bulk_out, size_t zero_mask, size_t max_packet_size)
         : device_address(device_address),
           serial(serial),
           closing(false),
           device_handle(device_handle.release()),
-          read("read", zero_mask),
-          write("write", zero_mask),
+          read("read", zero_mask, false),
+          write("write", zero_mask, true),
           interface(interface),
           bulk_in(bulk_in),
-          bulk_out(bulk_out) {
-    }
+          bulk_out(bulk_out),
+          max_packet_size(max_packet_size) {}
 
     ~usb_handle() {
         Close();
@@ -144,19 +145,43 @@
     uint8_t interface;
     uint8_t bulk_in;
     uint8_t bulk_out;
+
+    size_t max_packet_size;
 };
 
 static auto& usb_handles = *new std::unordered_map<std::string, std::unique_ptr<usb_handle>>();
 static auto& usb_handles_mutex = *new std::mutex();
 
-static std::thread* device_poll_thread = nullptr;
-static std::atomic<bool> terminate_device_poll_thread(false);
+static libusb_hotplug_callback_handle hotplug_handle;
 
 static std::string get_device_address(libusb_device* device) {
     return StringPrintf("usb:%d:%d", libusb_get_bus_number(device),
                         libusb_get_device_address(device));
 }
 
+#if defined(__linux__)
+static std::string get_device_serial_path(libusb_device* device) {
+    uint8_t ports[7];
+    int port_count = libusb_get_port_numbers(device, ports, 7);
+    if (port_count < 0) return "";
+
+    std::string path =
+        StringPrintf("/sys/bus/usb/devices/%d-%d", libusb_get_bus_number(device), ports[0]);
+    for (int port = 1; port < port_count; ++port) {
+        path += StringPrintf(".%d", ports[port]);
+    }
+    path += "/serial";
+    return path;
+}
+
+static std::string get_device_dev_path(libusb_device* device) {
+    uint8_t ports[7];
+    int port_count = libusb_get_port_numbers(device, ports, 7);
+    if (port_count < 0) return "";
+    return StringPrintf("/dev/bus/usb/%03u/%03u", libusb_get_bus_number(device), ports[0]);
+}
+#endif
+
 static bool endpoint_is_output(uint8_t endpoint) {
     return (endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT;
 }
@@ -166,191 +191,263 @@
            (write_length & zero_mask) == 0;
 }
 
-static void poll_for_devices() {
-    libusb_device** list;
-    adb_thread_setname("device poll");
-    while (!terminate_device_poll_thread) {
-        const ssize_t device_count = libusb_get_device_list(nullptr, &list);
+static void process_device(libusb_device* device) {
+    std::string device_address = get_device_address(device);
+    std::string device_serial;
 
-        LOG(VERBOSE) << "found " << device_count << " attached devices";
-
-        for (ssize_t i = 0; i < device_count; ++i) {
-            libusb_device* device = list[i];
-            std::string device_address = get_device_address(device);
-            std::string device_serial;
-
-            // Figure out if we want to open the device.
-            libusb_device_descriptor device_desc;
-            int rc = libusb_get_device_descriptor(device, &device_desc);
-            if (rc != 0) {
-                LOG(WARNING) << "failed to get device descriptor for device at " << device_address
-                             << ": " << libusb_error_name(rc);
-            }
-
-            if (device_desc.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) {
-                // Assume that all Android devices have the device class set to per interface.
-                // TODO: Is this assumption valid?
-                LOG(VERBOSE) << "skipping device with incorrect class at " << device_address;
-                continue;
-            }
-
-            libusb_config_descriptor* config_raw;
-            rc = libusb_get_active_config_descriptor(device, &config_raw);
-            if (rc != 0) {
-                LOG(WARNING) << "failed to get active config descriptor for device at "
-                             << device_address << ": " << libusb_error_name(rc);
-                continue;
-            }
-            const unique_config_descriptor config(config_raw);
-
-            // Use size_t for interface_num so <iostream>s don't mangle it.
-            size_t interface_num;
-            uint16_t zero_mask;
-            uint8_t bulk_in = 0, bulk_out = 0;
-            bool found_adb = false;
-
-            for (interface_num = 0; interface_num < config->bNumInterfaces; ++interface_num) {
-                const libusb_interface& interface = config->interface[interface_num];
-                if (interface.num_altsetting != 1) {
-                    // Assume that interfaces with alternate settings aren't adb interfaces.
-                    // TODO: Is this assumption valid?
-                    LOG(VERBOSE) << "skipping interface with incorrect num_altsetting at "
-                                 << device_address << " (interface " << interface_num << ")";
-                    continue;
-                }
-
-                const libusb_interface_descriptor& interface_desc = interface.altsetting[0];
-                if (!is_adb_interface(interface_desc.bInterfaceClass,
-                                      interface_desc.bInterfaceSubClass,
-                                      interface_desc.bInterfaceProtocol)) {
-                    LOG(VERBOSE) << "skipping non-adb interface at " << device_address
-                                 << " (interface " << interface_num << ")";
-                    continue;
-                }
-
-                LOG(VERBOSE) << "found potential adb interface at " << device_address
-                             << " (interface " << interface_num << ")";
-
-                bool found_in = false;
-                bool found_out = false;
-                for (size_t endpoint_num = 0; endpoint_num < interface_desc.bNumEndpoints;
-                     ++endpoint_num) {
-                    const auto& endpoint_desc = interface_desc.endpoint[endpoint_num];
-                    const uint8_t endpoint_addr = endpoint_desc.bEndpointAddress;
-                    const uint8_t endpoint_attr = endpoint_desc.bmAttributes;
-
-                    const uint8_t transfer_type = endpoint_attr & LIBUSB_TRANSFER_TYPE_MASK;
-
-                    if (transfer_type != LIBUSB_TRANSFER_TYPE_BULK) {
-                        continue;
-                    }
-
-                    if (endpoint_is_output(endpoint_addr) && !found_out) {
-                        found_out = true;
-                        bulk_out = endpoint_addr;
-                        zero_mask = endpoint_desc.wMaxPacketSize - 1;
-                    } else if (!endpoint_is_output(endpoint_addr) && !found_in) {
-                        found_in = true;
-                        bulk_in = endpoint_addr;
-                    }
-                }
-
-                if (found_in && found_out) {
-                    found_adb = true;
-                    break;
-                } else {
-                    LOG(VERBOSE) << "rejecting potential adb interface at " << device_address
-                                 << "(interface " << interface_num << "): missing bulk endpoints "
-                                 << "(found_in = " << found_in << ", found_out = " << found_out
-                                 << ")";
-                }
-            }
-
-            if (!found_adb) {
-                LOG(VERBOSE) << "skipping device with no adb interfaces at " << device_address;
-                continue;
-            }
-
-            {
-                std::unique_lock<std::mutex> lock(usb_handles_mutex);
-                if (usb_handles.find(device_address) != usb_handles.end()) {
-                    LOG(VERBOSE) << "device at " << device_address
-                                 << " has already been registered, skipping";
-                    continue;
-                }
-            }
-
-            libusb_device_handle* handle_raw;
-            rc = libusb_open(list[i], &handle_raw);
-            if (rc != 0) {
-                LOG(WARNING) << "failed to open usb device at " << device_address << ": "
-                             << libusb_error_name(rc);
-                continue;
-            }
-
-            unique_device_handle handle(handle_raw);
-            LOG(DEBUG) << "successfully opened adb device at " << device_address << ", "
-                       << StringPrintf("bulk_in = %#x, bulk_out = %#x", bulk_in, bulk_out);
-
-            device_serial.resize(255);
-            rc = libusb_get_string_descriptor_ascii(
-                handle_raw, device_desc.iSerialNumber,
-                reinterpret_cast<unsigned char*>(&device_serial[0]), device_serial.length());
-            if (rc == 0) {
-                LOG(WARNING) << "received empty serial from device at " << device_address;
-                continue;
-            } else if (rc < 0) {
-                LOG(WARNING) << "failed to get serial from device at " << device_address
-                             << libusb_error_name(rc);
-                continue;
-            }
-            device_serial.resize(rc);
-
-            // Try to reset the device.
-            rc = libusb_reset_device(handle_raw);
-            if (rc != 0) {
-                LOG(WARNING) << "failed to reset opened device '" << device_serial
-                             << "': " << libusb_error_name(rc);
-                continue;
-            }
-
-            // WARNING: this isn't released via RAII.
-            rc = libusb_claim_interface(handle.get(), interface_num);
-            if (rc != 0) {
-                LOG(WARNING) << "failed to claim adb interface for device '" << device_serial << "'"
-                             << libusb_error_name(rc);
-                continue;
-            }
-
-            for (uint8_t endpoint : {bulk_in, bulk_out}) {
-                rc = libusb_clear_halt(handle.get(), endpoint);
-                if (rc != 0) {
-                    LOG(WARNING) << "failed to clear halt on device '" << device_serial
-                                 << "' endpoint 0x" << std::hex << endpoint << ": "
-                                 << libusb_error_name(rc);
-                    libusb_release_interface(handle.get(), interface_num);
-                    continue;
-                }
-            }
-
-            auto result =
-                std::make_unique<usb_handle>(device_address, device_serial, std::move(handle),
-                                             interface_num, bulk_in, bulk_out, zero_mask);
-            usb_handle* usb_handle_raw = result.get();
-
-            {
-                std::unique_lock<std::mutex> lock(usb_handles_mutex);
-                usb_handles[device_address] = std::move(result);
-            }
-
-            register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(), 1);
-
-            LOG(INFO) << "registered new usb device '" << device_serial << "'";
-        }
-        libusb_free_device_list(list, 1);
-
-        std::this_thread::sleep_for(500ms);
+    // Figure out if we want to open the device.
+    libusb_device_descriptor device_desc;
+    int rc = libusb_get_device_descriptor(device, &device_desc);
+    if (rc != 0) {
+        LOG(WARNING) << "failed to get device descriptor for device at " << device_address << ": "
+                     << libusb_error_name(rc);
+        return;
     }
+
+    if (device_desc.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) {
+        // Assume that all Android devices have the device class set to per interface.
+        // TODO: Is this assumption valid?
+        LOG(VERBOSE) << "skipping device with incorrect class at " << device_address;
+        return;
+    }
+
+    libusb_config_descriptor* config_raw;
+    rc = libusb_get_active_config_descriptor(device, &config_raw);
+    if (rc != 0) {
+        LOG(WARNING) << "failed to get active config descriptor for device at " << device_address
+                     << ": " << libusb_error_name(rc);
+        return;
+    }
+    const unique_config_descriptor config(config_raw);
+
+    // Use size_t for interface_num so <iostream>s don't mangle it.
+    size_t interface_num;
+    uint16_t zero_mask;
+    uint8_t bulk_in = 0, bulk_out = 0;
+    size_t packet_size = 0;
+    bool found_adb = false;
+
+    for (interface_num = 0; interface_num < config->bNumInterfaces; ++interface_num) {
+        const libusb_interface& interface = config->interface[interface_num];
+        if (interface.num_altsetting != 1) {
+            // Assume that interfaces with alternate settings aren't adb interfaces.
+            // TODO: Is this assumption valid?
+            LOG(VERBOSE) << "skipping interface with incorrect num_altsetting at " << device_address
+                         << " (interface " << interface_num << ")";
+            continue;
+        }
+
+        const libusb_interface_descriptor& interface_desc = interface.altsetting[0];
+        if (!is_adb_interface(interface_desc.bInterfaceClass, interface_desc.bInterfaceSubClass,
+                              interface_desc.bInterfaceProtocol)) {
+            LOG(VERBOSE) << "skipping non-adb interface at " << device_address << " (interface "
+                         << interface_num << ")";
+            continue;
+        }
+
+        LOG(VERBOSE) << "found potential adb interface at " << device_address << " (interface "
+                     << interface_num << ")";
+
+        bool found_in = false;
+        bool found_out = false;
+        for (size_t endpoint_num = 0; endpoint_num < interface_desc.bNumEndpoints; ++endpoint_num) {
+            const auto& endpoint_desc = interface_desc.endpoint[endpoint_num];
+            const uint8_t endpoint_addr = endpoint_desc.bEndpointAddress;
+            const uint8_t endpoint_attr = endpoint_desc.bmAttributes;
+
+            const uint8_t transfer_type = endpoint_attr & LIBUSB_TRANSFER_TYPE_MASK;
+
+            if (transfer_type != LIBUSB_TRANSFER_TYPE_BULK) {
+                continue;
+            }
+
+            if (endpoint_is_output(endpoint_addr) && !found_out) {
+                found_out = true;
+                bulk_out = endpoint_addr;
+                zero_mask = endpoint_desc.wMaxPacketSize - 1;
+            } else if (!endpoint_is_output(endpoint_addr) && !found_in) {
+                found_in = true;
+                bulk_in = endpoint_addr;
+            }
+
+            size_t endpoint_packet_size = endpoint_desc.wMaxPacketSize;
+            CHECK(endpoint_packet_size != 0);
+            if (packet_size == 0) {
+                packet_size = endpoint_packet_size;
+            } else {
+                CHECK(packet_size == endpoint_packet_size);
+            }
+        }
+
+        if (found_in && found_out) {
+            found_adb = true;
+            break;
+        } else {
+            LOG(VERBOSE) << "rejecting potential adb interface at " << device_address
+                         << "(interface " << interface_num << "): missing bulk endpoints "
+                         << "(found_in = " << found_in << ", found_out = " << found_out << ")";
+        }
+    }
+
+    if (!found_adb) {
+        LOG(VERBOSE) << "skipping device with no adb interfaces at " << device_address;
+        return;
+    }
+
+    {
+        std::unique_lock<std::mutex> lock(usb_handles_mutex);
+        if (usb_handles.find(device_address) != usb_handles.end()) {
+            LOG(VERBOSE) << "device at " << device_address
+                         << " has already been registered, skipping";
+            return;
+        }
+    }
+
+    bool writable = true;
+    libusb_device_handle* handle_raw = nullptr;
+    rc = libusb_open(device, &handle_raw);
+    unique_device_handle handle(handle_raw);
+    if (rc == 0) {
+        LOG(DEBUG) << "successfully opened adb device at " << device_address << ", "
+                   << StringPrintf("bulk_in = %#x, bulk_out = %#x", bulk_in, bulk_out);
+
+        device_serial.resize(255);
+        rc = libusb_get_string_descriptor_ascii(handle_raw, device_desc.iSerialNumber,
+                                                reinterpret_cast<unsigned char*>(&device_serial[0]),
+                                                device_serial.length());
+        if (rc == 0) {
+            LOG(WARNING) << "received empty serial from device at " << device_address;
+            return;
+        } else if (rc < 0) {
+            LOG(WARNING) << "failed to get serial from device at " << device_address
+                         << libusb_error_name(rc);
+            return;
+        }
+        device_serial.resize(rc);
+
+        // WARNING: this isn't released via RAII.
+        rc = libusb_claim_interface(handle.get(), interface_num);
+        if (rc != 0) {
+            LOG(WARNING) << "failed to claim adb interface for device '" << device_serial << "'"
+                         << libusb_error_name(rc);
+            return;
+        }
+
+        for (uint8_t endpoint : {bulk_in, bulk_out}) {
+            rc = libusb_clear_halt(handle.get(), endpoint);
+            if (rc != 0) {
+                LOG(WARNING) << "failed to clear halt on device '" << device_serial
+                             << "' endpoint 0x" << std::hex << endpoint << ": "
+                             << libusb_error_name(rc);
+                libusb_release_interface(handle.get(), interface_num);
+                return;
+            }
+        }
+    } else {
+        LOG(WARNING) << "failed to open usb device at " << device_address << ": "
+                     << libusb_error_name(rc);
+        writable = false;
+
+#if defined(__linux__)
+        // libusb doesn't think we should be messing around with devices we don't have
+        // write access to, but Linux at least lets us get the serial number anyway.
+        if (!android::base::ReadFileToString(get_device_serial_path(device), &device_serial)) {
+            // We don't actually want to treat an unknown serial as an error because
+            // devices aren't able to communicate a serial number in early bringup.
+            // http://b/20883914
+            device_serial = "unknown";
+        }
+        device_serial = android::base::Trim(device_serial);
+#else
+        // On Mac OS and Windows, we're screwed. But I don't think this situation actually
+        // happens on those OSes.
+        return;
+#endif
+    }
+
+    auto result =
+        std::make_unique<usb_handle>(device_address, device_serial, std::move(handle),
+                                     interface_num, bulk_in, bulk_out, zero_mask, packet_size);
+    usb_handle* usb_handle_raw = result.get();
+
+    {
+        std::unique_lock<std::mutex> lock(usb_handles_mutex);
+        usb_handles[device_address] = std::move(result);
+
+        register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(),
+                               writable);
+    }
+    LOG(INFO) << "registered new usb device '" << device_serial << "'";
+}
+
+static std::atomic<int> connecting_devices(0);
+
+static void device_connected(libusb_device* device) {
+#if defined(__linux__)
+    // Android's host linux libusb uses netlink instead of udev for device hotplug notification,
+    // which means we can get hotplug notifications before udev has updated ownership/perms on the
+    // device. Since we're not going to be able to link against the system's libudev any time soon,
+    // hack around this by inserting a sleep.
+    auto thread = std::thread([device]() {
+        std::string device_path = get_device_dev_path(device);
+        std::this_thread::sleep_for(1s);
+
+        process_device(device);
+        if (--connecting_devices == 0) {
+            adb_notify_device_scan_complete();
+        }
+    });
+    thread.detach();
+#else
+    process_device(device);
+#endif
+}
+
+static void device_disconnected(libusb_device* device) {
+    std::string device_address = get_device_address(device);
+
+    LOG(INFO) << "device disconnected: " << device_address;
+    std::unique_lock<std::mutex> lock(usb_handles_mutex);
+    auto it = usb_handles.find(device_address);
+    if (it != usb_handles.end()) {
+        if (!it->second->device_handle) {
+            // If the handle is null, we were never able to open the device.
+            unregister_usb_transport(it->second.get());
+            usb_handles.erase(it);
+        } else {
+            // Closure of the transport will erase the usb_handle.
+        }
+    }
+}
+
+static auto& hotplug_queue = *new BlockingQueue<std::pair<libusb_hotplug_event, libusb_device*>>();
+static void hotplug_thread() {
+    adb_thread_setname("libusb hotplug");
+    while (true) {
+        hotplug_queue.PopAll([](std::pair<libusb_hotplug_event, libusb_device*> pair) {
+            libusb_hotplug_event event = pair.first;
+            libusb_device* device = pair.second;
+            if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
+                device_connected(device);
+            } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
+                device_disconnected(device);
+            }
+        });
+    }
+}
+
+static int hotplug_callback(libusb_context*, libusb_device* device, libusb_hotplug_event event,
+                            void*) {
+    // We're called with the libusb lock taken. Call these on a separate thread outside of this
+    // function so that the usb_handle mutex is always taken before the libusb mutex.
+    static std::once_flag once;
+    std::call_once(once, []() { std::thread(hotplug_thread).detach(); });
+
+    if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
+        ++connecting_devices;
+    }
+    hotplug_queue.Push({event, device});
+    return 0;
 }
 
 void usb_init() {
@@ -360,6 +457,17 @@
         LOG(FATAL) << "failed to initialize libusb: " << libusb_error_name(rc);
     }
 
+    // Register the hotplug callback.
+    rc = libusb_hotplug_register_callback(
+        nullptr, static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
+                                                   LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
+        LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
+        LIBUSB_CLASS_PER_INTERFACE, hotplug_callback, nullptr, &hotplug_handle);
+
+    if (rc != LIBUSB_SUCCESS) {
+        LOG(FATAL) << "failed to register libusb hotplug callback";
+    }
+
     // Spawn a thread for libusb_handle_events.
     std::thread([]() {
         adb_thread_setname("libusb");
@@ -367,19 +475,10 @@
             libusb_handle_events(nullptr);
         }
     }).detach();
+}
 
-    // Spawn a thread to do device enumeration.
-    // TODO: Use libusb_hotplug_* instead?
-    device_poll_thread = new std::thread(poll_for_devices);
-    android::base::at_quick_exit([]() {
-        terminate_device_poll_thread = true;
-        std::unique_lock<std::mutex> lock(usb_handles_mutex);
-        for (auto& it : usb_handles) {
-            it.second->Close();
-        }
-        lock.unlock();
-        device_poll_thread->join();
-    });
+void usb_cleanup() {
+    libusb_hotplug_deregister_callback(nullptr, hotplug_handle);
 }
 
 // Dispatch a libusb transfer, unlock |device_lock|, and then wait for the result.
@@ -405,7 +504,8 @@
             return;
         }
 
-        if (transfer->actual_length != transfer->length) {
+        // usb_read() can return when receiving some data.
+        if (info->is_bulk_out && transfer->actual_length != transfer->length) {
             LOG(DEBUG) << info->name << " transfer incomplete, resubmitting";
             transfer->length -= transfer->actual_length;
             transfer->buffer += transfer->actual_length;
@@ -499,8 +599,12 @@
     info->transfer->num_iso_packets = 0;
 
     int rc = perform_usb_transfer(h, info, std::move(lock));
-    LOG(DEBUG) << "usb_read(" << len << ") = " << rc;
-    return rc;
+    LOG(DEBUG) << "usb_read(" << len << ") = " << rc << ", actual_length "
+               << info->transfer->actual_length;
+    if (rc < 0) {
+        return rc;
+    }
+    return info->transfer->actual_length;
 }
 
 int usb_close(usb_handle* h) {
@@ -516,4 +620,10 @@
 void usb_kick(usb_handle* h) {
     h->Close();
 }
+
+size_t usb_get_max_packet_size(usb_handle* h) {
+    CHECK(h->max_packet_size != 0);
+    return h->max_packet_size;
+}
+
 } // namespace libusb
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 13b7674..a7df0ed 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -65,6 +65,7 @@
     unsigned char ep_in;
     unsigned char ep_out;
 
+    size_t max_packet_size;
     unsigned zero_mask;
     unsigned writeable = 1;
 
@@ -120,9 +121,9 @@
 }
 
 static void find_usb_device(const std::string& base,
-        void (*register_device_callback)
-                (const char*, const char*, unsigned char, unsigned char, int, int, unsigned))
-{
+                            void (*register_device_callback)(const char*, const char*,
+                                                             unsigned char, unsigned char, int, int,
+                                                             unsigned, size_t)) {
     std::unique_ptr<DIR, int(*)(DIR*)> bus_dir(opendir(base.c_str()), closedir);
     if (!bus_dir) return;
 
@@ -144,6 +145,7 @@
             struct usb_interface_descriptor* interface;
             struct usb_endpoint_descriptor *ep1, *ep2;
             unsigned zero_mask = 0;
+            size_t max_packet_size = 0;
             unsigned vid, pid;
 
             if (contains_non_digit(de->d_name)) continue;
@@ -251,7 +253,8 @@
                             continue;
                         }
                             /* aproto 01 needs 0 termination */
-                        if(interface->bInterfaceProtocol == 0x01) {
+                        if (interface->bInterfaceProtocol == 0x01) {
+                            max_packet_size = ep1->wMaxPacketSize;
                             zero_mask = ep1->wMaxPacketSize - 1;
                         }
 
@@ -281,9 +284,9 @@
                             }
                         }
 
-                        register_device_callback(dev_name.c_str(), devpath,
-                                local_ep_in, local_ep_out,
-                                interface->bInterfaceNumber, device->iSerialNumber, zero_mask);
+                        register_device_callback(dev_name.c_str(), devpath, local_ep_in,
+                                                 local_ep_out, interface->bInterfaceNumber,
+                                                 device->iSerialNumber, zero_mask, max_packet_size);
                         break;
                     }
                 } else {
@@ -401,7 +404,6 @@
     }
 }
 
-
 int usb_write(usb_handle *h, const void *_data, int len)
 {
     D("++ usb_write ++");
@@ -429,19 +431,16 @@
     int n;
 
     D("++ usb_read ++");
-    while(len > 0) {
+    int orig_len = len;
+    while (len == orig_len) {
         int xfer = len;
 
         D("[ usb read %d fd = %d], path=%s", xfer, h->fd, h->path.c_str());
         n = usb_bulk_read(h, data, xfer);
         D("[ usb read %d ] = %d, path=%s", xfer, n, h->path.c_str());
-        if(n != xfer) {
+        if (n <= 0) {
             if((errno == ETIMEDOUT) && (h->fd != -1)) {
                 D("[ timeout ]");
-                if(n > 0){
-                    data += n;
-                    len -= n;
-                }
                 continue;
             }
             D("ERROR: n = %d, errno = %d (%s)",
@@ -449,12 +448,12 @@
             return -1;
         }
 
-        len -= xfer;
-        data += xfer;
+        len -= n;
+        data += n;
     }
 
     D("-- usb_read --");
-    return 0;
+    return orig_len - len;
 }
 
 void usb_kick(usb_handle* h) {
@@ -501,10 +500,13 @@
     return 0;
 }
 
-static void register_device(const char* dev_name, const char* dev_path,
-                            unsigned char ep_in, unsigned char ep_out,
-                            int interface, int serial_index,
-                            unsigned zero_mask) {
+size_t usb_get_max_packet_size(usb_handle* h) {
+    return h->max_packet_size;
+}
+
+static void register_device(const char* dev_name, const char* dev_path, unsigned char ep_in,
+                            unsigned char ep_out, int interface, int serial_index,
+                            unsigned zero_mask, size_t max_packet_size) {
     // Since Linux will not reassign the device ID (and dev_name) as long as the
     // device is open, we can add to the list here once we open it and remove
     // from the list when we're finally closed and everything will work out
@@ -527,6 +529,7 @@
     usb->ep_in = ep_in;
     usb->ep_out = ep_out;
     usb->zero_mask = zero_mask;
+    usb->max_packet_size = max_packet_size;
 
     // Initialize mark so we don't get garbage collected after the device scan.
     usb->mark = true;
@@ -574,7 +577,7 @@
     register_usb_transport(done_usb, serial.c_str(), dev_path, done_usb->writeable);
 }
 
-static void device_poll_thread(void*) {
+static void device_poll_thread() {
     adb_thread_setname("device poll");
     D("Created device thread");
     while (true) {
@@ -593,8 +596,9 @@
     actions.sa_handler = [](int) {};
     sigaction(SIGALRM, &actions, nullptr);
 
-    if (!adb_thread_create(device_poll_thread, nullptr)) {
-        fatal_errno("cannot create device_poll thread");
-    }
+    std::thread(device_poll_thread).detach();
 }
+
+void usb_cleanup() {}
+
 } // namespace native
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index d4fc7c0..4e1480f 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -38,6 +38,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
 
 #include "adb.h"
 #include "transport.h"
@@ -51,15 +52,21 @@
     UInt8 bulkOut;
     IOUSBInterfaceInterface190** interface;
     unsigned int zero_mask;
+    size_t max_packet_size;
 
     // For garbage collecting disconnected devices.
     bool mark;
     std::string devpath;
     std::atomic<bool> dead;
 
-    usb_handle() : bulkIn(0), bulkOut(0), interface(nullptr),
-        zero_mask(0), mark(false), dead(false) {
-    }
+    usb_handle()
+        : bulkIn(0),
+          bulkOut(0),
+          interface(nullptr),
+          zero_mask(0),
+          max_packet_size(0),
+          mark(false),
+          dead(false) {}
 };
 
 static std::atomic<bool> usb_inited_flag;
@@ -390,6 +397,7 @@
         }
 
         handle->zero_mask = maxPacketSize - 1;
+        handle->max_packet_size = maxPacketSize;
     }
 
     handle->interface = interface;
@@ -405,7 +413,7 @@
 
 std::mutex& operate_device_lock = *new std::mutex();
 
-static void RunLoopThread(void* unused) {
+static void RunLoopThread() {
     adb_thread_setname("RunLoop");
 
     VLOG(USB) << "RunLoopThread started";
@@ -422,7 +430,7 @@
     VLOG(USB) << "RunLoopThread done";
 }
 
-static void usb_cleanup() {
+void usb_cleanup() NO_THREAD_SAFETY_ANALYSIS {
     VLOG(USB) << "usb_cleanup";
     // Wait until usb operations in RunLoopThread finish, and prevent further operations.
     operate_device_lock.lock();
@@ -432,13 +440,9 @@
 void usb_init() {
     static bool initialized = false;
     if (!initialized) {
-        atexit(usb_cleanup);
-
         usb_inited_flag = false;
 
-        if (!adb_thread_create(RunLoopThread, nullptr)) {
-            fatal_errno("cannot create RunLoop thread");
-        }
+        std::thread(RunLoopThread).detach();
 
         // Wait for initialization to finish
         while (!usb_inited_flag) {
@@ -520,7 +524,7 @@
     }
 
     if (kIOReturnSuccess == result)
-        return 0;
+        return numBytes;
     else {
         LOG(ERROR) << "usb_read failed with status: " << std::hex << result;
     }
@@ -560,4 +564,9 @@
     std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
     usb_kick_locked(handle);
 }
+
+size_t usb_get_max_packet_size(usb_handle* handle) {
+    return handle->max_packet_size;
+}
+
 } // namespace native
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 640e91e..1620e6e 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -65,6 +65,9 @@
   /// Interface name
   wchar_t*      interface_name;
 
+  /// Maximum packet size.
+  unsigned max_packet_size;
+
   /// Mask for determining when to use zero length packets
   unsigned zero_mask;
 };
@@ -103,7 +106,7 @@
 
 /// Entry point for thread that polls (every second) for new usb interfaces.
 /// This routine calls find_devices in infinite loop.
-static void device_poll_thread(void*);
+static void device_poll_thread();
 
 /// Initializes this module
 void usb_init();
@@ -174,7 +177,7 @@
   return 1;
 }
 
-void device_poll_thread(void*) {
+void device_poll_thread() {
   adb_thread_setname("Device Poll");
   D("Created device thread");
 
@@ -203,7 +206,7 @@
   return DefWindowProcW(hwnd, uMsg, wParam, lParam);
 }
 
-static void _power_notification_thread(void*) {
+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
@@ -258,14 +261,12 @@
 }
 
 void usb_init() {
-  if (!adb_thread_create(device_poll_thread, nullptr)) {
-    fatal_errno("cannot create device poll thread");
-  }
-  if (!adb_thread_create(_power_notification_thread, nullptr)) {
-    fatal_errno("cannot create power notification thread");
-  }
+  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;
 
@@ -419,6 +420,7 @@
   unsigned long time_out = 0;
   unsigned long read = 0;
   int err = 0;
+  int orig_len = len;
 
   D("usb_read %d", len);
   if (NULL == handle) {
@@ -427,9 +429,8 @@
     goto fail;
   }
 
-  while (len > 0) {
-    if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read,
-                             time_out)) {
+  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;
@@ -437,11 +438,11 @@
     }
     D("usb_read got: %ld, expected: %d", read, len);
 
-    data = (char *)data + read;
+    data = (char*)data + read;
     len -= read;
   }
 
-  return 0;
+  return orig_len - len;
 
 fail:
   // Any failure should cause us to kick the device instead of leaving it a
@@ -526,6 +527,10 @@
   return 0;
 }
 
+size_t usb_get_max_packet_size(usb_handle* handle) {
+    return handle->max_packet_size;
+}
+
 int recognized_device(usb_handle* handle) {
   if (NULL == handle)
     return 0;
@@ -561,6 +566,7 @@
       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 {
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 4979eef..c9f1ee9 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -68,20 +68,17 @@
 static int install_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
 static int uninstall_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
 
-static auto& gProductOutPath = *new std::string();
 extern int gListenAll;
 
 DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
 
-static std::string product_file(const char *extra) {
-    if (gProductOutPath.empty()) {
-        fprintf(stderr, "adb: Product directory not specified; "
-                "use -p or define ANDROID_PRODUCT_OUT\n");
+static std::string product_file(const char* file) {
+    const char* ANDROID_PRODUCT_OUT = getenv("ANDROID_PRODUCT_OUT");
+    if (ANDROID_PRODUCT_OUT == nullptr) {
+        fprintf(stderr, "adb: product directory not specified; set $ANDROID_PRODUCT_OUT\n");
         exit(1);
     }
-
-    return android::base::StringPrintf("%s%s%s",
-                                       gProductOutPath.c_str(), OS_PATH_SEPARATOR_STR, extra);
+    return android::base::StringPrintf("%s%s%s", ANDROID_PRODUCT_OUT, OS_PATH_SEPARATOR_STR, file);
 }
 
 static void help() {
@@ -92,11 +89,7 @@
         " -a         listen on all network interfaces, not just localhost\n"
         " -d         use USB device (error if multiple devices connected)\n"
         " -e         use TCP/IP device (error if multiple TCP/IP devices available)\n"
-        " -s SERIAL\n"
-        "     use device with given serial number (overrides $ANDROID_SERIAL)\n"
-        " -p PRODUCT\n"
-        "     name or path ('angler'/'out/target/product/angler');\n"
-        "     default $ANDROID_PRODUCT_OUT\n"
+        " -s SERIAL  use device with given serial (overrides $ANDROID_SERIAL)\n"
         " -H         name of adb server host [default=localhost]\n"
         " -P         port of adb server [default=5037]\n"
         " -L SOCKET  listen on given socket for adb server [default=tcp:localhost:5037]\n"
@@ -133,14 +126,14 @@
         " reverse --remove-all     remove all reverse socket connections from device\n"
         "\n"
         "file transfer:\n"
-        " push LOCAL... REMOTE\n"
+        " push [--sync] LOCAL... REMOTE\n"
         "     copy local files/directories to device\n"
+        "     --sync: only push files that are newer on the host than the device\n"
         " pull [-a] REMOTE... LOCAL\n"
         "     copy files/dirs from device\n"
         "     -a: preserve file timestamp and mode\n"
-        " sync [DIR]\n"
-        "     copy all changed files to device; if DIR is \"system\", \"vendor\", \"oem\",\n"
-        "     or \"data\", only sync that partition (default all)\n"
+        " sync [system|vendor|oem|data|all]\n"
+        "     sync a local build from $ANDROID_PRODUCT_OUT to the device (default all)\n"
         "     -l: list but don't copy\n"
         "\n"
         "shell:\n"
@@ -169,15 +162,7 @@
         "     '-k': keep the data and cache directories\n"
         "\n"
         "backup/restore:\n"
-        " backup [-f FILE] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all] [-system|-nosystem] [PACKAGE...]\n"
-        "     write an archive of the device's data to FILE [default=backup.adb]\n"
-        "     package list optional if -all/-shared are supplied\n"
-        "     -apk/-noapk: do/don't back up .apk files (default -noapk)\n"
-        "     -obb/-noobb: do/don't back up .obb files (default -noobb)\n"
-        "     -shared|-noshared: do/don't back up shared storage (default -noshared)\n"
-        "     -all: back up all installed applications\n"
-        "     -system|-nosystem: include system apps in -all (default -system)\n"
-        " restore FILE             restore device contents from FILE\n"
+        "   to show usage run \"adb shell bu help\"\n"
         "\n"
         "debugging:\n"
         " bugreport [PATH]\n"
@@ -220,6 +205,7 @@
         " kill-server              kill the server if it is running\n"
         " reconnect                kick connection from host side to force reconnect\n"
         " reconnect device         kick connection from device side to force reconnect\n"
+        " reconnect offline        reset offline/unauthorized devices to force reconnect\n"
         "\n"
         "environment variables:\n"
         " $ADB_TRACE\n"
@@ -613,6 +599,13 @@
     std::string service_string = ShellServiceString(use_shell_protocol,
                                                     type_arg, command);
 
+    // Old devices can't handle a service string that's longer than MAX_PAYLOAD_V1.
+    // Use |use_shell_protocol| to determine whether to allow a command longer than that.
+    if (service_string.size() > MAX_PAYLOAD_V1 && !use_shell_protocol) {
+        fprintf(stderr, "error: shell command too long\n");
+        return 1;
+    }
+
     // Make local stdin raw if the device allocates a PTY, which happens if:
     //   1. We are explicitly asking for a PTY shell, or
     //   2. We don't specify shell type and are starting an interactive session.
@@ -663,13 +656,8 @@
 #endif
 
     // TODO: combine read_and_dump with stdin_read_thread to make life simpler?
-    int exit_code = 1;
-    if (!adb_thread_create(stdin_read_thread_loop, args)) {
-        PLOG(ERROR) << "error starting stdin read thread";
-        delete args;
-    } else {
-        exit_code = read_and_dump(fd, use_shell_protocol);
-    }
+    std::thread(stdin_read_thread_loop, args).detach();
+    int exit_code = read_and_dump(fd, use_shell_protocol);
 
     // TODO: properly exit stdin_read_thread_loop and close |fd|.
 
@@ -703,8 +691,7 @@
         switch (opt) {
             case 'e':
                 if (!(strlen(optarg) == 1 || strcmp(optarg, "none") == 0)) {
-                    fprintf(stderr, "error: -e requires a single-character argument or 'none'\n");
-                    return 1;
+                    return syntax_error("-e requires a single-character argument or 'none'");
                 }
                 escape_char = (strcmp(optarg, "none") == 0) ? 0 : optarg[0];
                 break;
@@ -782,55 +769,46 @@
     return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
 }
 
-static int adb_download_buffer(const char* service, const char* filename) {
-    std::string content;
-    if (!android::base::ReadFileToString(filename, &content)) {
-        fprintf(stderr, "error: couldn't read %s: %s\n", filename, strerror(errno));
-        return -1;
-    }
-
-    const uint8_t* data = reinterpret_cast<const uint8_t*>(content.data());
-    unsigned sz = content.size();
-
+static int adb_sideload_legacy(const char* filename, int in_fd, int size) {
     std::string error;
-    int fd = adb_connect(android::base::StringPrintf("%s:%d", service, sz), &error);
-    if (fd < 0) {
-        fprintf(stderr,"error: %s\n", error.c_str());
+    int out_fd = adb_connect(android::base::StringPrintf("sideload:%d", size), &error);
+    if (out_fd < 0) {
+        fprintf(stderr, "adb: pre-KitKat sideload connection failed: %s\n", error.c_str());
         return -1;
     }
 
     int opt = CHUNK_SIZE;
-    opt = adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &opt, sizeof(opt));
+    opt = adb_setsockopt(out_fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
 
-    unsigned total = sz;
-    const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
-
-    const char* x = strrchr(service, ':');
-    if (x) service = x + 1;
-
-    while (sz > 0) {
-        unsigned xfer = (sz > CHUNK_SIZE) ? CHUNK_SIZE : sz;
-        if (!WriteFdExactly(fd, ptr, xfer)) {
-            std::string error;
-            adb_status(fd, &error);
-            fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
-            adb_close(fd);
+    char buf[CHUNK_SIZE];
+    int total = size;
+    while (size > 0) {
+        unsigned xfer = (size > CHUNK_SIZE) ? CHUNK_SIZE : size;
+        if (!ReadFdExactly(in_fd, buf, xfer)) {
+            fprintf(stderr, "adb: failed to read data from %s: %s\n", filename, strerror(errno));
+            adb_close(out_fd);
             return -1;
         }
-        sz -= xfer;
-        ptr += xfer;
-        printf("sending: '%s' %4d%%    \r", filename, (int)(100LL - ((100LL * sz) / (total))));
+        if (!WriteFdExactly(out_fd, buf, xfer)) {
+            std::string error;
+            adb_status(out_fd, &error);
+            fprintf(stderr, "adb: failed to write data: %s\n", error.c_str());
+            adb_close(out_fd);
+            return -1;
+        }
+        size -= xfer;
+        printf("sending: '%s' %4d%%    \r", filename, (int)(100LL - ((100LL * size) / (total))));
         fflush(stdout);
     }
     printf("\n");
 
-    if (!adb_status(fd, &error)) {
-        fprintf(stderr,"* error response '%s' *\n", error.c_str());
-        adb_close(fd);
+    if (!adb_status(out_fd, &error)) {
+        fprintf(stderr, "adb: error response: %s\n", error.c_str());
+        adb_close(out_fd);
         return -1;
     }
 
-    adb_close(fd);
+    adb_close(out_fd);
     return 0;
 }
 
@@ -856,19 +834,18 @@
  *   we hang up.
  */
 static int adb_sideload_host(const char* filename) {
-    fprintf(stderr, "opening '%s'...\n", filename);
+    // TODO: use a LinePrinter instead...
     struct stat sb;
     if (stat(filename, &sb) == -1) {
-        fprintf(stderr, "failed to stat file %s: %s\n", filename, strerror(errno));
+        fprintf(stderr, "adb: failed to stat file %s: %s\n", filename, strerror(errno));
         return -1;
     }
     unique_fd package_fd(adb_open(filename, O_RDONLY));
     if (package_fd == -1) {
-        fprintf(stderr, "failed to open file %s: %s\n", filename, strerror(errno));
+        fprintf(stderr, "adb: failed to open file %s: %s\n", filename, strerror(errno));
         return -1;
     }
 
-    fprintf(stderr, "connecting...\n");
     std::string service = android::base::StringPrintf(
         "sideload-host:%d:%d", static_cast<int>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
     std::string error;
@@ -876,8 +853,9 @@
     if (device_fd < 0) {
         // Try falling back to the older (<= K) sideload method. Maybe this
         // is an older device that doesn't support sideload-host.
-        fprintf(stderr, "falling back to older sideload method...\n");
-        return adb_download_buffer("sideload", filename);
+        fprintf(stderr, "adb: sideload connection failed: %s\n", error.c_str());
+        fprintf(stderr, "adb: trying pre-KitKat sideload method...\n");
+        return adb_sideload_legacy(filename, package_fd, static_cast<int>(sb.st_size));
     }
 
     int opt = SIDELOAD_HOST_BLOCK_SIZE;
@@ -889,7 +867,7 @@
     int last_percent = -1;
     while (true) {
         if (!ReadFdExactly(device_fd, buf, 8)) {
-            fprintf(stderr, "* failed to read command: %s\n", strerror(errno));
+            fprintf(stderr, "adb: failed to read command: %s\n", strerror(errno));
             return -1;
         }
         buf[8] = '\0';
@@ -905,7 +883,7 @@
 
         size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
         if (offset >= static_cast<size_t>(sb.st_size)) {
-            fprintf(stderr, "* attempt to read block %d past end\n", block);
+            fprintf(stderr, "adb: failed to read block %d past end\n", block);
             return -1;
         }
 
@@ -915,17 +893,17 @@
         }
 
         if (adb_lseek(package_fd, offset, SEEK_SET) != static_cast<int>(offset)) {
-            fprintf(stderr, "* failed to seek to package block: %s\n", strerror(errno));
+            fprintf(stderr, "adb: failed to seek to package block: %s\n", strerror(errno));
             return -1;
         }
         if (!ReadFdExactly(package_fd, buf, to_write)) {
-            fprintf(stderr, "* failed to read package block: %s\n", strerror(errno));
+            fprintf(stderr, "adb: failed to read package block: %s\n", strerror(errno));
             return -1;
         }
 
         if (!WriteFdExactly(device_fd, buf, to_write)) {
             adb_status(device_fd, &error);
-            fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
+            fprintf(stderr, "adb: failed to write data '%s' *\n", error.c_str());
             return -1;
         }
         xfer += to_write;
@@ -957,19 +935,13 @@
     fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]);
     return -1;
 #else
-    if (argc < 2) {
-        fprintf(stderr, "usage: adb %s <adb service name> [ppp opts]\n",
-                argv[0]);
-
-        return 1;
-    }
+    if (argc < 2) return syntax_error("adb %s <adb service name> [ppp opts]", argv[0]);
 
     const char* adb_service_name = argv[1];
     std::string error;
     int fd = adb_connect(adb_service_name, &error);
     if (fd < 0) {
-        fprintf(stderr,"Error: Could not open adb service: %s. Error: %s\n",
-                adb_service_name, error.c_str());
+        fprintf(stderr, "adb: could not open adb service %s: %s\n", adb_service_name, error.c_str());
         return 1;
     }
 
@@ -1188,10 +1160,7 @@
     /* find, extract, and use any -f argument */
     for (int i = 1; i < argc; i++) {
         if (!strcmp("-f", argv[i])) {
-            if (i == argc-1) {
-                fprintf(stderr, "adb: backup -f passed with no filename.\n");
-                return EXIT_FAILURE;
-            }
+            if (i == argc - 1) return syntax_error("backup -f passed with no filename");
             filename = argv[i+1];
             for (int j = i+2; j <= argc; ) {
                 argv[i++] = argv[j++];
@@ -1203,10 +1172,7 @@
 
     // Bare "adb backup" or "adb backup -f filename" are not valid invocations ---
     // a list of packages is required.
-    if (argc < 2) {
-        fprintf(stderr, "adb: backup either needs a list of packages or -all/-shared.\n");
-        return EXIT_FAILURE;
-    }
+    if (argc < 2) return syntax_error("backup either needs a list of packages or -all/-shared");
 
     adb_unlink(filename);
     int outFd = adb_creat(filename, 0640);
@@ -1231,7 +1197,7 @@
         return EXIT_FAILURE;
     }
 
-    printf("Now unlock your device and confirm the backup operation...\n");
+    fprintf(stdout, "Now unlock your device and confirm the backup operation...\n");
     fflush(stdout);
 
     copy_to_file(fd, outFd);
@@ -1242,7 +1208,7 @@
 }
 
 static int restore(int argc, const char** argv) {
-    if (argc != 2) return usage("restore requires an argument");
+    if (argc != 2) return syntax_error("restore requires an argument");
 
     const char* filename = argv[1];
     int tarFd = adb_open(filename, O_RDONLY);
@@ -1259,7 +1225,9 @@
         return -1;
     }
 
-    printf("Now unlock your device and confirm the restore operation.\n");
+    fprintf(stdout, "Now unlock your device and confirm the restore operation.\n");
+    fflush(stdout);
+
     copy_to_file(tarFd, fd);
 
     // Provide an in-band EOD marker in case the archive file is malformed
@@ -1273,69 +1241,8 @@
     return 0;
 }
 
-/* <hint> may be:
- * - A simple product name
- *   e.g., "sooner"
- * - A relative path from the CWD to the ANDROID_PRODUCT_OUT dir
- *   e.g., "out/target/product/sooner"
- * - An absolute path to the PRODUCT_OUT dir
- *   e.g., "/src/device/out/target/product/sooner"
- *
- * Given <hint>, try to construct an absolute path to the
- * ANDROID_PRODUCT_OUT dir.
- */
-static std::string find_product_out_path(const std::string& hint) {
-    if (hint.empty()) {
-        return "";
-    }
-
-    // If it's already absolute, don't bother doing any work.
-    if (adb_is_absolute_host_path(hint.c_str())) {
-        return hint;
-    }
-
-    // If any of the OS_PATH_SEPARATORS is found, assume it's a relative path;
-    // make it absolute.
-    // NOLINT: Do not complain if OS_PATH_SEPARATORS has only one character.
-    if (hint.find_first_of(OS_PATH_SEPARATORS) != std::string::npos) {  // NOLINT
-        std::string cwd;
-        if (!getcwd(&cwd)) {
-            perror("adb: getcwd failed");
-            return "";
-        }
-        return android::base::StringPrintf("%s%c%s", cwd.c_str(), OS_PATH_SEPARATOR, hint.c_str());
-    }
-
-    // It's a string without any slashes.  Try to do something with it.
-    //
-    // Try to find the root of the build tree, and build a PRODUCT_OUT
-    // path from there.
-    char* top = getenv("ANDROID_BUILD_TOP");
-    if (top == nullptr) {
-        fprintf(stderr, "adb: ANDROID_BUILD_TOP not set!\n");
-        return "";
-    }
-
-    std::string path = top;
-    path += OS_PATH_SEPARATOR_STR;
-    path += "out";
-    path += OS_PATH_SEPARATOR_STR;
-    path += "target";
-    path += OS_PATH_SEPARATOR_STR;
-    path += "product";
-    path += OS_PATH_SEPARATOR_STR;
-    path += hint;
-    if (!directory_exists(path)) {
-        fprintf(stderr, "adb: Couldn't find a product dir based on -p %s; "
-                        "\"%s\" doesn't exist\n", hint.c_str(), path.c_str());
-        return "";
-    }
-    return path;
-}
-
-static void parse_push_pull_args(const char** arg, int narg,
-                                 std::vector<const char*>* srcs,
-                                 const char** dst, bool* copy_attrs) {
+static void parse_push_pull_args(const char** arg, int narg, std::vector<const char*>* srcs,
+                                 const char** dst, bool* copy_attrs, bool* sync) {
     *copy_attrs = false;
 
     srcs->clear();
@@ -1348,10 +1255,14 @@
                 // Silently ignore for backwards compatibility.
             } else if (!strcmp(*arg, "-a")) {
                 *copy_attrs = true;
+            } else if (!strcmp(*arg, "--sync")) {
+                if (sync != nullptr) {
+                    *sync = true;
+                }
             } else if (!strcmp(*arg, "--")) {
                 ignore_flags = true;
             } else {
-                fprintf(stderr, "adb: unrecognized option '%s'\n", *arg);
+                syntax_error("unrecognized option '%s'", *arg);
                 exit(1);
             }
         }
@@ -1423,17 +1334,6 @@
     signal(SIGPIPE, SIG_IGN);
 #endif
 
-    // If defined, this should be an absolute path to
-    // the directory containing all of the various system images
-    // for a particular product.  If not defined, and the adb
-    // command requires this information, then the user must
-    // specify the path using "-p".
-    char* ANDROID_PRODUCT_OUT = getenv("ANDROID_PRODUCT_OUT");
-    if (ANDROID_PRODUCT_OUT != nullptr) {
-        gProductOutPath = ANDROID_PRODUCT_OUT;
-    }
-    // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
-
     const char* server_host_str = nullptr;
     const char* server_port_str = nullptr;
     const char* server_socket_str = nullptr;
@@ -1450,7 +1350,7 @@
             /* this is a special flag used only when the ADB client launches the ADB Server */
             is_daemon = 1;
         } else if (!strcmp(argv[0], "--reply-fd")) {
-            if (argc < 2) return usage("--reply-fd requires an argument");
+            if (argc < 2) return syntax_error("--reply-fd requires an argument");
             const char* reply_fd_str = argv[1];
             argc--;
             argv++;
@@ -1459,26 +1359,11 @@
                 fprintf(stderr, "adb: invalid reply fd \"%s\"\n", reply_fd_str);
                 return 1;
             }
-        } else if (!strncmp(argv[0], "-p", 2)) {
-            const char* product = nullptr;
-            if (argv[0][2] == '\0') {
-                if (argc < 2) return usage("-p requires an argument");
-                product = argv[1];
-                argc--;
-                argv++;
-            } else {
-                product = argv[0] + 2;
-            }
-            gProductOutPath = find_product_out_path(product);
-            if (gProductOutPath.empty()) {
-                fprintf(stderr, "adb: could not resolve \"-p %s\"\n", product);
-                return 1;
-            }
         } else if (argv[0][0]=='-' && argv[0][1]=='s') {
             if (isdigit(argv[0][2])) {
                 serial = argv[0] + 2;
             } else {
-                if (argc < 2 || argv[0][2] != '\0') return usage("-s requires an argument");
+                if (argc < 2 || argv[0][2] != '\0') return syntax_error("-s requires an argument");
                 serial = argv[1];
                 argc--;
                 argv++;
@@ -1491,7 +1376,7 @@
             gListenAll = 1;
         } else if (!strncmp(argv[0], "-H", 2)) {
             if (argv[0][2] == '\0') {
-                if (argc < 2) return usage("-H requires an argument");
+                if (argc < 2) return syntax_error("-H requires an argument");
                 server_host_str = argv[1];
                 argc--;
                 argv++;
@@ -1500,7 +1385,7 @@
             }
         } else if (!strncmp(argv[0], "-P", 2)) {
             if (argv[0][2] == '\0') {
-                if (argc < 2) return usage("-P requires an argument");
+                if (argc < 2) return syntax_error("-P requires an argument");
                 server_port_str = argv[1];
                 argc--;
                 argv++;
@@ -1508,7 +1393,7 @@
                 server_port_str = argv[0] + 2;
             }
         } else if (!strcmp(argv[0], "-L")) {
-            if (argc < 2) return usage("-L requires an argument");
+            if (argc < 2) return syntax_error("-L requires an argument");
             server_socket_str = argv[1];
             argc--;
             argv++;
@@ -1521,8 +1406,7 @@
     }
 
     if ((server_host_str || server_port_str) && server_socket_str) {
-        fprintf(stderr, "adb: -L is incompatible with -H or -P\n");
-        exit(1);
+        return syntax_error("-L is incompatible with -H or -P");
     }
 
     // If -L, -H, or -P are specified, ignore environment variables.
@@ -1617,8 +1501,7 @@
         } else if (argc == 2 && !strcmp(argv[1], "-l")) {
             listopt = argv[1];
         } else {
-            fprintf(stderr, "Usage: adb devices [-l]\n");
-            return 1;
+            return syntax_error("adb devices [-l]");
         }
 
         std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
@@ -1626,19 +1509,13 @@
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "connect")) {
-        if (argc != 2) {
-            fprintf(stderr, "Usage: adb connect <host>[:<port>]\n");
-            return 1;
-        }
+        if (argc != 2) return syntax_error("adb connect <host>[:<port>]");
 
         std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "disconnect")) {
-        if (argc > 2) {
-            fprintf(stderr, "Usage: adb disconnect [<host>[:<port>]]\n");
-            return 1;
-        }
+        if (argc > 2) return syntax_error("adb disconnect [<host>[:<port>]]");
 
         std::string query = android::base::StringPrintf("host:disconnect:%s",
                                                         (argc == 2) ? argv[1] : "");
@@ -1653,10 +1530,7 @@
     else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
         int exec_in = !strcmp(argv[0], "exec-in");
 
-        if (argc < 2) {
-            fprintf(stderr, "Usage: adb %s command\n", argv[0]);
-            return 1;
-        }
+        if (argc < 2) return syntax_error("adb %s command", argv[0]);
 
         std::string cmd = "exec:";
         cmd += argv[1];
@@ -1683,28 +1557,10 @@
         return 0;
     }
     else if (!strcmp(argv[0], "kill-server")) {
-        std::string error;
-        int fd = _adb_connect("host:kill", &error);
-        if (fd == -2) {
-            // Failed to make network connection to server. Don't output the
-            // network error since that is expected.
-            fprintf(stderr,"* server not running *\n");
-            // Successful exit code because the server is already "killed".
-            return 0;
-        } else if (fd == -1) {
-            // Some other error.
-            fprintf(stderr, "error: %s\n", error.c_str());
-            return 1;
-        } else {
-            // Successfully connected, kill command sent, okay status came back.
-            // Server should exit() in a moment, if not already.
-            ReadOrderlyShutdown(fd);
-            adb_close(fd);
-            return 0;
-        }
+        return adb_kill_server() ? 0 : 1;
     }
     else if (!strcmp(argv[0], "sideload")) {
-        if (argc != 2) return usage("sideload requires an argument");
+        if (argc != 2) return syntax_error("sideload requires an argument");
         if (adb_sideload_host(argv[1])) {
             return 1;
         } else {
@@ -1738,7 +1594,7 @@
         bool reverse = !strcmp(argv[0], "reverse");
         ++argv;
         --argc;
-        if (argc < 1) return usage("%s requires an argument", argv[0]);
+        if (argc < 1) return syntax_error("%s requires an argument", argv[0]);
 
         // Determine the <host-prefix> for this command.
         std::string host_prefix;
@@ -1758,24 +1614,24 @@
 
         std::string cmd, error;
         if (strcmp(argv[0], "--list") == 0) {
-            if (argc != 1) return usage("--list doesn't take any arguments");
+            if (argc != 1) return syntax_error("--list doesn't take any arguments");
             return adb_query_command(host_prefix + ":list-forward");
         } else if (strcmp(argv[0], "--remove-all") == 0) {
-            if (argc != 1) return usage("--remove-all doesn't take any arguments");
+            if (argc != 1) return syntax_error("--remove-all doesn't take any arguments");
             cmd = host_prefix + ":killforward-all";
         } else if (strcmp(argv[0], "--remove") == 0) {
             // forward --remove <local>
-            if (argc != 2) return usage("--remove requires an argument");
+            if (argc != 2) return syntax_error("--remove requires an argument");
             cmd = host_prefix + ":killforward:" + argv[1];
         } else if (strcmp(argv[0], "--no-rebind") == 0) {
             // forward --no-rebind <local> <remote>
-            if (argc != 3) return usage("--no-rebind takes two arguments");
+            if (argc != 3) return syntax_error("--no-rebind takes two arguments");
             if (forward_targets_are_valid(argv[1], argv[2], &error)) {
                 cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
             }
         } else {
             // forward <local> <remote>
-            if (argc != 2) return usage("forward takes two arguments");
+            if (argc != 2) return syntax_error("forward takes two arguments");
             if (forward_targets_are_valid(argv[0], argv[1], &error)) {
                 cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
             }
@@ -1804,40 +1660,41 @@
     }
     /* do_sync_*() commands */
     else if (!strcmp(argv[0], "ls")) {
-        if (argc != 2) return usage("ls requires an argument");
+        if (argc != 2) return syntax_error("ls requires an argument");
         return do_sync_ls(argv[1]) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "push")) {
         bool copy_attrs = false;
+        bool sync = false;
         std::vector<const char*> srcs;
         const char* dst = nullptr;
 
-        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
-        if (srcs.empty() || !dst) return usage("push requires an argument");
-        return do_sync_push(srcs, dst) ? 0 : 1;
+        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, &sync);
+        if (srcs.empty() || !dst) return syntax_error("push requires an argument");
+        return do_sync_push(srcs, dst, sync) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "pull")) {
         bool copy_attrs = false;
         std::vector<const char*> srcs;
         const char* dst = ".";
 
-        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
-        if (srcs.empty()) return usage("pull requires an argument");
+        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, nullptr);
+        if (srcs.empty()) return syntax_error("pull requires an argument");
         return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "install")) {
-        if (argc < 2) return usage("install requires an argument");
+        if (argc < 2) return syntax_error("install requires an argument");
         if (_use_legacy_install()) {
             return install_app_legacy(transport_type, serial, argc, argv);
         }
         return install_app(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "install-multiple")) {
-        if (argc < 2) return usage("install-multiple requires an argument");
+        if (argc < 2) return syntax_error("install-multiple requires an argument");
         return install_multiple_app(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "uninstall")) {
-        if (argc < 2) return usage("uninstall requires an argument");
+        if (argc < 2) return syntax_error("uninstall requires an argument");
         if (_use_legacy_install()) {
             return uninstall_app_legacy(transport_type, serial, argc, argv);
         }
@@ -1860,12 +1717,14 @@
             // A local path or "android"/"data" arg was specified.
             src = argv[1];
         } else {
-            return usage("usage: adb sync [-l] [PARTITION]");
+            return syntax_error("adb sync [-l] [PARTITION]");
         }
 
+        if (src == "all") src = "";
+
         if (src != "" &&
             src != "system" && src != "data" && src != "vendor" && src != "oem") {
-            return usage("don't know how to sync %s partition", src.c_str());
+            return syntax_error("don't know how to sync %s partition", src.c_str());
         }
 
         std::string system_src_path = product_file("system");
@@ -1917,7 +1776,7 @@
         return restore(argc, argv);
     }
     else if (!strcmp(argv[0], "keygen")) {
-        if (argc != 2) return usage("keygen requires an argument");
+        if (argc != 2) return syntax_error("keygen requires an argument");
         // Always print key generation information for keygen command.
         adb_trace_enable(AUTH);
         return adb_auth_keygen(argv[1]);
@@ -1960,7 +1819,7 @@
         return adb_query_command("host:host-features");
     } else if (!strcmp(argv[0], "reconnect")) {
         if (argc == 1) {
-            return adb_query_command("host:reconnect");
+            return adb_query_command(format_host_command(argv[0], transport_type, serial));
         } else if (argc == 2) {
             if (!strcmp(argv[1], "device")) {
                 std::string err;
@@ -1970,12 +1829,12 @@
                 std::string err;
                 return adb_query_command("host:reconnect-offline");
             } else {
-                return usage("usage: adb reconnect [device|offline]");
+                return syntax_error("adb reconnect [device|offline]");
             }
         }
     }
 
-    usage("unknown command %s", argv[0]);
+    syntax_error("unknown command %s", argv[0]);
     return 1;
 }
 
@@ -2002,19 +1861,18 @@
     // The last argument must be the APK file
     const char* file = argv[argc - 1];
     if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
-        fprintf(stderr, "Filename doesn't end .apk: %s\n", file);
-        return EXIT_FAILURE;
+        return syntax_error("filename doesn't end .apk: %s", file);
     }
 
     struct stat sb;
     if (stat(file, &sb) == -1) {
-        fprintf(stderr, "Failed to stat %s: %s\n", file, strerror(errno));
+        fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
         return 1;
     }
 
     int localFd = adb_open(file, O_RDONLY);
     if (localFd < 0) {
-        fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+        fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
         return 1;
     }
 
@@ -2032,7 +1890,7 @@
 
     int remoteFd = adb_connect(cmd, &error);
     if (remoteFd < 0) {
-        fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+        fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
         adb_close(localFd);
         return 1;
     }
@@ -2044,12 +1902,12 @@
     adb_close(localFd);
     adb_close(remoteFd);
 
-    if (strncmp("Success", buf, 7)) {
-        fprintf(stderr, "Failed to install %s: %s", file, buf);
-        return 1;
+    if (!strncmp("Success", buf, 7)) {
+        fputs(buf, stdout);
+        return 0;
     }
-    fputs(buf, stderr);
-    return 0;
+    fprintf(stderr, "adb: failed to install %s: %s", file, buf);
+    return 1;
 }
 
 static int install_multiple_app(TransportType transport, const char* serial, int argc,
@@ -2071,10 +1929,7 @@
         }
     }
 
-    if (first_apk == -1) {
-        fprintf(stderr, "No APK file on command line\n");
-        return 1;
-    }
+    if (first_apk == -1) return syntax_error("need APK file on command line");
 
     std::string install_cmd;
     if (_use_legacy_install()) {
@@ -2092,7 +1947,7 @@
     std::string error;
     int fd = adb_connect(cmd, &error);
     if (fd < 0) {
-        fprintf(stderr, "Connect error for create: %s\n", error.c_str());
+        fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
         return EXIT_FAILURE;
     }
     char buf[BUFSIZ];
@@ -2109,7 +1964,7 @@
         }
     }
     if (session_id < 0) {
-        fprintf(stderr, "Failed to create session\n");
+        fprintf(stderr, "adb: failed to create session\n");
         fputs(buf, stderr);
         return EXIT_FAILURE;
     }
@@ -2120,7 +1975,7 @@
         const char* file = argv[i];
         struct stat sb;
         if (stat(file, &sb) == -1) {
-            fprintf(stderr, "Failed to stat %s: %s\n", file, strerror(errno));
+            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
             success = 0;
             goto finalize_session;
         }
@@ -2132,7 +1987,7 @@
 
         int localFd = adb_open(file, O_RDONLY);
         if (localFd < 0) {
-            fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
             success = 0;
             goto finalize_session;
         }
@@ -2140,7 +1995,7 @@
         std::string error;
         int remoteFd = adb_connect(cmd, &error);
         if (remoteFd < 0) {
-            fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
             adb_close(localFd);
             success = 0;
             goto finalize_session;
@@ -2153,7 +2008,7 @@
         adb_close(remoteFd);
 
         if (strncmp("Success", buf, 7)) {
-            fprintf(stderr, "Failed to write %s\n", file);
+            fprintf(stderr, "adb: failed to write %s\n", file);
             fputs(buf, stderr);
             success = 0;
             goto finalize_session;
@@ -2167,20 +2022,19 @@
                                         install_cmd.c_str(), success ? "commit" : "abandon", session_id);
     fd = adb_connect(service, &error);
     if (fd < 0) {
-        fprintf(stderr, "Connect error for finalize: %s\n", error.c_str());
+        fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
         return EXIT_FAILURE;
     }
     read_status_line(fd, buf, sizeof(buf));
     adb_close(fd);
 
     if (!strncmp("Success", buf, 7)) {
-        fputs(buf, stderr);
+        fputs(buf, stdout);
         return 0;
-    } else {
-        fprintf(stderr, "Failed to finalize session\n");
-        fputs(buf, stderr);
-        return EXIT_FAILURE;
     }
+    fprintf(stderr, "adb: failed to finalize session\n");
+    fputs(buf, stderr);
+    return EXIT_FAILURE;
 }
 
 static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
@@ -2238,16 +2092,13 @@
         }
     }
 
-    if (last_apk == -1) {
-        fprintf(stderr, "No APK file on command line\n");
-        return EXIT_FAILURE;
-    }
+    if (last_apk == -1) return syntax_error("need APK file on command line");
 
     int result = -1;
     std::vector<const char*> apk_file = {argv[last_apk]};
     std::string apk_dest = android::base::StringPrintf(
         where, android::base::Basename(argv[last_apk]).c_str());
-    if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
+    if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
     argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
     result = pm_command(transport, serial, argc, argv);
 
diff --git a/adb/commandline.h b/adb/commandline.h
index 0cf655c..9ba69a3 100644
--- a/adb/commandline.h
+++ b/adb/commandline.h
@@ -87,7 +87,6 @@
 extern DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK;
 
 int adb_commandline(int argc, const char** argv);
-int usage();
 
 // Connects to the device "shell" service with |command| and prints the
 // resulting output.
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 7a87df4..1c94298 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -107,10 +107,10 @@
     // AID_SDCARD_RW to allow writing to the SD card
     // AID_NET_BW_STATS to read out qtaguid statistics
     // AID_READPROC for reading /proc entries across UID boundaries
-    gid_t groups[] = {AID_ADB,      AID_LOG,       AID_INPUT,
-                      AID_INET,     AID_NET_BT,    AID_NET_BT_ADMIN,
-                      AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS,
-                      AID_READPROC};
+    // AID_UHID for using 'hid' command to read/write to /dev/uhid
+    gid_t groups[] = {AID_ADB,          AID_LOG,          AID_INPUT,    AID_INET,
+                      AID_NET_BT,       AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
+                      AID_NET_BW_STATS, AID_READPROC,     AID_UHID};
     minijail_set_supplementary_gids(jail.get(), arraysize(groups), groups);
 
     // Don't listen on a port (default 5037) if running in secure mode.
@@ -233,9 +233,8 @@
             adb_device_banner = optarg;
             break;
         case 'v':
-            printf("Android Debug Bridge Daemon version %d.%d.%d %s\n",
-                   ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
-                   ADB_REVISION);
+            printf("Android Debug Bridge Daemon version %d.%d.%d (%s)\n", ADB_VERSION_MAJOR,
+                   ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_VERSION);
             return 0;
         default:
             // getopt already prints "adbd: invalid option -- %c" for us.
diff --git a/adb/daemon/mdns.cpp b/adb/daemon/mdns.cpp
index 7811143..849378f 100644
--- a/adb/daemon/mdns.cpp
+++ b/adb/daemon/mdns.cpp
@@ -17,12 +17,14 @@
 #include "adb_mdns.h"
 #include "sysdeps.h"
 
-#include <chrono>
 #include <dns_sd.h>
 #include <endian.h>
-#include <mutex>
 #include <unistd.h>
 
+#include <chrono>
+#include <mutex>
+#include <thread>
+
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 
@@ -58,7 +60,7 @@
     }
 }
 
-static void setup_mdns_thread(void* /* unused */) {
+static void setup_mdns_thread() {
     start_mdns();
     std::lock_guard<std::mutex> lock(mdns_lock);
 
@@ -88,7 +90,7 @@
 
 void setup_mdns(int port_in) {
     port = port_in;
-    adb_thread_create(setup_mdns_thread, nullptr, nullptr);
+    std::thread(setup_mdns_thread).detach();
 
     // TODO: Make this more robust against a hard kill.
     atexit(teardown_mdns);
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 92e9039..0f92282 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -64,7 +64,7 @@
 
 #define FUNCTIONFS_ENDPOINT_ALLOC       _IOR('g', 231, __u32)
 
-static constexpr size_t ENDPOINT_ALLOC_RETRIES = 2;
+static constexpr size_t ENDPOINT_ALLOC_RETRIES = 10;
 
 static int dummy_fd = -1;
 
@@ -235,6 +235,8 @@
 };
 
 bool init_functionfs(struct usb_handle* h) {
+    LOG(INFO) << "initializing functionfs";
+
     ssize_t ret;
     struct desc_v1 v1_descriptor;
     struct desc_v2 v2_descriptor;
@@ -255,10 +257,10 @@
     v2_descriptor.os_desc = os_desc_compat;
 
     if (h->control < 0) { // might have already done this before
-        D("OPENING %s", USB_FFS_ADB_EP0);
+        LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
         h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR);
         if (h->control < 0) {
-            D("[ %s: cannot open control endpoint: errno=%d]", USB_FFS_ADB_EP0, errno);
+            PLOG(ERROR) << "cannot open control endpoint " << USB_FFS_ADB_EP0;
             goto err;
         }
 
@@ -289,13 +291,13 @@
 
     h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR);
     if (h->bulk_out < 0) {
-        D("[ %s: cannot open bulk-out ep: errno=%d ]", USB_FFS_ADB_OUT, errno);
+        PLOG(ERROR) << "cannot open bulk-out endpoint " << USB_FFS_ADB_OUT;
         goto err;
     }
 
     h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR);
     if (h->bulk_in < 0) {
-        D("[ %s: cannot open bulk-in ep: errno=%d ]", USB_FFS_ADB_IN, errno);
+        PLOG(ERROR) << "cannot open bulk-in endpoint " << USB_FFS_ADB_IN;
         goto err;
     }
 
@@ -356,12 +358,13 @@
 
         while (true) {
             if (init_functionfs(usb)) {
+                LOG(INFO) << "functionfs successfully initialized";
                 break;
             }
             std::this_thread::sleep_for(1s);
         }
 
-        D("[ usb_thread - registering device ]");
+        LOG(INFO) << "registering usb transport";
         register_usb_transport(usb, 0, 0, 1);
     }
 
@@ -430,6 +433,8 @@
 }
 
 static void usb_ffs_close(usb_handle* h) {
+    LOG(INFO) << "closing functionfs transport";
+
     h->kicked = false;
     adb_close(h->bulk_out);
     adb_close(h->bulk_in);
@@ -451,9 +456,7 @@
     h->close = usb_ffs_close;
 
     D("[ usb_init - starting thread ]");
-    if (!adb_thread_create(usb_ffs_open_thread, h)) {
-        fatal_errno("[ cannot create usb thread ]\n");
-    }
+    std::thread(usb_ffs_open_thread, h).detach();
 }
 
 void usb_init() {
diff --git a/adb/diagnose_usb.cpp b/adb/diagnose_usb.cpp
index 0f067b0..9f721bf 100644
--- a/adb/diagnose_usb.cpp
+++ b/adb/diagnose_usb.cpp
@@ -25,13 +25,14 @@
 
 #if defined(__linux__)
 #include <grp.h>
+#include <pwd.h>
 #endif
 
 static const char kPermissionsHelpUrl[] = "http://developer.android.com/tools/device.html";
 
-// Returns a message describing any potential problems we find with udev, or nullptr if we can't
-// find plugdev information (i.e. udev is not installed).
-static const char* GetUdevProblem() {
+// Returns a message describing any potential problems we find with udev, or an empty string if we
+// can't find plugdev information (i.e. udev is not installed).
+static std::string GetUdevProblem() {
 #if defined(__linux__)
     errno = 0;
     group* plugdev_group = getgrnam("plugdev");
@@ -41,43 +42,45 @@
             perror("failed to read plugdev group info");
         }
         // We can't give any generally useful advice here, just let the caller print the help URL.
-        return nullptr;
+        return "";
     }
 
-    // getgroups(2) indicates that the group_member() may not check the egid so we check it
+    // getgroups(2) indicates that the GNU group_member(3) may not check the egid so we check it
     // additionally just to be sure.
     if (group_member(plugdev_group->gr_gid) || getegid() == plugdev_group->gr_gid) {
         // The user is in plugdev so the problem is likely with the udev rules.
-        return "verify udev rules";
+        return "user in plugdev group; are your udev rules wrong?";
     }
-    return "udev requires plugdev group membership";
+    passwd* pwd = getpwuid(getuid());
+    return android::base::StringPrintf("user %s is not in the plugdev group",
+                                       pwd ? pwd->pw_name : "?");
 #else
-    return nullptr;
+    return "";
 #endif
 }
 
 // Short help text must be a single line, and will look something like:
-//   no permissions (reason); see <URL>
+//
+//   no permissions (reason); see [URL]
 std::string UsbNoPermissionsShortHelpText() {
     std::string help_text = "no permissions";
 
-    const char* problem = GetUdevProblem();
-    if (problem != nullptr) {
-        help_text += android::base::StringPrintf(" (%s)", problem);
-    }
+    std::string problem(GetUdevProblem());
+    if (!problem.empty()) help_text += " (" + problem + ")";
 
     return android::base::StringPrintf("%s; see [%s]", help_text.c_str(), kPermissionsHelpUrl);
 }
 
-// Long help text can span multiple lines and should provide more detailed information.
+// Long help text can span multiple lines but doesn't currently provide more detailed information:
+//
+//   insufficient permissions for device: reason
+//   See [URL] for more information
 std::string UsbNoPermissionsLongHelpText() {
     std::string header = "insufficient permissions for device";
 
-    const char* problem = GetUdevProblem();
-    if (problem != nullptr) {
-        header += android::base::StringPrintf(": %s", problem);
-    }
+    std::string problem(GetUdevProblem());
+    if (!problem.empty()) header += ": " + problem;
 
-    return android::base::StringPrintf("%s.\nSee [%s] for more information.",
-                                       header.c_str(), kPermissionsHelpUrl);
+    return android::base::StringPrintf("%s\nSee [%s] for more information", header.c_str(),
+                                       kPermissionsHelpUrl);
 }
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 04cd865..b28de4b 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -26,15 +26,19 @@
 #include <unistd.h>
 
 #include <atomic>
+#include <functional>
 #include <list>
+#include <mutex>
 #include <unordered_map>
 #include <vector>
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
 
 #include "adb_io.h"
 #include "adb_trace.h"
+#include "adb_unique_fd.h"
 #include "adb_utils.h"
 
 #if !ADB_HOST
@@ -75,13 +79,17 @@
 static bool main_thread_valid;
 static unsigned long main_thread_id;
 
-static void check_main_thread() {
+static auto& run_queue_notify_fd = *new unique_fd();
+static auto& run_queue_mutex = *new std::mutex();
+static auto& run_queue GUARDED_BY(run_queue_mutex) = *new std::vector<std::function<void()>>();
+
+void check_main_thread() {
     if (main_thread_valid) {
         CHECK_EQ(main_thread_id, adb_thread_id());
     }
 }
 
-static void set_main_thread() {
+void set_main_thread() {
     main_thread_valid = true;
     main_thread_id = adb_thread_id();
 }
@@ -112,8 +120,7 @@
     return android::base::StringPrintf("(fdevent %d %s)", fde->fd, state.c_str());
 }
 
-fdevent *fdevent_create(int fd, fd_func func, void *arg)
-{
+fdevent* fdevent_create(int fd, fd_func func, void* arg) {
     check_main_thread();
     fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
     if(fde == 0) return 0;
@@ -122,8 +129,7 @@
     return fde;
 }
 
-void fdevent_destroy(fdevent *fde)
-{
+void fdevent_destroy(fdevent* fde) {
     check_main_thread();
     if(fde == 0) return;
     if(!(fde->state & FDE_CREATED)) {
@@ -278,8 +284,7 @@
     }
 }
 
-static void fdevent_call_fdfunc(fdevent* fde)
-{
+static void fdevent_call_fdfunc(fdevent* fde) {
     unsigned events = fde->events;
     fde->events = 0;
     CHECK(fde->state & FDE_PENDING);
@@ -292,10 +297,7 @@
 
 #include <sys/ioctl.h>
 
-static void fdevent_subproc_event_func(int fd, unsigned ev,
-                                       void* /* userdata */)
-{
-
+static void fdevent_subproc_event_func(int fd, unsigned ev, void* /* userdata */) {
     D("subproc handling on fd = %d, ev = %x", fd, ev);
 
     CHECK_GE(fd, 0);
@@ -342,8 +344,7 @@
     }
 }
 
-void fdevent_subproc_setup()
-{
+static void fdevent_subproc_setup() {
     int s[2];
 
     if(adb_socketpair(s)) {
@@ -358,12 +359,63 @@
 }
 #endif // !ADB_HOST
 
-void fdevent_loop()
-{
+static void fdevent_run_flush() REQUIRES(run_queue_mutex) {
+    for (auto& f : run_queue) {
+        f();
+    }
+    run_queue.clear();
+}
+
+static void fdevent_run_func(int fd, unsigned ev, void* /* userdata */) {
+    CHECK_GE(fd, 0);
+    CHECK(ev & FDE_READ);
+
+    char buf[1024];
+
+    // Empty the fd.
+    if (adb_read(fd, buf, sizeof(buf)) == -1) {
+        PLOG(FATAL) << "failed to empty run queue notify fd";
+    }
+
+    std::lock_guard<std::mutex> lock(run_queue_mutex);
+    fdevent_run_flush();
+}
+
+static void fdevent_run_setup() {
+    std::lock_guard<std::mutex> lock(run_queue_mutex);
+    CHECK(run_queue_notify_fd.get() == -1);
+    int s[2];
+    if (adb_socketpair(s) != 0) {
+        PLOG(FATAL) << "failed to create run queue notify socketpair";
+    }
+
+    run_queue_notify_fd.reset(s[0]);
+    fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
+    CHECK(fde != nullptr);
+    fdevent_add(fde, FDE_READ);
+
+    fdevent_run_flush();
+}
+
+void fdevent_run_on_main_thread(std::function<void()> fn) {
+    std::lock_guard<std::mutex> lock(run_queue_mutex);
+    run_queue.push_back(std::move(fn));
+
+    // run_queue_notify_fd could still be -1 if we're called before fdevent has finished setting up.
+    // In that case, rely on the setup code to flush the queue without a notification being needed.
+    if (run_queue_notify_fd != -1) {
+        if (adb_write(run_queue_notify_fd.get(), "", 1) != 1) {
+            PLOG(FATAL) << "failed to write to run queue notify fd";
+        }
+    }
+}
+
+void fdevent_loop() {
     set_main_thread();
 #if !ADB_HOST
     fdevent_subproc_setup();
 #endif // !ADB_HOST
+    fdevent_run_setup();
 
     while (true) {
         if (terminate_loop) {
@@ -393,6 +445,11 @@
 void fdevent_reset() {
     g_poll_node_map.clear();
     g_pending_list.clear();
+
+    std::lock_guard<std::mutex> lock(run_queue_mutex);
+    run_queue_notify_fd.reset();
+    run_queue.clear();
+
     main_thread_valid = false;
     terminate_loop = false;
 }
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 207f9b7..896400a 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -20,6 +20,8 @@
 #include <stddef.h>
 #include <stdint.h>  /* for int64_t */
 
+#include <functional>
+
 /* events that may be observed */
 #define FDE_READ              0x0001
 #define FDE_WRITE             0x0002
@@ -76,9 +78,15 @@
 */
 void fdevent_loop();
 
+void check_main_thread();
+
+// Queue an operation to run on the main thread.
+void fdevent_run_on_main_thread(std::function<void()> fn);
+
 // The following functions are used only for tests.
 void fdevent_terminate_loop();
 size_t fdevent_installed_count();
 void fdevent_reset();
+void set_main_thread();
 
 #endif
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index c933ed5..86e0209 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -21,6 +21,7 @@
 #include <limits>
 #include <queue>
 #include <string>
+#include <thread>
 #include <vector>
 
 #include "adb_io.h"
@@ -77,9 +78,9 @@
 };
 
 TEST_F(FdeventTest, fdevent_terminate) {
-    adb_thread_t thread;
     PrepareThread();
-    ASSERT_TRUE(adb_thread_create([](void*) { fdevent_loop(); }, nullptr, &thread));
+
+    std::thread thread(fdevent_loop);
     TerminateThread(thread);
 }
 
@@ -112,7 +113,6 @@
     int fd_pair2[2];
     ASSERT_EQ(0, adb_socketpair(fd_pair1));
     ASSERT_EQ(0, adb_socketpair(fd_pair2));
-    adb_thread_t thread;
     ThreadArg thread_arg;
     thread_arg.first_read_fd = fd_pair1[0];
     thread_arg.last_write_fd = fd_pair2[1];
@@ -121,8 +121,7 @@
     int reader = fd_pair2[0];
 
     PrepareThread();
-    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(FdEventThreadFunc), &thread_arg,
-                                  &thread));
+    std::thread thread(FdEventThreadFunc, &thread_arg);
 
     for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
         std::string read_buffer = MESSAGE;
@@ -152,7 +151,7 @@
     }
 }
 
-static void InvalidFdThreadFunc(void*) {
+static void InvalidFdThreadFunc() {
     const int INVALID_READ_FD = std::numeric_limits<int>::max() - 1;
     size_t happened_event_count = 0;
     InvalidFdArg read_arg;
@@ -171,7 +170,27 @@
 }
 
 TEST_F(FdeventTest, invalid_fd) {
-    adb_thread_t thread;
-    ASSERT_TRUE(adb_thread_create(InvalidFdThreadFunc, nullptr, &thread));
-    ASSERT_TRUE(adb_thread_join(thread));
+    std::thread thread(InvalidFdThreadFunc);
+    thread.join();
+}
+
+TEST_F(FdeventTest, run_on_main_thread) {
+    std::vector<int> vec;
+
+    PrepareThread();
+    std::thread thread(fdevent_loop);
+
+    for (int i = 0; i < 100; ++i) {
+        fdevent_run_on_main_thread([i, &vec]() {
+            check_main_thread();
+            vec.push_back(i);
+        });
+    }
+
+    TerminateThread(thread);
+
+    ASSERT_EQ(100u, vec.size());
+    for (int i = 0; i < 100; ++i) {
+        ASSERT_EQ(i, vec[i]);
+    }
 }
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
index ef65b74..5ca49ac 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent_test.h
@@ -16,6 +16,8 @@
 
 #include <gtest/gtest.h>
 
+#include <thread>
+
 #include "socket.h"
 #include "sysdeps.h"
 
@@ -51,18 +53,18 @@
 
     size_t GetAdditionalLocalSocketCount() {
 #if ADB_HOST
-        // dummy socket installed in PrepareThread()
-        return 1;
-#else
-        // dummy socket and one more socket installed in fdevent_subproc_setup()
+        // dummy socket installed in PrepareThread() + fdevent_run_on_main_thread socket
         return 2;
+#else
+        // dummy socket + fdevent_run_on_main_thread + fdevent_subproc_setup() sockets
+        return 3;
 #endif
     }
 
-    void TerminateThread(adb_thread_t thread) {
+    void TerminateThread(std::thread& thread) {
         fdevent_terminate_loop();
         ASSERT_TRUE(WriteFdExactly(dummy, "", 1));
-        ASSERT_TRUE(adb_thread_join(thread));
+        thread.join();
         ASSERT_EQ(0, adb_close(dummy));
     }
 };
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 22bd2f2..2576fb1 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -674,11 +674,22 @@
     return true;
 }
 
-static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
-                      unsigned mtime, mode_t mode)
-{
+static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, unsigned mtime,
+                      mode_t mode, bool sync) {
     std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
 
+    if (sync) {
+        struct stat st;
+        if (sync_lstat(sc, rpath, &st)) {
+            // For links, we cannot update the atime/mtime.
+            if ((S_ISREG(mode & st.st_mode) && st.st_mtime == static_cast<time_t>(mtime)) ||
+                (S_ISLNK(mode & st.st_mode) && st.st_mtime >= static_cast<time_t>(mtime))) {
+                sc.RecordFilesSkipped(1);
+                return true;
+            }
+        }
+    }
+
     if (S_ISLNK(mode)) {
 #if !defined(_WIN32)
         char buf[PATH_MAX];
@@ -902,7 +913,7 @@
             if (list_only) {
                 sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
             } else {
-                if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode)) {
+                if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode, false)) {
                     return false;
                 }
             }
@@ -916,7 +927,7 @@
     return true;
 }
 
-bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync) {
     SyncConnection sc;
     if (!sc.IsValid()) return false;
 
@@ -981,7 +992,7 @@
                 dst_dir.append(android::base::Basename(src_path));
             }
 
-            success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), false, false);
+            success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), sync, false);
             continue;
         } else if (!should_push_file(st.st_mode)) {
             sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
@@ -1002,7 +1013,7 @@
 
         sc.NewTransfer();
         sc.SetExpectedTotalBytes(st.st_size);
-        success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
+        success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync);
         sc.ReportTransferRate(src_path, TransferDirection::push);
     }
 
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 90f1965..6606efd 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -81,7 +81,7 @@
 
 void file_sync_service(int fd, void* cookie);
 bool do_sync_ls(const char* path);
-bool do_sync_push(const std::vector<const char*>& srcs, const char* dst);
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync);
 bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
                   bool copy_attrs, const char* name=nullptr);
 
diff --git a/adb/framebuffer_service.cpp b/adb/framebuffer_service.cpp
index 7baad8b..6c3a225 100644
--- a/adb/framebuffer_service.cpp
+++ b/adb/framebuffer_service.cpp
@@ -37,10 +37,11 @@
 */
 /* This version number defines the format of the fbinfo struct.
    It must match versioning in ddms where this data is consumed. */
-#define DDMS_RAWIMAGE_VERSION 1
+#define DDMS_RAWIMAGE_VERSION 2
 struct fbinfo {
     unsigned int version;
     unsigned int bpp;
+    unsigned int colorSpace;
     unsigned int size;
     unsigned int width;
     unsigned int height;
@@ -60,7 +61,7 @@
     unsigned int i, bsize;
     char buf[640];
     int fd_screencap;
-    int w, h, f;
+    int w, h, f, c;
     int fds[2];
     pid_t pid;
 
@@ -82,12 +83,14 @@
     adb_close(fds[1]);
     fd_screencap = fds[0];
 
-    /* read w, h & format */
+    /* read w, h, format & color space */
     if(!ReadFdExactly(fd_screencap, &w, 4)) goto done;
     if(!ReadFdExactly(fd_screencap, &h, 4)) goto done;
     if(!ReadFdExactly(fd_screencap, &f, 4)) goto done;
+    if(!ReadFdExactly(fd_screencap, &c, 4)) goto done;
 
     fbinfo.version = DDMS_RAWIMAGE_VERSION;
+    fbinfo.colorSpace = c;
     /* see hardware/hardware.h */
     switch (f) {
         case 1: /* RGBA_8888 */
diff --git a/adb/services.cpp b/adb/services.cpp
index c9ea039..9605e6e 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -31,6 +31,8 @@
 #include <unistd.h>
 #endif
 
+#include <thread>
+
 #include <android-base/file.h>
 #include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
@@ -259,13 +261,7 @@
     sti->cookie = cookie;
     sti->fd = s[1];
 
-    if (!adb_thread_create(service_bootstrap_func, sti)) {
-        free(sti);
-        adb_close(s[0]);
-        adb_close(s[1]);
-        printf("cannot create service thread\n");
-        return -1;
-    }
+    std::thread(service_bootstrap_func, sti).detach();
 
     D("service thread started, %d:%d",s[0], s[1]);
     return s[0];
@@ -351,7 +347,7 @@
         std::string error = "unknown error";
         const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL;
         atransport* t = acquire_one_transport(sinfo->transport_type, serial, &is_ambiguous, &error);
-        if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->connection_state)) {
+        if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
             SendOkay(fd);
             break;
         } else if (!is_ambiguous) {
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp
index d4dd256..253d14a 100644
--- a/adb/set_verity_enable_state_service.cpp
+++ b/adb/set_verity_enable_state_service.cpp
@@ -20,6 +20,7 @@
 
 #include <fcntl.h>
 #include <inttypes.h>
+#include <libavb_user/libavb_user.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <sys/stat.h>
@@ -45,13 +46,12 @@
 #endif
 
 /* Turn verity on/off */
-static int set_verity_enabled_state(int fd, const char *block_device,
-                                    const char* mount_point, bool enable)
-{
+static bool set_verity_enabled_state(int fd, const char* block_device, const char* mount_point,
+                                     bool enable) {
     if (!make_block_device_writable(block_device)) {
         WriteFdFmt(fd, "Could not make block device %s writable (%s).\n",
                    block_device, strerror(errno));
-        return -1;
+        return false;
     }
 
     fec::io fh(block_device, O_RDWR);
@@ -59,70 +59,142 @@
     if (!fh) {
         WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
         WriteFdFmt(fd, "Maybe run adb root?\n");
-        return -1;
+        return false;
     }
 
     fec_verity_metadata metadata;
 
     if (!fh.get_verity_metadata(metadata)) {
         WriteFdFmt(fd, "Couldn't find verity metadata!\n");
-        return -1;
+        return false;
     }
 
     if (!enable && metadata.disabled) {
         WriteFdFmt(fd, "Verity already disabled on %s\n", mount_point);
-        return -1;
+        return false;
     }
 
     if (enable && !metadata.disabled) {
         WriteFdFmt(fd, "Verity already enabled on %s\n", mount_point);
-        return -1;
+        return false;
     }
 
     if (!fh.set_verity_status(enable)) {
         WriteFdFmt(fd, "Could not set verity %s flag on device %s with error %s\n",
                    enable ? "enabled" : "disabled",
                    block_device, strerror(errno));
-        return -1;
+        return false;
     }
 
     WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
-    return 0;
+    return true;
+}
+
+/* Helper function to get A/B suffix, if any. If the device isn't
+ * using A/B the empty string is returned. Otherwise either "_a",
+ * "_b", ... is returned.
+ *
+ * Note that since sometime in O androidboot.slot_suffix is deprecated
+ * and androidboot.slot should be used instead. Since bootloaders may
+ * be out of sync with the OS, we check both and for extra safety
+ * prepend a leading underscore if there isn't one already.
+ */
+static std::string get_ab_suffix() {
+    std::string ab_suffix = android::base::GetProperty("ro.boot.slot_suffix", "");
+    if (ab_suffix == "") {
+        ab_suffix = android::base::GetProperty("ro.boot.slot", "");
+    }
+    if (ab_suffix.size() > 0 && ab_suffix[0] != '_') {
+        ab_suffix = std::string("_") + ab_suffix;
+    }
+    return ab_suffix;
+}
+
+/* Use AVB to turn verity on/off */
+static bool set_avb_verity_enabled_state(int fd, AvbOps* ops, bool enable_verity) {
+    std::string ab_suffix = get_ab_suffix();
+
+    bool verity_enabled;
+    if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
+        WriteFdFmt(fd, "Error getting verity state\n");
+        return false;
+    }
+
+    if ((verity_enabled && enable_verity) || (!verity_enabled && !enable_verity)) {
+        WriteFdFmt(fd, "verity is already %s\n", verity_enabled ? "enabled" : "disabled");
+        return false;
+    }
+
+    if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
+        WriteFdFmt(fd, "Error setting verity\n");
+        return false;
+    }
+
+    WriteFdFmt(fd, "Successfully %s verity\n", enable_verity ? "enabled" : "disabled");
+    return true;
 }
 
 void set_verity_enabled_state_service(int fd, void* cookie) {
     unique_fd closer(fd);
+    bool any_changed = false;
 
     bool enable = (cookie != NULL);
-    if (!kAllowDisableVerity) {
-        WriteFdFmt(fd, "%s-verity only works for userdebug builds\n",
-                   enable ? "enable" : "disable");
+
+    // Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
+    // contract, androidboot.vbmeta.digest is set by the bootloader
+    // when using AVB).
+    bool using_avb = !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
+
+    // If using AVB, dm-verity is used on any build so we want it to
+    // be possible to disable/enable on any build (except USER). For
+    // VB1.0 dm-verity is only enabled on certain builds.
+    if (!using_avb) {
+        if (!kAllowDisableVerity) {
+            WriteFdFmt(fd, "%s-verity only works for userdebug builds\n",
+                       enable ? "enable" : "disable");
+        }
+
+        if (!android::base::GetBoolProperty("ro.secure", false)) {
+            WriteFdFmt(fd, "verity not enabled - ENG build\n");
+            return;
+        }
     }
 
-    if (!android::base::GetBoolProperty("ro.secure", false)) {
-        WriteFdFmt(fd, "verity not enabled - ENG build\n");
-        return;
-    }
+    // Should never be possible to disable dm-verity on a USER build
+    // regardless of using AVB or VB1.0.
     if (!__android_log_is_debuggable()) {
         WriteFdFmt(fd, "verity cannot be disabled/enabled - USER build\n");
         return;
     }
 
-    // read all fstab entries at once from all sources
-    fstab = fs_mgr_read_fstab_default();
-    if (!fstab) {
-        WriteFdFmt(fd, "Failed to read fstab\nMaybe run adb root?\n");
-        return;
-    }
+    if (using_avb) {
+        // Yep, the system is using AVB.
+        AvbOps* ops = avb_ops_user_new();
+        if (ops == nullptr) {
+            WriteFdFmt(fd, "Error getting AVB ops\n");
+            return;
+        }
+        if (set_avb_verity_enabled_state(fd, ops, enable)) {
+            any_changed = true;
+        }
+        avb_ops_user_free(ops);
+    } else {
+        // Not using AVB - assume VB1.0.
 
-    // Loop through entries looking for ones that vold manages.
-    bool any_changed = false;
-    for (int i = 0; i < fstab->num_entries; i++) {
-        if (fs_mgr_is_verified(&fstab->recs[i])) {
-            if (!set_verity_enabled_state(fd, fstab->recs[i].blk_device,
-                                          fstab->recs[i].mount_point,
-                                          enable)) {
-                any_changed = true;
+        // read all fstab entries at once from all sources
+        fstab = fs_mgr_read_fstab_default();
+        if (!fstab) {
+            WriteFdFmt(fd, "Failed to read fstab\nMaybe run adb root?\n");
+            return;
+        }
+
+        // Loop through entries looking for ones that vold manages.
+        for (int i = 0; i < fstab->num_entries; i++) {
+            if (fs_mgr_is_verified(&fstab->recs[i])) {
+                if (set_verity_enabled_state(fd, fstab->recs[i].blk_device,
+                                             fstab->recs[i].mount_point, enable)) {
+                    any_changed = true;
+                }
             }
         }
     }
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index d4f334b..ee821f8 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -90,6 +90,7 @@
 
 #include <memory>
 #include <string>
+#include <thread>
 #include <unordered_map>
 #include <vector>
 
@@ -392,12 +393,7 @@
 
 bool Subprocess::StartThread(std::unique_ptr<Subprocess> subprocess, std::string* error) {
     Subprocess* raw = subprocess.release();
-    if (!adb_thread_create(ThreadHandler, raw)) {
-        *error =
-            android::base::StringPrintf("failed to create subprocess thread: %s", strerror(errno));
-        kill(raw->pid_, SIGKILL);
-        return false;
-    }
+    std::thread(ThreadHandler, raw).detach();
 
     return true;
 }
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index f56f7f7..f7c66db 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -42,10 +42,6 @@
 
 class LocalSocketTest : public FdeventTest {};
 
-static void FdEventThreadFunc(void*) {
-    fdevent_loop();
-}
-
 constexpr auto SLEEP_FOR_FDEVENT = 100ms;
 
 TEST_F(LocalSocketTest, smoke) {
@@ -88,8 +84,7 @@
     connect(prev_tail, end);
 
     PrepareThread();
-    adb_thread_t thread;
-    ASSERT_TRUE(adb_thread_create(FdEventThreadFunc, nullptr, &thread));
+    std::thread thread(fdevent_loop);
 
     for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
         std::string read_buffer = MESSAGE;
@@ -152,9 +147,7 @@
     arg.cause_close_fd = cause_close_fd[1];
 
     PrepareThread();
-    adb_thread_t thread;
-    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
-                                  &arg, &thread));
+    std::thread thread(CloseWithPacketThreadFunc, &arg);
     // Wait until the fdevent_loop() starts.
     std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
     ASSERT_EQ(0, adb_close(cause_close_fd[0]));
@@ -177,9 +170,7 @@
     arg.cause_close_fd = cause_close_fd[1];
 
     PrepareThread();
-    adb_thread_t thread;
-    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
-                                  &arg, &thread));
+    std::thread thread(CloseWithPacketThreadFunc, &arg);
     // Wait until the fdevent_loop() starts.
     std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
     ASSERT_EQ(0, adb_close(cause_close_fd[0]));
@@ -211,10 +202,7 @@
     arg.cause_close_fd = cause_close_fd[1];
 
     PrepareThread();
-    adb_thread_t thread;
-    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
-                                  &arg, &thread));
-
+    std::thread thread(CloseWithPacketThreadFunc, &arg);
     // Wait until the fdevent_loop() starts.
     std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
     EXPECT_EQ(2u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
@@ -252,9 +240,7 @@
     int listen_fd = network_inaddr_any_server(5038, SOCK_STREAM, &error);
     ASSERT_GE(listen_fd, 0);
 
-    adb_thread_t client_thread;
-    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(ClientThreadFunc), nullptr,
-                                  &client_thread));
+    std::thread client_thread(ClientThreadFunc);
 
     int accept_fd = adb_socket_accept(listen_fd, nullptr, nullptr);
     ASSERT_GE(accept_fd, 0);
@@ -262,16 +248,14 @@
     arg.socket_fd = accept_fd;
 
     PrepareThread();
-    adb_thread_t thread;
-    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseRdHupSocketThreadFunc),
-                                  &arg, &thread));
+    std::thread thread(CloseRdHupSocketThreadFunc, &arg);
 
     // Wait until the fdevent_loop() starts.
     std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
     EXPECT_EQ(1u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
 
     // Wait until the client closes its socket.
-    ASSERT_TRUE(adb_thread_join(client_thread));
+    client_thread.join();
 
     std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
     ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 59a48f5..e0143c6 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -686,7 +686,7 @@
     }
 
     len = unhex(p->data, 4);
-    if ((len < 1) || (len > MAX_PAYLOAD_V1)) {
+    if ((len < 1) || (len > MAX_PAYLOAD)) {
         D("SS(%d): bad size (%d)", s->id, len);
         goto fail;
     }
@@ -794,7 +794,7 @@
     if (!s->transport) {
         SendFail(s->peer->fd, "device offline (no transport)");
         goto fail;
-    } else if (s->transport->connection_state == kCsOffline) {
+    } else if (s->transport->GetConnectionState() == kCsOffline) {
         /* if there's no remote we fail the connection
          ** right here and terminate it
          */
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index f195b4e..49c7847 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -35,6 +35,7 @@
 #include <android-base/utf8.h>
 
 #include "sysdeps/errno.h"
+#include "sysdeps/network.h"
 #include "sysdeps/stat.h"
 
 /*
@@ -98,64 +99,6 @@
     return c == '\\' || c == '/';
 }
 
-typedef void (*adb_thread_func_t)(void* arg);
-typedef HANDLE adb_thread_t;
-
-struct adb_winthread_args {
-    adb_thread_func_t func;
-    void* arg;
-};
-
-static unsigned __stdcall adb_winthread_wrapper(void* heap_args) {
-    // Move the arguments from the heap onto the thread's stack.
-    adb_winthread_args thread_args = *static_cast<adb_winthread_args*>(heap_args);
-    delete static_cast<adb_winthread_args*>(heap_args);
-    thread_args.func(thread_args.arg);
-    return 0;
-}
-
-static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg,
-                                         adb_thread_t* thread = nullptr) {
-    adb_winthread_args* args = new adb_winthread_args{.func = func, .arg = arg};
-    uintptr_t handle = _beginthreadex(nullptr, 0, adb_winthread_wrapper, args, 0, nullptr);
-    if (handle != static_cast<uintptr_t>(0)) {
-        if (thread) {
-            *thread = reinterpret_cast<HANDLE>(handle);
-        } else {
-            CloseHandle(thread);
-        }
-        return true;
-    }
-    return false;
-}
-
-static __inline__ bool adb_thread_join(adb_thread_t thread) {
-    switch (WaitForSingleObject(thread, INFINITE)) {
-        case WAIT_OBJECT_0:
-            CloseHandle(thread);
-            return true;
-
-        case WAIT_FAILED:
-            fprintf(stderr, "adb_thread_join failed: %s\n",
-                    android::base::SystemErrorCodeToString(GetLastError()).c_str());
-            break;
-
-        default:
-            abort();
-    }
-
-    return false;
-}
-
-static __inline__ bool adb_thread_detach(adb_thread_t thread) {
-    CloseHandle(thread);
-    return true;
-}
-
-static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
-    _endthreadex(0);
-}
-
 static __inline__ int adb_thread_setname(const std::string& name) {
     // TODO: See https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx for how to set
     // the thread name in Windows. Unfortunately, it only works during debugging, but
@@ -163,14 +106,6 @@
     return 0;
 }
 
-static __inline__ adb_thread_t adb_thread_self() {
-    return GetCurrentThread();
-}
-
-static __inline__ bool adb_thread_equal(adb_thread_t lhs, adb_thread_t rhs) {
-    return GetThreadId(lhs) == GetThreadId(rhs);
-}
-
 static __inline__  unsigned long adb_thread_id()
 {
     return GetCurrentThreadId();
@@ -248,8 +183,6 @@
 int unix_isatty(int fd);
 #define  isatty  ___xxx_isatty
 
-int network_loopback_client(int port, int type, std::string* error);
-int network_loopback_server(int port, int type, std::string* error);
 int network_inaddr_any_server(int port, int type, std::string* error);
 
 inline int network_local_client(const char* name, int namespace_id, int type, std::string* error) {
@@ -268,10 +201,6 @@
 #undef   accept
 #define  accept  ___xxx_accept
 
-int adb_getsockname(int fd, struct sockaddr* sockaddr, socklen_t* optlen);
-#undef getsockname
-#define getsockname(...) ___xxx_getsockname(__VA__ARGS__)
-
 // Returns the local port number of a bound socket, or -1 on failure.
 int adb_socket_get_local_port(int fd);
 
@@ -591,17 +520,6 @@
   return fd;
 }
 
-inline int network_loopback_client(int port, int type, std::string* error) {
-  return _fd_set_error_str(socket_network_client("localhost", port, type), error);
-}
-
-inline int network_loopback_server(int port, int type, std::string* error) {
-  int fd = socket_loopback_server(port, type);
-  if (fd < 0 && errno == EAFNOSUPPORT)
-      return _fd_set_error_str(socket_loopback_server6(port, type), error);
-  return _fd_set_error_str(fd, error);
-}
-
 inline int network_inaddr_any_server(int port, int type, std::string* error) {
   return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
 }
@@ -660,55 +578,6 @@
 #define  unix_write  adb_write
 #define  unix_close  adb_close
 
-// Win32 is limited to DWORDs for thread return values; limit the POSIX systems to this as well to
-// ensure compatibility.
-typedef void (*adb_thread_func_t)(void* arg);
-typedef pthread_t adb_thread_t;
-
-struct adb_pthread_args {
-    adb_thread_func_t func;
-    void* arg;
-};
-
-static void* adb_pthread_wrapper(void* heap_args) {
-    // Move the arguments from the heap onto the thread's stack.
-    adb_pthread_args thread_args = *reinterpret_cast<adb_pthread_args*>(heap_args);
-    delete static_cast<adb_pthread_args*>(heap_args);
-    thread_args.func(thread_args.arg);
-    return nullptr;
-}
-
-static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg,
-                                         adb_thread_t* thread = nullptr) {
-    pthread_t temp;
-    pthread_attr_t attr;
-    pthread_attr_init(&attr);
-    pthread_attr_setdetachstate(&attr, thread ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED);
-    auto* pthread_args = new adb_pthread_args{.func = start, .arg = arg};
-    errno = pthread_create(&temp, &attr, adb_pthread_wrapper, pthread_args);
-    if (errno == 0) {
-        if (thread) {
-            *thread = temp;
-        }
-        return true;
-    }
-    return false;
-}
-
-static __inline__ bool adb_thread_join(adb_thread_t thread) {
-    errno = pthread_join(thread, nullptr);
-    return errno == 0;
-}
-
-static __inline__ bool adb_thread_detach(adb_thread_t thread) {
-    errno = pthread_detach(thread);
-    return errno == 0;
-}
-
-static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
-    pthread_exit(nullptr);
-}
-
 static __inline__ int adb_thread_setname(const std::string& name) {
 #ifdef __APPLE__
     return pthread_setname_np(name.c_str());
diff --git a/libsystem/include/system/window.h b/adb/sysdeps/network.h
similarity index 70%
copy from libsystem/include/system/window.h
copy to adb/sysdeps/network.h
index efa10d6..83ce371 100644
--- a/libsystem/include/system/window.h
+++ b/adb/sysdeps/network.h
@@ -1,5 +1,7 @@
+#pragma once
+
 /*
- * 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.
@@ -14,9 +16,7 @@
  * limitations under the License.
  */
 
-#ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
-#define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
+#include <string>
 
-#include <system/window-deprecated.h>
-
-#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
+int network_loopback_client(int port, int type, std::string* error);
+int network_loopback_server(int port, int type, std::string* error);
diff --git a/adb/sysdeps/posix/network.cpp b/adb/sysdeps/posix/network.cpp
new file mode 100644
index 0000000..45da5af
--- /dev/null
+++ b/adb/sysdeps/posix/network.cpp
@@ -0,0 +1,127 @@
+/*
+ * 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 "sysdeps/network.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include <string>
+
+#include "adb_unique_fd.h"
+
+static void set_error(std::string* error) {
+    if (error) {
+        *error = strerror(errno);
+    }
+}
+
+static sockaddr* loopback_addr4(sockaddr_storage* addr, socklen_t* addrlen, int port) {
+    struct sockaddr_in* addr4 = reinterpret_cast<sockaddr_in*>(addr);
+    *addrlen = sizeof(*addr4);
+
+    addr4->sin_family = AF_INET;
+    addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    addr4->sin_port = htons(port);
+    return reinterpret_cast<sockaddr*>(addr);
+}
+
+static sockaddr* loopback_addr6(sockaddr_storage* addr, socklen_t* addrlen, int port) {
+    struct sockaddr_in6* addr6 = reinterpret_cast<sockaddr_in6*>(addr);
+    *addrlen = sizeof(*addr6);
+
+    addr6->sin6_family = AF_INET6;
+    addr6->sin6_addr = in6addr_loopback;
+    addr6->sin6_port = htons(port);
+    return reinterpret_cast<sockaddr*>(addr);
+}
+
+static int _network_loopback_client(bool ipv6, int port, int type, std::string* error) {
+    unique_fd s(socket(ipv6 ? AF_INET6 : AF_INET, type, 0));
+    if (s == -1) {
+        set_error(error);
+        return -1;
+    }
+
+    struct sockaddr_storage addr_storage = {};
+    socklen_t addrlen = sizeof(addr_storage);
+    sockaddr* addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, 0);
+
+    if (bind(s.get(), addr, addrlen) != 0) {
+        set_error(error);
+        return -1;
+    }
+
+    addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, port);
+
+    if (connect(s.get(), addr, addrlen) != 0) {
+        set_error(error);
+        return -1;
+    }
+
+    return s.release();
+}
+
+int network_loopback_client(int port, int type, std::string* error) {
+    // Try IPv4 first, use IPv6 as a fallback.
+    int rc = _network_loopback_client(false, port, type, error);
+    if (rc == -1) {
+        return _network_loopback_client(true, port, type, error);
+    }
+    return rc;
+}
+
+static int _network_loopback_server(bool ipv6, int port, int type, std::string* error) {
+    unique_fd s(socket(ipv6 ? AF_INET6 : AF_INET, type, 0));
+    if (s == -1) {
+        set_error(error);
+        return -1;
+    }
+
+    int n = 1;
+    setsockopt(s.get(), SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+    struct sockaddr_storage addr_storage = {};
+    socklen_t addrlen = sizeof(addr_storage);
+    sockaddr* addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, port);
+
+    if (bind(s, addr, addrlen) != 0) {
+        set_error(error);
+        return -1;
+    }
+
+    if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
+        // Arbitrarily selected value, ported from libcutils.
+        if (listen(s, 4) != 0) {
+            set_error(error);
+            return -1;
+        }
+    }
+
+    return s.release();
+}
+
+int network_loopback_server(int port, int type, std::string* error) {
+    int rc = _network_loopback_server(false, port, type, error);
+
+    // Only attempt to listen on IPv6 if IPv4 is unavailable.
+    // We don't want to start an IPv6 server if there's already an IPv4 one running.
+    if (rc == -1 && (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT)) {
+        return _network_loopback_server(true, port, type, error);
+    }
+    return rc;
+}
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
index 9007e75..fd19882 100644
--- a/adb/sysdeps_test.cpp
+++ b/adb/sysdeps_test.cpp
@@ -25,54 +25,6 @@
 #include "sysdeps.h"
 #include "sysdeps/chrono.h"
 
-static void increment_atomic_int(void* c) {
-    std::this_thread::sleep_for(1s);
-    reinterpret_cast<std::atomic<int>*>(c)->fetch_add(1);
-}
-
-TEST(sysdeps_thread, smoke) {
-    std::atomic<int> counter(0);
-
-    for (int i = 0; i < 100; ++i) {
-        ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter));
-    }
-
-    std::this_thread::sleep_for(2s);
-    ASSERT_EQ(100, counter.load());
-}
-
-TEST(sysdeps_thread, join) {
-    std::atomic<int> counter(0);
-    std::vector<adb_thread_t> threads(500);
-    for (size_t i = 0; i < threads.size(); ++i) {
-        ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter, &threads[i]));
-    }
-
-    int current = counter.load();
-    ASSERT_GE(current, 0);
-    // Make sure that adb_thread_create actually creates threads, and doesn't do something silly
-    // like synchronously run the function passed in. The sleep in increment_atomic_int should be
-    // enough to keep this from being flakey.
-    ASSERT_LT(current, 500);
-
-    for (const auto& thread : threads) {
-        ASSERT_TRUE(adb_thread_join(thread));
-    }
-
-    ASSERT_EQ(500, counter.load());
-}
-
-TEST(sysdeps_thread, exit) {
-    adb_thread_t thread;
-    ASSERT_TRUE(adb_thread_create(
-        [](void*) {
-            adb_thread_exit();
-            for (;;) continue;
-        },
-        nullptr, &thread));
-    ASSERT_TRUE(adb_thread_join(thread));
-}
-
 TEST(sysdeps_socketpair, smoke) {
     int fds[2];
     ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
@@ -249,65 +201,22 @@
     }
 }
 
-TEST(sysdeps_mutex, mutex_smoke) {
-    static std::atomic<bool> finished(false);
-    static std::mutex &m = *new std::mutex();
-    m.lock();
-    ASSERT_FALSE(m.try_lock());
-    adb_thread_create([](void*) {
-        ASSERT_FALSE(m.try_lock());
-        m.lock();
-        finished.store(true);
-        std::this_thread::sleep_for(200ms);
-        m.unlock();
-    }, nullptr);
-
-    ASSERT_FALSE(finished.load());
-    std::this_thread::sleep_for(100ms);
-    ASSERT_FALSE(finished.load());
-    m.unlock();
-    std::this_thread::sleep_for(100ms);
-    m.lock();
-    ASSERT_TRUE(finished.load());
-    m.unlock();
-}
-
-TEST(sysdeps_mutex, recursive_mutex_smoke) {
-    static std::recursive_mutex &m = *new std::recursive_mutex();
-
-    m.lock();
-    ASSERT_TRUE(m.try_lock());
-    m.unlock();
-
-    adb_thread_create([](void*) {
-        ASSERT_FALSE(m.try_lock());
-        m.lock();
-        std::this_thread::sleep_for(500ms);
-        m.unlock();
-    }, nullptr);
-
-    std::this_thread::sleep_for(100ms);
-    m.unlock();
-    std::this_thread::sleep_for(100ms);
-    ASSERT_FALSE(m.try_lock());
-    m.lock();
-    m.unlock();
-}
-
 TEST(sysdeps_condition_variable, smoke) {
     static std::mutex &m = *new std::mutex;
     static std::condition_variable &cond = *new std::condition_variable;
     static volatile bool flag = false;
 
     std::unique_lock<std::mutex> lock(m);
-    adb_thread_create([](void*) {
+    std::thread thread([]() {
         m.lock();
         flag = true;
         cond.notify_one();
         m.unlock();
-    }, nullptr);
+    });
 
     while (!flag) {
         cond.wait(lock);
     }
+
+    thread.join();
 }
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index f997e6b..5873b2b 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -984,7 +984,7 @@
         return -1;
     }
 
-    int result = (getsockname)(fh->fh_socket, sockaddr, optlen);
+    int result = getsockname(fh->fh_socket, sockaddr, optlen);
     if (result == SOCKET_ERROR) {
         const DWORD err = WSAGetLastError();
         D("adb_getsockname: setsockopt on fd %d failed: %s\n", fd,
@@ -1042,11 +1042,6 @@
     int local_port = -1;
     std::string error;
 
-    struct sockaddr_storage peer_addr = {};
-    struct sockaddr_storage client_addr = {};
-    socklen_t peer_socklen = sizeof(peer_addr);
-    socklen_t client_socklen = sizeof(client_addr);
-
     server = network_loopback_server(0, SOCK_STREAM, &error);
     if (server < 0) {
         D("adb_socketpair: failed to create server: %s", error.c_str());
@@ -1066,32 +1061,12 @@
         goto fail;
     }
 
-    // Make sure that the peer that connected to us and the client are the same.
-    accepted = adb_socket_accept(server, reinterpret_cast<sockaddr*>(&peer_addr), &peer_socklen);
+    accepted = adb_socket_accept(server, nullptr, nullptr);
     if (accepted < 0) {
         D("adb_socketpair: failed to accept: %s", strerror(errno));
         goto fail;
     }
-
-    if (adb_getsockname(client, reinterpret_cast<sockaddr*>(&client_addr), &client_socklen) != 0) {
-        D("adb_socketpair: failed to getpeername: %s", strerror(errno));
-        goto fail;
-    }
-
-    if (peer_socklen != client_socklen) {
-        D("adb_socketpair: client and peer sockaddrs have different lengths");
-        errno = EIO;
-        goto fail;
-    }
-
-    if (memcmp(&peer_addr, &client_addr, peer_socklen) != 0) {
-        D("adb_socketpair: client and peer sockaddrs don't match");
-        errno = EIO;
-        goto fail;
-    }
-
     adb_close(server);
-
     sv[0] = client;
     sv[1] = accepted;
     return 0;
diff --git a/adb/test_device.py b/adb/test_device.py
index e76aaed..9e1a2ec 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -342,6 +342,13 @@
         out = self.device.shell(['echo', 'foo'])[0]
         self.assertEqual(out, 'foo' + self.device.linesep)
 
+    def test_shell_command_length(self):
+        # Devices that have shell_v2 should be able to handle long commands.
+        if self.device.has_shell_protocol():
+            rc, out, err = self.device.shell_nocheck(['echo', 'x' * 16384])
+            self.assertEqual(rc, 0)
+            self.assertTrue(out == ('x' * 16384 + '\n'))
+
     def test_shell_nocheck_failure(self):
         rc, out, _ = self.device.shell_nocheck(['false'])
         self.assertNotEqual(rc, 0)
@@ -1130,8 +1137,18 @@
             if host_dir is not None:
                 shutil.rmtree(host_dir)
 
+    def verify_sync(self, device, temp_files, device_dir):
+        """Verifies that a list of temp files was synced to the device."""
+        # Confirm that every file on the device mirrors that on the host.
+        for temp_file in temp_files:
+            device_full_path = posixpath.join(
+                device_dir, temp_file.base_name)
+            dev_md5, _ = device.shell(
+                [get_md5_prog(self.device), device_full_path])[0].split()
+            self.assertEqual(temp_file.checksum, dev_md5)
+
     def test_sync(self):
-        """Sync a randomly generated directory of files to specified device."""
+        """Sync a host directory to the data partition."""
 
         try:
             base_dir = tempfile.mkdtemp()
@@ -1141,27 +1158,50 @@
             os.makedirs(full_dir_path)
 
             # Create 32 random files within the host mirror.
-            temp_files = make_random_host_files(in_dir=full_dir_path, num_files=32)
+            temp_files = make_random_host_files(
+                in_dir=full_dir_path, num_files=32)
 
-            # Clean up any trash on the device.
-            device = adb.get_device(product=base_dir)
+            # Clean up any stale files on the device.
+            device = adb.get_device()  # pylint: disable=no-member
             device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
 
+            old_product_out = os.environ.get('ANDROID_PRODUCT_OUT')
+            os.environ['ANDROID_PRODUCT_OUT'] = base_dir
             device.sync('data')
+            if old_product_out is None:
+                del os.environ['ANDROID_PRODUCT_OUT']
+            else:
+                os.environ['ANDROID_PRODUCT_OUT'] = old_product_out
 
-            # Confirm that every file on the device mirrors that on the host.
-            for temp_file in temp_files:
-                device_full_path = posixpath.join(self.DEVICE_TEMP_DIR,
-                                                  temp_file.base_name)
-                dev_md5, _ = device.shell(
-                    [get_md5_prog(self.device), device_full_path])[0].split()
-                self.assertEqual(temp_file.checksum, dev_md5)
+            self.verify_sync(device, temp_files, self.DEVICE_TEMP_DIR)
 
-            self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+            #self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
         finally:
             if base_dir is not None:
                 shutil.rmtree(base_dir)
 
+    def test_push_sync(self):
+        """Sync a host directory to a specific path."""
+
+        try:
+            temp_dir = tempfile.mkdtemp()
+            temp_files = make_random_host_files(in_dir=temp_dir, num_files=32)
+
+            device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'sync_src_dst')
+
+            # Clean up any stale files on the device.
+            device = adb.get_device()  # pylint: disable=no-member
+            device.shell(['rm', '-rf', device_dir])
+
+            device.push(temp_dir, device_dir, sync=True)
+
+            self.verify_sync(device, temp_files, device_dir)
+
+            self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+        finally:
+            if temp_dir is not None:
+                shutil.rmtree(temp_dir)
+
     def test_unicode_paths(self):
         """Ensure that we can support non-ASCII paths, even on Windows."""
         name = u'로보카 폴리'
@@ -1188,6 +1228,97 @@
         self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
 
 
+class DeviceOfflineTest(DeviceTest):
+    def _get_device_state(self, serialno):
+        output = subprocess.check_output(self.device.adb_cmd + ['devices'])
+        for line in output.split('\n'):
+            m = re.match('(\S+)\s+(\S+)', line)
+            if m and m.group(1) == serialno:
+                return m.group(2)
+        return None
+
+    def test_killed_when_pushing_a_large_file(self):
+        """
+           While running adb push with a large file, kill adb server.
+           Occasionally the device becomes offline. Because the device is still
+           reading data without realizing that the adb server has been restarted.
+           Test if we can bring the device online automatically now.
+           http://b/32952319
+        """
+        serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
+        # 1. Push a large file
+        file_path = 'tmp_large_file'
+        try:
+            fh = open(file_path, 'w')
+            fh.write('\0' * (100 * 1024 * 1024))
+            fh.close()
+            subproc = subprocess.Popen(self.device.adb_cmd + ['push', file_path, '/data/local/tmp'])
+            time.sleep(0.1)
+            # 2. Kill the adb server
+            subprocess.check_call(self.device.adb_cmd + ['kill-server'])
+            subproc.terminate()
+        finally:
+            try:
+                os.unlink(file_path)
+            except:
+                pass
+        # 3. See if the device still exist.
+        # Sleep to wait for the adb server exit.
+        time.sleep(0.5)
+        # 4. The device should be online
+        self.assertEqual(self._get_device_state(serialno), 'device')
+
+    def test_killed_when_pulling_a_large_file(self):
+        """
+           While running adb pull with a large file, kill adb server.
+           Occasionally the device can't be connected. Because the device is trying to
+           send a message larger than what is expected by the adb server.
+           Test if we can bring the device online automatically now.
+        """
+        serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
+        file_path = 'tmp_large_file'
+        try:
+            # 1. Create a large file on device.
+            self.device.shell(['dd', 'if=/dev/zero', 'of=/data/local/tmp/tmp_large_file',
+                               'bs=1000000', 'count=100'])
+            # 2. Pull the large file on host.
+            subproc = subprocess.Popen(self.device.adb_cmd +
+                                       ['pull','/data/local/tmp/tmp_large_file', file_path])
+            time.sleep(0.1)
+            # 3. Kill the adb server
+            subprocess.check_call(self.device.adb_cmd + ['kill-server'])
+            subproc.terminate()
+        finally:
+            try:
+                os.unlink(file_path)
+            except:
+                pass
+        # 4. See if the device still exist.
+        # Sleep to wait for the adb server exit.
+        time.sleep(0.5)
+        self.assertEqual(self._get_device_state(serialno), 'device')
+
+
+    def test_packet_size_regression(self):
+        """Test for http://b/37783561
+
+        Receiving packets of a length divisible by 512 but not 1024 resulted in
+        the adb client waiting indefinitely for more input.
+        """
+        # The values that trigger things are 507 (512 - 5 bytes from shell protocol) + 1024*n
+        # Probe some surrounding values as well, for the hell of it.
+        for length in [506, 507, 508, 1018, 1019, 1020, 1530, 1531, 1532]:
+            cmd = ['dd', 'if=/dev/zero', 'bs={}'.format(length), 'count=1', '2>/dev/null;'
+                   'echo', 'foo']
+            rc, stdout, _ = self.device.shell_nocheck(cmd)
+
+            self.assertEqual(0, rc)
+
+            # Output should be '\0' * length, followed by "foo\n"
+            self.assertEqual(length, len(stdout) - 4)
+            self.assertEqual(stdout, "\0" * length + "foo\n")
+
+
 def main():
     random.seed(0)
     if len(adb.get_devices()) > 0:
diff --git a/adb/transport.cpp b/adb/transport.cpp
index c951f5b..2bbbefd 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -29,9 +29,11 @@
 #include <algorithm>
 #include <list>
 #include <mutex>
+#include <thread>
 
 #include <android-base/logging.h>
 #include <android-base/parsenetaddress.h>
+#include <android-base/quick_exit.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
@@ -40,6 +42,7 @@
 #include "adb_trace.h"
 #include "adb_utils.h"
 #include "diagnose_usb.h"
+#include "fdevent.h"
 
 static void transport_unref(atransport *t);
 
@@ -52,6 +55,7 @@
 const char* const kFeatureCmd = "cmd";
 const char* const kFeatureStat2 = "stat_v2";
 const char* const kFeatureLibusb = "libusb";
+const char* const kFeaturePushSync = "push_sync";
 
 static std::string dump_packet(const char* name, const char* func, apacket* p) {
     unsigned command = p->msg.command;
@@ -208,6 +212,11 @@
                 put_apacket(p);
                 break;
             }
+#if ADB_HOST
+            if (p->msg.command == 0) {
+                continue;
+            }
+#endif
         }
 
         D("%s: received remote packet, sending to transport", t->serial);
@@ -270,7 +279,11 @@
             if (active) {
                 D("%s: transport got packet, sending to remote", t->serial);
                 ATRACE_NAME("write_transport write_remote");
-                t->write_to_remote(p, t);
+                if (t->Write(p) != 0) {
+                    D("%s: remote write failed for transport", t->serial);
+                    put_apacket(p);
+                    break;
+                }
             } else {
                 D("%s: transport ignoring packet while offline", t->serial);
             }
@@ -388,8 +401,27 @@
     return &tracker->socket;
 }
 
+// Check if all of the USB transports are connected.
+bool iterate_transports(std::function<bool(const atransport*)> fn) {
+    std::lock_guard<std::mutex> lock(transport_lock);
+    for (const auto& t : transport_list) {
+        if (!fn(t)) {
+            return false;
+        }
+    }
+    for (const auto& t : pending_list) {
+        if (!fn(t)) {
+            return false;
+        }
+    }
+    return true;
+}
+
 // Call this function each time the transport list has changed.
 void update_transports() {
+    update_transport_status();
+
+    // Notify `adb track-devices` clients.
     std::string transports = list_transports(false);
 
     device_tracker* tracker = device_tracker_list;
@@ -492,7 +524,7 @@
     }
 
     /* don't create transport threads for inaccessible devices */
-    if (t->connection_state != kCsNoPerm) {
+    if (t->GetConnectionState() != kCsNoPerm) {
         /* initial references are the two threads */
         t->ref_count = 2;
 
@@ -509,13 +541,8 @@
 
         fdevent_set(&(t->transport_fde), FDE_READ);
 
-        if (!adb_thread_create(write_transport_thread, t)) {
-            fatal_errno("cannot create write_transport thread");
-        }
-
-        if (!adb_thread_create(read_transport_thread, t)) {
-            fatal_errno("cannot create read_transport thread");
-        }
+        std::thread(write_transport_thread, t).detach();
+        std::thread(read_transport_thread, t).detach();
     }
 
     {
@@ -544,6 +571,14 @@
     fdevent_set(&transport_registration_fde, FDE_READ);
 }
 
+void kick_all_transports() {
+    // To avoid only writing part of a packet to a transport after exit, kick all transports.
+    std::lock_guard<std::mutex> lock(transport_lock);
+    for (auto t : transport_list) {
+        t->Kick();
+    }
+}
+
 /* the fdevent select pump is single threaded */
 static void register_transport(atransport* transport) {
     tmsg m;
@@ -568,15 +603,15 @@
 static void transport_unref(atransport* t) {
     CHECK(t != nullptr);
 
-    std::lock_guard<std::mutex> lock(transport_lock);
-    CHECK_GT(t->ref_count, 0u);
-    t->ref_count--;
-    if (t->ref_count == 0) {
+    size_t old_refcount = t->ref_count--;
+    CHECK_GT(old_refcount, 0u);
+
+    if (old_refcount == 1u) {
         D("transport: %s unref (kicking and closing)", t->serial);
         t->close(t);
         remove_transport(t);
     } else {
-        D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
+        D("transport: %s unref (count=%zu)", t->serial, old_refcount - 1);
     }
 }
 
@@ -604,7 +639,7 @@
 }
 
 atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous,
-                                  std::string* error_out) {
+                                  std::string* error_out, bool accept_any_state) {
     atransport* result = nullptr;
 
     if (serial) {
@@ -619,7 +654,7 @@
 
     std::unique_lock<std::mutex> lock(transport_lock);
     for (const auto& t : transport_list) {
-        if (t->connection_state == kCsNoPerm) {
+        if (t->GetConnectionState() == kCsNoPerm) {
 #if ADB_HOST
             *error_out = UsbNoPermissionsLongHelpText();
 #endif
@@ -668,7 +703,7 @@
     lock.unlock();
 
     // Don't return unauthorized devices; the caller can't do anything with them.
-    if (result && result->connection_state == kCsUnauthorized) {
+    if (result && result->GetConnectionState() == kCsUnauthorized && !accept_any_state) {
         *error_out = "device unauthorized.\n";
         char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
         *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
@@ -680,7 +715,7 @@
     }
 
     // Don't return offline devices; the caller can't do anything with them.
-    if (result && result->connection_state == kCsOffline) {
+    if (result && result->GetConnectionState() == kCsOffline && !accept_any_state) {
         *error_out = "device offline";
         result = nullptr;
     }
@@ -692,16 +727,38 @@
     return result;
 }
 
+int atransport::Write(apacket* p) {
+#if ADB_HOST
+    std::lock_guard<std::mutex> lock(write_msg_lock_);
+#endif
+    return write_func_(p, this);
+}
+
 void atransport::Kick() {
     if (!kicked_) {
         kicked_ = true;
         CHECK(kick_func_ != nullptr);
+#if ADB_HOST
+        // On host, adb server should avoid writing part of a packet, so don't
+        // kick a transport whiling writing a packet.
+        std::lock_guard<std::mutex> lock(write_msg_lock_);
+#endif
         kick_func_(this);
     }
 }
 
+ConnectionState atransport::GetConnectionState() const {
+    return connection_state_;
+}
+
+void atransport::SetConnectionState(ConnectionState state) {
+    check_main_thread();
+    connection_state_ = state;
+}
+
 const std::string atransport::connection_state_name() const {
-    switch (connection_state) {
+    ConnectionState state = GetConnectionState();
+    switch (state) {
         case kCsOffline:
             return "offline";
         case kCsBootloader:
@@ -967,10 +1024,10 @@
 
 void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath,
                             unsigned writeable) {
-    atransport* t = new atransport();
+    atransport* t = new atransport((writeable ? kCsOffline : kCsNoPerm));
 
     D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb, serial ? serial : "");
-    init_usb_transport(t, usb, (writeable ? kCsOffline : kCsNoPerm));
+    init_usb_transport(t, usb);
     if (serial) {
         t->serial = strdup(serial);
     }
@@ -991,29 +1048,27 @@
 void unregister_usb_transport(usb_handle* usb) {
     std::lock_guard<std::mutex> lock(transport_lock);
     transport_list.remove_if(
-        [usb](atransport* t) { return t->usb == usb && t->connection_state == kCsNoPerm; });
+        [usb](atransport* t) { return t->usb == usb && t->GetConnectionState() == kCsNoPerm; });
 }
 
-int check_header(apacket* p, atransport* t) {
+bool check_header(apacket* p, atransport* t) {
     if (p->msg.magic != (p->msg.command ^ 0xffffffff)) {
-        VLOG(RWX) << "check_header(): invalid magic";
-        return -1;
+        VLOG(RWX) << "check_header(): invalid magic command = " << std::hex << p->msg.command
+                  << ", magic = " << p->msg.magic;
+        return false;
     }
 
     if (p->msg.data_length > t->get_max_payload()) {
         VLOG(RWX) << "check_header(): " << p->msg.data_length
                   << " atransport::max_payload = " << t->get_max_payload();
-        return -1;
+        return false;
     }
 
-    return 0;
+    return true;
 }
 
-int check_data(apacket* p) {
-    if (calculate_apacket_checksum(p) != p->msg.data_check) {
-        return -1;
-    }
-    return 0;
+bool check_data(apacket* p) {
+    return calculate_apacket_checksum(p) == p->msg.data_check;
 }
 
 #if ADB_HOST
@@ -1024,4 +1079,11 @@
     keys_.pop_front();
     return result;
 }
+bool atransport::SetSendConnectOnError() {
+    if (has_send_connect_on_error_) {
+        return false;
+    }
+    has_send_connect_on_error_ = true;
+    return true;
+}
 #endif
diff --git a/adb/transport.h b/adb/transport.h
index 4d97fc7..4a89ed9 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -19,10 +19,12 @@
 
 #include <sys/types.h>
 
+#include <atomic>
 #include <deque>
 #include <functional>
 #include <list>
 #include <memory>
+#include <mutex>
 #include <string>
 #include <unordered_set>
 
@@ -49,6 +51,8 @@
 extern const char* const kFeatureStat2;
 // The server is running with libusb enabled.
 extern const char* const kFeatureLibusb;
+// The server supports `push --sync`.
+extern const char* const kFeaturePushSync;
 
 class atransport {
 public:
@@ -57,31 +61,35 @@
     // class in one go is a very large change. Given how bad our testing is,
     // it's better to do this piece by piece.
 
-    atransport() {
+    atransport(ConnectionState state = kCsOffline) : ref_count(0), connection_state_(state) {
         transport_fde = {};
         protocol_version = A_VERSION;
         max_payload = MAX_PAYLOAD;
     }
-
     virtual ~atransport() {}
 
     int (*read_from_remote)(apacket* p, atransport* t) = nullptr;
-    int (*write_to_remote)(apacket* p, atransport* t) = nullptr;
     void (*close)(atransport* t) = nullptr;
+
+    void SetWriteFunction(int (*write_func)(apacket*, atransport*)) { write_func_ = write_func; }
     void SetKickFunction(void (*kick_func)(atransport*)) {
         kick_func_ = kick_func;
     }
     bool IsKicked() {
         return kicked_;
     }
+    int Write(apacket* p);
     void Kick();
 
+    // ConnectionState can be read by all threads, but can only be written in the main thread.
+    ConnectionState GetConnectionState() const;
+    void SetConnectionState(ConnectionState state);
+
     int fd = -1;
     int transport_socket = -1;
     fdevent transport_fde;
-    size_t ref_count = 0;
+    std::atomic<size_t> ref_count;
     uint32_t sync_token = 0;
-    ConnectionState connection_state = kCsOffline;
     bool online = false;
     TransportType type = kTransportAny;
 
@@ -114,11 +122,13 @@
 
 #if ADB_HOST
     std::shared_ptr<RSA> NextKey();
+    bool SetSendConnectOnError();
 #endif
 
     char token[TOKEN_SIZE] = {};
     size_t failed_auth_attempts = 0;
 
+    const std::string serial_name() const { return serial ? serial : "<unknown>"; }
     const std::string connection_state_name() const;
 
     void update_version(int version, size_t payload);
@@ -157,6 +167,7 @@
     int local_port_for_emulator_ = -1;
     bool kicked_ = false;
     void (*kick_func_)(atransport*) = nullptr;
+    int (*write_func_)(apacket*, atransport*) = nullptr;
 
     // A set of features transmitted in the banner with the initial connection.
     // This is stored in the banner as 'features=feature0,feature1,etc'.
@@ -167,8 +178,11 @@
     // A list of adisconnect callbacks called when the transport is kicked.
     std::list<adisconnect*> disconnects_;
 
+    std::atomic<ConnectionState> connection_state_;
 #if ADB_HOST
     std::deque<std::shared_ptr<RSA>> keys_;
+    std::mutex write_msg_lock_;
+    bool has_send_connect_on_error_ = false;
 #endif
 
     DISALLOW_COPY_AND_ASSIGN(atransport);
@@ -181,16 +195,21 @@
  * is set to true and nullptr returned.
  * If no suitable transport is found, error is set and nullptr returned.
  */
-atransport* acquire_one_transport(TransportType type, const char* serial,
-                                  bool* is_ambiguous, std::string* error_out);
+atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous,
+                                  std::string* error_out, bool accept_any_state = false);
 void kick_transport(atransport* t);
 void update_transports(void);
 
+// Iterates across all of the current and pending transports.
+// Stops iteration and returns false if fn returns false, otherwise returns true.
+bool iterate_transports(std::function<bool(const atransport*)> fn);
+
 void init_transport_registration(void);
 void init_mdns_transport_discovery(void);
 std::string list_transports(bool long_listing);
 atransport* find_transport(const char* serial);
 void kick_all_tcp_devices();
+void kick_all_transports();
 
 void register_usb_transport(usb_handle* h, const char* serial,
                             const char* devpath, unsigned writeable);
@@ -204,8 +223,8 @@
 // This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb);
 
-int check_header(apacket* p, atransport* t);
-int check_data(apacket* p);
+bool check_header(apacket* p, atransport* t);
+bool check_data(apacket* p);
 
 void close_usb_devices();
 void close_usb_devices(std::function<bool(const atransport*)> predicate);
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 4198a52..809ed89 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -62,22 +62,22 @@
 
 static int remote_read(apacket *p, atransport *t)
 {
-    if(!ReadFdExactly(t->sfd, &p->msg, sizeof(amessage))){
+    if (!ReadFdExactly(t->sfd, &p->msg, sizeof(amessage))) {
         D("remote local: read terminated (message)");
         return -1;
     }
 
-    if(check_header(p, t)) {
+    if (!check_header(p, t)) {
         D("bad header: terminated (data)");
         return -1;
     }
 
-    if(!ReadFdExactly(t->sfd, p->data, p->msg.data_length)){
+    if (!ReadFdExactly(t->sfd, p->data, p->msg.data_length)) {
         D("remote local: terminated (data)");
         return -1;
     }
 
-    if(check_data(p)) {
+    if (!check_data(p)) {
         D("bad data: terminated (data)");
         return -1;
     }
@@ -199,7 +199,7 @@
 std::mutex &retry_ports_lock = *new std::mutex;
 std::condition_variable &retry_ports_cond = *new std::condition_variable;
 
-static void client_socket_thread(void* x) {
+static void client_socket_thread(int) {
     adb_thread_setname("client_socket_thread");
     D("transport: client_socket_thread() starting");
     PollAllLocalPortsForEmulator();
@@ -244,9 +244,8 @@
 
 #else // ADB_HOST
 
-static void server_socket_thread(void* arg) {
+static void server_socket_thread(int port) {
     int serverfd, fd;
-    int port = (int) (uintptr_t) arg;
 
     adb_thread_setname("server socket");
     D("transport: server_socket_thread() starting");
@@ -289,7 +288,7 @@
 #define open    adb_open
 #define read    adb_read
 #define write   adb_write
-#include <system/qemu_pipe.h>
+#include <qemu_pipe.h>
 #undef open
 #undef read
 #undef write
@@ -325,7 +324,7 @@
  *   the transport registration is completed. That's why we need to send the
  *   'start' request after the transport is registered.
  */
-static void qemu_socket_thread(void* arg) {
+static void qemu_socket_thread(int port) {
     /* 'accept' request to the adb QEMUD service. */
     static const char _accept_req[] = "accept";
     /* 'start' request to the adb QEMUD service. */
@@ -333,7 +332,6 @@
     /* 'ok' reply from the adb QEMUD service. */
     static const char _ok_resp[] = "ok";
 
-    const int port = (int) (uintptr_t) arg;
     int fd;
     char tmp[256];
     char con_name[32];
@@ -350,7 +348,7 @@
         /* This could be an older version of the emulator, that doesn't
          * implement adb QEMUD service. Fall back to the old TCP way. */
         D("adb service is not available. Falling back to TCP socket.");
-        adb_thread_create(server_socket_thread, arg);
+        std::thread(server_socket_thread, port).detach();
         return;
     }
 
@@ -394,7 +392,7 @@
 
 void local_init(int port)
 {
-    adb_thread_func_t func;
+    void (*func)(int);
     const char* debug_name = "";
 
 #if ADB_HOST
@@ -414,9 +412,7 @@
 #endif // !ADB_HOST
 
     D("transport: local %s init", debug_name);
-    if (!adb_thread_create(func, (void *) (uintptr_t) port)) {
-        fatal_errno("cannot create local socket %s thread", debug_name);
-    }
+    std::thread(func, port).detach();
 }
 
 static void remote_kick(atransport *t)
@@ -519,12 +515,11 @@
     int  fail = 0;
 
     t->SetKickFunction(remote_kick);
+    t->SetWriteFunction(remote_write);
     t->close = remote_close;
     t->read_from_remote = remote_read;
-    t->write_to_remote = remote_write;
     t->sfd = s;
     t->sync_token = 1;
-    t->connection_state = kCsOffline;
     t->type = kTransportLocal;
 
 #if ADB_HOST
diff --git a/adb/transport_mdns.cpp b/adb/transport_mdns.cpp
index e49b1c6..3603f09 100644
--- a/adb/transport_mdns.cpp
+++ b/adb/transport_mdns.cpp
@@ -24,6 +24,8 @@
 #include <arpa/inet.h>
 #endif
 
+#include <thread>
+
 #include <android-base/stringprintf.h>
 #include <dns_sd.h>
 
@@ -262,19 +264,22 @@
     }
 }
 
-void init_mdns_transport_discovery(void) {
-    DNSServiceErrorType errorCode =
-        DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
-                         register_mdns_transport, nullptr);
+void init_mdns_transport_discovery_thread(void) {
+    DNSServiceErrorType errorCode = DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
+                                                     register_mdns_transport, nullptr);
 
     if (errorCode != kDNSServiceErr_NoError) {
         D("Got %d initiating mDNS browse.", errorCode);
         return;
     }
 
-    fdevent_install(&service_ref_fde,
-                    adb_DNSServiceRefSockFD(service_ref),
-                    pump_service_ref,
-                    &service_ref);
-    fdevent_set(&service_ref_fde, FDE_READ);
+    fdevent_run_on_main_thread([]() {
+        fdevent_install(&service_ref_fde, adb_DNSServiceRefSockFD(service_ref), pump_service_ref,
+                        &service_ref);
+        fdevent_set(&service_ref_fde, FDE_READ);
+    });
+}
+
+void init_mdns_transport_discovery(void) {
+    std::thread(init_mdns_transport_discovery_thread).detach();
 }
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 8b38e03..68689d4 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -94,12 +94,13 @@
 }
 
 TEST(transport, parse_banner_no_features) {
+    set_main_thread();
     atransport t;
 
     parse_banner("host::", &t);
 
     ASSERT_EQ(0U, t.features().size());
-    ASSERT_EQ(kCsHost, t.connection_state);
+    ASSERT_EQ(kCsHost, t.GetConnectionState());
 
     ASSERT_EQ(nullptr, t.product);
     ASSERT_EQ(nullptr, t.model);
@@ -113,7 +114,7 @@
         "host::ro.product.name=foo;ro.product.model=bar;ro.product.device=baz;";
     parse_banner(banner, &t);
 
-    ASSERT_EQ(kCsHost, t.connection_state);
+    ASSERT_EQ(kCsHost, t.GetConnectionState());
 
     ASSERT_EQ(0U, t.features().size());
 
@@ -130,7 +131,7 @@
         "features=woodly,doodly";
     parse_banner(banner, &t);
 
-    ASSERT_EQ(kCsHost, t.connection_state);
+    ASSERT_EQ(kCsHost, t.GetConnectionState());
 
     ASSERT_EQ(2U, t.features().size());
     ASSERT_TRUE(t.has_feature("woodly"));
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 516b4f2..7e8ae67 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -25,44 +25,137 @@
 
 #include "adb.h"
 
-static int remote_read(apacket *p, atransport *t)
-{
-    if(usb_read(t->usb, &p->msg, sizeof(amessage))){
+#if ADB_HOST
+
+// Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes
+// to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html.
+static int UsbReadMessage(usb_handle* h, amessage* msg) {
+    D("UsbReadMessage");
+
+    size_t usb_packet_size = usb_get_max_packet_size(h);
+    CHECK(usb_packet_size >= sizeof(*msg));
+    CHECK(usb_packet_size < 4096);
+
+    char buffer[4096];
+    int n = usb_read(h, buffer, usb_packet_size);
+    if (n != sizeof(*msg)) {
+        D("usb_read returned unexpected length %d (expected %zu)", n, sizeof(*msg));
+        return -1;
+    }
+    memcpy(msg, buffer, sizeof(*msg));
+    return n;
+}
+
+// Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes
+// to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html.
+static int UsbReadPayload(usb_handle* h, apacket* p) {
+    D("UsbReadPayload(%d)", p->msg.data_length);
+
+    size_t usb_packet_size = usb_get_max_packet_size(h);
+    CHECK(sizeof(p->data) % usb_packet_size == 0);
+
+    // Round the data length up to the nearest packet size boundary.
+    // The device won't send a zero packet for packet size aligned payloads,
+    // so don't read any more packets than needed.
+    size_t len = p->msg.data_length;
+    size_t rem_size = len % usb_packet_size;
+    if (rem_size) {
+        len += usb_packet_size - rem_size;
+    }
+    CHECK(len <= sizeof(p->data));
+    return usb_read(h, &p->data, len);
+}
+
+static int remote_read(apacket* p, atransport* t) {
+    int n = UsbReadMessage(t->usb, &p->msg);
+    if (n < 0) {
         D("remote usb: read terminated (message)");
         return -1;
     }
+    if (static_cast<size_t>(n) != sizeof(p->msg) || !check_header(p, t)) {
+        D("remote usb: check_header failed, skip it");
+        goto err_msg;
+    }
+    if (t->GetConnectionState() == kCsOffline) {
+        // If we read a wrong msg header declaring a large message payload, don't read its payload.
+        // Otherwise we may miss true messages from the device.
+        if (p->msg.command != A_CNXN && p->msg.command != A_AUTH) {
+            goto err_msg;
+        }
+    }
+    if (p->msg.data_length) {
+        n = UsbReadPayload(t->usb, p);
+        if (n < 0) {
+            D("remote usb: terminated (data)");
+            return -1;
+        }
+        if (static_cast<uint32_t>(n) != p->msg.data_length) {
+            D("remote usb: read payload failed (need %u bytes, give %d bytes), skip it",
+              p->msg.data_length, n);
+            goto err_msg;
+        }
+    }
+    if (!check_data(p)) {
+        D("remote usb: check_data failed, skip it");
+        goto err_msg;
+    }
+    return 0;
 
-    if(check_header(p, t)) {
-        D("remote usb: check_header failed");
+err_msg:
+    p->msg.command = 0;
+    if (t->GetConnectionState() == kCsOffline) {
+        // If the data toggle of ep_out on device and ep_in on host are not the same, we may receive
+        // an error message. In this case, resend one A_CNXN message to connect the device.
+        if (t->SetSendConnectOnError()) {
+            SendConnectOnHost(t);
+        }
+    }
+    return 0;
+}
+
+#else
+
+// On Android devices, we rely on the kernel to provide buffered read.
+// So we can recover automatically from EOVERFLOW.
+static int remote_read(apacket *p, atransport *t)
+{
+    if (usb_read(t->usb, &p->msg, sizeof(amessage))) {
+        PLOG(ERROR) << "remote usb: read terminated (message)";
         return -1;
     }
 
-    if(p->msg.data_length) {
-        if(usb_read(t->usb, p->data, p->msg.data_length)){
-            D("remote usb: terminated (data)");
+    if (!check_header(p, t)) {
+        LOG(ERROR) << "remote usb: check_header failed";
+        return -1;
+    }
+
+    if (p->msg.data_length) {
+        if (usb_read(t->usb, p->data, p->msg.data_length)) {
+            PLOG(ERROR) << "remote usb: terminated (data)";
             return -1;
         }
     }
 
-    if(check_data(p)) {
-        D("remote usb: check_data failed");
+    if (!check_data(p)) {
+        LOG(ERROR) << "remote usb: check_data failed";
         return -1;
     }
 
     return 0;
 }
+#endif
 
 static int remote_write(apacket *p, atransport *t)
 {
     unsigned size = p->msg.data_length;
 
-    if(usb_write(t->usb, &p->msg, sizeof(amessage))) {
-        D("remote usb: 1 - write terminated");
+    if (usb_write(t->usb, &p->msg, sizeof(amessage))) {
+        PLOG(ERROR) << "remote usb: 1 - write terminated";
         return -1;
     }
-    if(p->msg.data_length == 0) return 0;
-    if(usb_write(t->usb, &p->data, size)) {
-        D("remote usb: 2 - write terminated");
+    if (p->msg.data_length == 0) return 0;
+    if (usb_write(t->usb, &p->data, size)) {
+        PLOG(ERROR) << "remote usb: 2 - write terminated";
         return -1;
     }
 
@@ -75,20 +168,17 @@
     t->usb = 0;
 }
 
-static void remote_kick(atransport *t)
-{
+static void remote_kick(atransport* t) {
     usb_kick(t->usb);
 }
 
-void init_usb_transport(atransport *t, usb_handle *h, ConnectionState state)
-{
+void init_usb_transport(atransport* t, usb_handle* h) {
     D("transport: usb");
     t->close = remote_close;
     t->SetKickFunction(remote_kick);
+    t->SetWriteFunction(remote_write);
     t->read_from_remote = remote_read;
-    t->write_to_remote = remote_write;
     t->sync_token = 1;
-    t->connection_state = state;
     t->type = kTransportUsb;
     t->usb = h;
 }
@@ -102,7 +192,7 @@
 #if defined(_WIN32) || !ADB_HOST
     return false;
 #else
-    static bool disable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "0") == 0;
-    return !disable;
+    static bool enable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "1") == 0;
+    return enable;
 #endif
 }
diff --git a/adb/usb.h b/adb/usb.h
index ba70de4..f428ede 100644
--- a/adb/usb.h
+++ b/adb/usb.h
@@ -16,14 +16,18 @@
 
 #pragma once
 
+#include <sys/types.h>
+
 // USB host/client interface.
 
 #define ADB_USB_INTERFACE(handle_ref_type)                       \
     void usb_init();                                             \
+    void usb_cleanup();                                          \
     int usb_write(handle_ref_type h, const void* data, int len); \
     int usb_read(handle_ref_type h, void* data, int len);        \
     int usb_close(handle_ref_type h);                            \
-    void usb_kick(handle_ref_type h)
+    void usb_kick(handle_ref_type h);                            \
+    size_t usb_get_max_packet_size(handle_ref_type)
 
 #if defined(_WIN32) || !ADB_HOST
 // Windows and the daemon have a single implementation.
diff --git a/base/Android.bp b/base/Android.bp
index a8dda5d..b636dc3 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -26,9 +26,6 @@
     host_supported: true,
     export_include_dirs: ["include"],
 
-    header_libs: ["libutils_headers"],
-    export_header_lib_headers: ["libutils_headers"],
-
     target: {
         linux_bionic: {
             enabled: true,
@@ -54,7 +51,9 @@
         "test_utils.cpp",
     ],
 
-    header_libs: ["libbase_headers"],
+    header_libs: [
+        "libbase_headers",
+    ],
     export_header_lib_headers: ["libbase_headers"],
 
     cppflags: libbase_cppflags,
@@ -64,24 +63,36 @@
             srcs: [
                 "errors_unix.cpp",
                 "properties.cpp",
+                "chrono_utils.cpp",
             ],
             cppflags: ["-Wexit-time-destructors"],
             sanitize: {
                 misc_undefined: ["integer"],
             },
+
         },
         darwin: {
-            srcs: ["errors_unix.cpp"],
+            srcs: [
+                "chrono_utils.cpp",
+                "errors_unix.cpp",
+            ],
             cppflags: ["-Wexit-time-destructors"],
         },
         linux_bionic: {
-            srcs: ["errors_unix.cpp"],
+            srcs: [
+                "chrono_utils.cpp",
+                "errors_unix.cpp",
+            ],
             cppflags: ["-Wexit-time-destructors"],
             enabled: true,
         },
         linux: {
-            srcs: ["errors_unix.cpp"],
+            srcs: [
+                "chrono_utils.cpp",
+                "errors_unix.cpp",
+            ],
             cppflags: ["-Wexit-time-destructors"],
+            host_ldlibs: ["-lrt"],
         },
         windows: {
             srcs: [
@@ -108,17 +119,25 @@
         "parseint_test.cpp",
         "parsenetaddress_test.cpp",
         "quick_exit_test.cpp",
+        "scopeguard_test.cpp",
         "stringprintf_test.cpp",
         "strings_test.cpp",
         "test_main.cpp",
     ],
     target: {
         android: {
-            srcs: ["properties_test.cpp"],
+            srcs: [
+                "chrono_utils_test.cpp",
+                "properties_test.cpp"
+            ],
             sanitize: {
                 misc_undefined: ["integer"],
             },
         },
+        linux: {
+            srcs: ["chrono_utils_test.cpp"],
+            host_ldlibs: ["-lrt"],
+        },
         windows: {
             srcs: ["utf8_test.cpp"],
             enabled: true,
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp
new file mode 100644
index 0000000..5eedf3b
--- /dev/null
+++ b/base/chrono_utils.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 "android-base/chrono_utils.h"
+
+#include <time.h>
+
+namespace android {
+namespace base {
+
+boot_clock::time_point boot_clock::now() {
+#ifdef __ANDROID__
+  timespec ts;
+  clock_gettime(CLOCK_BOOTTIME, &ts);
+  return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
+                                std::chrono::nanoseconds(ts.tv_nsec));
+#else
+  // Darwin does not support clock_gettime.
+  return boot_clock::time_point();
+#endif  // __ANDROID__
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/chrono_utils_test.cpp b/base/chrono_utils_test.cpp
new file mode 100644
index 0000000..057132d
--- /dev/null
+++ b/base/chrono_utils_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "android-base/chrono_utils.h"
+
+#include <time.h>
+
+#include <chrono>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+std::chrono::seconds GetBootTimeSeconds() {
+  struct timespec now;
+  clock_gettime(CLOCK_BOOTTIME, &now);
+
+  auto now_tp = boot_clock::time_point(std::chrono::seconds(now.tv_sec) +
+                                       std::chrono::nanoseconds(now.tv_nsec));
+  return std::chrono::duration_cast<std::chrono::seconds>(now_tp.time_since_epoch());
+}
+
+// Tests (at least) the seconds accuracy of the boot_clock::now() method.
+TEST(ChronoUtilsTest, BootClockNowSeconds) {
+  auto now = GetBootTimeSeconds();
+  auto boot_seconds =
+      std::chrono::duration_cast<std::chrono::seconds>(boot_clock::now().time_since_epoch());
+  EXPECT_EQ(now, boot_seconds);
+}
+
+}  // namespace base
+}  // namespace android
\ No newline at end of file
diff --git a/base/file.cpp b/base/file.cpp
index d4e5894..a2f2887 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -28,16 +28,18 @@
 #include <string>
 #include <vector>
 
-#include "android-base/macros.h"  // For TEMP_FAILURE_RETRY on Darwin.
 #include "android-base/logging.h"
+#include "android-base/macros.h"  // For TEMP_FAILURE_RETRY on Darwin.
+#include "android-base/unique_fd.h"
 #include "android-base/utf8.h"
-#include "utils/Compat.h"
 
 #if defined(__APPLE__)
 #include <mach-o/dyld.h>
 #endif
 #if defined(_WIN32)
 #include <windows.h>
+#define O_CLOEXEC O_NOINHERIT
+#define O_NOFOLLOW 0
 #endif
 
 namespace android {
@@ -69,13 +71,11 @@
   content->clear();
 
   int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW);
-  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags));
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
   if (fd == -1) {
     return false;
   }
-  bool result = ReadFdToString(fd, content);
-  close(fd);
-  return result;
+  return ReadFdToString(fd, content);
 }
 
 bool WriteStringToFd(const std::string& content, int fd) {
@@ -106,7 +106,7 @@
                        bool follow_symlinks) {
   int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
               (follow_symlinks ? 0 : O_NOFOLLOW);
-  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
   if (fd == -1) {
     PLOG(ERROR) << "android::WriteStringToFile open failed";
     return false;
@@ -126,7 +126,6 @@
     PLOG(ERROR) << "android::WriteStringToFile write failed";
     return CleanUpAfterFailedWrite(path);
   }
-  close(fd);
   return true;
 }
 #endif
@@ -135,14 +134,11 @@
                        bool follow_symlinks) {
   int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
               (follow_symlinks ? 0 : O_NOFOLLOW);
-  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE));
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, 0666)));
   if (fd == -1) {
     return false;
   }
-
-  bool result = WriteStringToFd(content, fd);
-  close(fd);
-  return result || CleanUpAfterFailedWrite(path);
+  return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
 }
 
 bool ReadFully(int fd, void* data, size_t byte_count) {
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
new file mode 100644
index 0000000..0086425
--- /dev/null
+++ b/base/include/android-base/chrono_utils.h
@@ -0,0 +1,37 @@
+/*
+ * 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 ANDROID_BASE_CHRONO_UTILS_H
+#define ANDROID_BASE_CHRONO_UTILS_H
+
+#include <chrono>
+
+namespace android {
+namespace base {
+
+// A std::chrono clock based on CLOCK_BOOTTIME.
+class boot_clock {
+ public:
+  typedef std::chrono::nanoseconds duration;
+  typedef std::chrono::time_point<boot_clock, duration> time_point;
+
+  static time_point now();
+};
+
+}  // namespace base
+}  // namespace android
+
+#endif  // ANDROID_BASE_CHRONO_UTILS_H
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index e78edbb..548b286 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -164,9 +164,31 @@
   using ::android::base::FATAL;               \
   return (severity); }())
 
+#ifdef __clang_analyzer__
+// Clang's static analyzer does not see the conditional statement inside
+// LogMessage's destructor that will abort on FATAL severity.
+#define ABORT_AFTER_LOG_FATAL for (;; abort())
+
+struct LogAbortAfterFullExpr {
+  ~LogAbortAfterFullExpr() __attribute__((noreturn)) { abort(); }
+  explicit operator bool() const { return false; }
+};
+// Provides an expression that evaluates to the truthiness of `x`, automatically
+// aborting if `c` is true.
+#define ABORT_AFTER_LOG_EXPR_IF(c, x) (((c) && ::android::base::LogAbortAfterFullExpr()) || (x))
+// Note to the static analyzer that we always execute FATAL logs in practice.
+#define MUST_LOG_MESSAGE(severity) (SEVERITY_LAMBDA(severity) == ::android::base::FATAL)
+#else
+#define ABORT_AFTER_LOG_FATAL
+#define ABORT_AFTER_LOG_EXPR_IF(c, x) (x)
+#define MUST_LOG_MESSAGE(severity) false
+#endif
+#define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x)
+
 // Defines whether the given severity will be logged or silently swallowed.
 #define WOULD_LOG(severity) \
-  UNLIKELY((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity())
+  (UNLIKELY((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity()) || \
+   MUST_LOG_MESSAGE(severity))
 
 // Get an ostream that can be used for logging at the given severity and to the default
 // destination.
@@ -190,54 +212,47 @@
 //     LOG(FATAL) << "We didn't expect to reach here";
 #define LOG(severity) LOG_TO(DEFAULT, severity)
 
+// Checks if we want to log something, and sets up appropriate RAII objects if
+// so.
+// Note: DO NOT USE DIRECTLY. This is an implementation detail.
+#define LOGGING_PREAMBLE(severity)                                                         \
+  (WOULD_LOG(severity) &&                                                                  \
+   ABORT_AFTER_LOG_EXPR_IF((SEVERITY_LAMBDA(severity)) == ::android::base::FATAL, true) && \
+   ::android::base::ErrnoRestorer())
+
 // Logs a message to logcat with the specified log ID on Android otherwise to
 // stderr. If the severity is FATAL it also causes an abort.
-// Use an if-else statement instead of just an if statement here. So if there is a
-// else statement after LOG() macro, it won't bind to the if statement in the macro.
-// do-while(0) statement doesn't work here. Because we need to support << operator
-// following the macro, like "LOG(DEBUG) << xxx;".
-
-#define LOG_TO(dest, severity) \
-  WOULD_LOG(severity) &&                   \
-    ::android::base::ErrnoRestorer() &&    \
-      LOG_STREAM_TO(dest, severity)
+// Use an expression here so we can support the << operator following the macro,
+// like "LOG(DEBUG) << xxx;".
+#define LOG_TO(dest, severity) LOGGING_PREAMBLE(severity) && LOG_STREAM_TO(dest, severity)
 
 // A variant of LOG that also logs the current errno value. To be used when
 // library calls fail.
 #define PLOG(severity) PLOG_TO(DEFAULT, severity)
 
 // Behaves like PLOG, but logs to the specified log ID.
-#define PLOG_TO(dest, severity)                         \
-  WOULD_LOG(SEVERITY_LAMBDA(severity)) &&               \
-    ::android::base::ErrnoRestorer() &&                 \
-      ::android::base::LogMessage(__FILE__, __LINE__,   \
-          ::android::base::dest,                        \
-          SEVERITY_LAMBDA(severity), errno).stream()
+#define PLOG_TO(dest, severity)                                              \
+  LOGGING_PREAMBLE(severity) &&                                              \
+      ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \
+                                  SEVERITY_LAMBDA(severity), errno)          \
+          .stream()
 
 // Marker that code is yet to be implemented.
 #define UNIMPLEMENTED(level) \
   LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
 
-#ifdef __clang_analyzer__
-// ClangL static analyzer does not see the conditional statement inside
-// LogMessage's destructor that will abort on FATAL severity.
-#define ABORT_AFTER_LOG_FATAL for (;;abort())
-#else
-#define ABORT_AFTER_LOG_FATAL
-#endif
-
 // Check whether condition x holds and LOG(FATAL) if not. The value of the
 // expression x is only evaluated once. Extra logging can be appended using <<
 // after. For example:
 //
 //     CHECK(false == true) results in a log message of
 //       "Check failed: false == true".
-#define CHECK(x)                                                              \
-  LIKELY((x)) ||                                                              \
-    ABORT_AFTER_LOG_FATAL                                                     \
-    ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
-                                ::android::base::FATAL, -1).stream()          \
-        << "Check failed: " #x << " "
+#define CHECK(x)                                                                \
+  LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) ||                           \
+      ::android::base::LogMessage(                                              \
+          __FILE__, __LINE__, ::android::base::DEFAULT, ::android::base::FATAL, \
+          -1).stream()                                                          \
+          << "Check failed: " #x << " "
 
 // Helper for CHECK_xx(x,y) macros.
 #define CHECK_OP(LHS, RHS, OP)                                              \
@@ -304,7 +319,7 @@
 // DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
 // CHECK should be used unless profiling identifies a CHECK as being in
 // performance critical code.
-#if defined(NDEBUG)
+#if defined(NDEBUG) && !defined(__clang_analyzer__)
 static constexpr bool kEnableDChecks = false;
 #else
 static constexpr bool kEnableDChecks = true;
@@ -328,7 +343,7 @@
   if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
 #define DCHECK_STRNE(s1, s2) \
   if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
-#if defined(NDEBUG)
+#if defined(NDEBUG) && !defined(__clang_analyzer__)
 #define DCHECK_CONSTEXPR(x, out, dummy)
 #else
 #define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 4de5e57..041586c 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -62,15 +62,14 @@
 // Waits for the system property `key` to have the value `expected_value`.
 // Times out after `relative_timeout`.
 // Returns true on success, false on timeout.
-bool WaitForProperty(const std::string& key,
-                     const std::string& expected_value,
-                     std::chrono::milliseconds relative_timeout);
+bool WaitForProperty(const std::string& key, const std::string& expected_value,
+                     std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
 
 // Waits for the system property `key` to be created.
 // Times out after `relative_timeout`.
 // Returns true on success, false on timeout.
-bool WaitForPropertyCreation(const std::string& key,
-                             std::chrono::milliseconds relative_timeout);
+bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
+                                                         std::chrono::milliseconds::max());
 
 } // namespace base
 } // namespace android
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
new file mode 100644
index 0000000..abcf4bc
--- /dev/null
+++ b/base/include/android-base/scopeguard.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 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 ANDROID_BASE_SCOPEGUARD_H
+#define ANDROID_BASE_SCOPEGUARD_H
+
+#include <utility>  // for std::move
+
+namespace android {
+namespace base {
+
+template <typename F>
+class ScopeGuard {
+ public:
+  ScopeGuard(F f) : f_(f), active_(true) {}
+
+  ScopeGuard(ScopeGuard&& that) : f_(std::move(that.f_)), active_(that.active_) {
+    that.active_ = false;
+  }
+
+  ~ScopeGuard() {
+    if (active_) f_();
+  }
+
+  ScopeGuard() = delete;
+  ScopeGuard(const ScopeGuard&) = delete;
+  void operator=(const ScopeGuard&) = delete;
+  void operator=(ScopeGuard&& that) = delete;
+
+  void Disable() { active_ = false; }
+
+  bool active() const { return active_; }
+
+ private:
+  F f_;
+  bool active_;
+};
+
+template <typename T>
+ScopeGuard<T> make_scope_guard(T f) {
+  return ScopeGuard<T>(f);
+}
+
+}  // namespace base
+}  // namespace android
+
+#endif  // ANDROID_BASE_SCOPEGUARD_H
diff --git a/base/properties.cpp b/base/properties.cpp
index 32c0128..816bca0 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -101,22 +101,24 @@
 }
 
 // TODO: chrono_utils?
-static void DurationToTimeSpec(timespec& ts, std::chrono::nanoseconds d) {
+static void DurationToTimeSpec(timespec& ts, const std::chrono::milliseconds d) {
   auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
   auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
   ts.tv_sec = s.count();
   ts.tv_nsec = ns.count();
 }
 
+// TODO: boot_clock?
 using AbsTime = std::chrono::time_point<std::chrono::steady_clock>;
 
-static void UpdateTimeSpec(timespec& ts,
-                           const AbsTime& timeout) {
+static void UpdateTimeSpec(timespec& ts, std::chrono::milliseconds relative_timeout,
+                           const AbsTime& start_time) {
   auto now = std::chrono::steady_clock::now();
-  auto remaining_timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout - now);
-  if (remaining_timeout < 0ns) {
+  auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+  if (time_elapsed >= relative_timeout) {
     ts = { 0, 0 };
   } else {
+    auto remaining_timeout = relative_timeout - time_elapsed;
     DurationToTimeSpec(ts, remaining_timeout);
   }
 }
@@ -127,11 +129,7 @@
 // Returns nullptr on timeout.
 static const prop_info* WaitForPropertyCreation(const std::string& key,
                                                 const std::chrono::milliseconds& relative_timeout,
-                                                AbsTime& absolute_timeout) {
-  // TODO: boot_clock?
-  auto now = std::chrono::steady_clock::now();
-  absolute_timeout = now + relative_timeout;
-
+                                                const AbsTime& start_time) {
   // Find the property's prop_info*.
   const prop_info* pi;
   unsigned global_serial = 0;
@@ -139,17 +137,16 @@
     // The property doesn't even exist yet.
     // Wait for a global change and then look again.
     timespec ts;
-    UpdateTimeSpec(ts, absolute_timeout);
+    UpdateTimeSpec(ts, relative_timeout, start_time);
     if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return nullptr;
   }
   return pi;
 }
 
-bool WaitForProperty(const std::string& key,
-                     const std::string& expected_value,
+bool WaitForProperty(const std::string& key, const std::string& expected_value,
                      std::chrono::milliseconds relative_timeout) {
-  AbsTime absolute_timeout;
-  const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, absolute_timeout);
+  auto start_time = std::chrono::steady_clock::now();
+  const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, start_time);
   if (pi == nullptr) return false;
 
   WaitForPropertyData data;
@@ -162,7 +159,7 @@
     if (data.done) return true;
 
     // It didn't, so wait for the property to change before checking again.
-    UpdateTimeSpec(ts, absolute_timeout);
+    UpdateTimeSpec(ts, relative_timeout, start_time);
     uint32_t unused;
     if (!__system_property_wait(pi, data.last_read_serial, &unused, &ts)) return false;
   }
@@ -170,8 +167,8 @@
 
 bool WaitForPropertyCreation(const std::string& key,
                              std::chrono::milliseconds relative_timeout) {
-  AbsTime absolute_timeout;
-  return (WaitForPropertyCreation(key, relative_timeout, absolute_timeout) != nullptr);
+  auto start_time = std::chrono::steady_clock::now();
+  return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
 }
 
 }  // namespace base
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index 1bbe482..de5f3dc 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -151,6 +151,38 @@
   ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
 }
 
+TEST(properties, WaitForProperty_MaxTimeout) {
+  std::atomic<bool> flag{false};
+  std::thread thread([&]() {
+    android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+    while (!flag) std::this_thread::yield();
+    std::this_thread::sleep_for(500ms);
+    android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+  });
+
+  ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+  flag = true;
+  // Test that this does not immediately return false due to overflow issues with the timeout.
+  ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b"));
+  thread.join();
+}
+
+TEST(properties, WaitForProperty_NegativeTimeout) {
+  std::atomic<bool> flag{false};
+  std::thread thread([&]() {
+    android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+    while (!flag) std::this_thread::yield();
+    std::this_thread::sleep_for(500ms);
+    android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+  });
+
+  ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+  flag = true;
+  // Assert that this immediately returns with a negative timeout
+  ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
+  thread.join();
+}
+
 TEST(properties, WaitForPropertyCreation) {
   std::thread thread([&]() {
     std::this_thread::sleep_for(100ms);
diff --git a/base/scopeguard_test.cpp b/base/scopeguard_test.cpp
new file mode 100644
index 0000000..e11154a
--- /dev/null
+++ b/base/scopeguard_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "android-base/scopeguard.h"
+
+#include <utility>
+
+#include <gtest/gtest.h>
+
+TEST(scopeguard, normal) {
+  bool guarded_var = true;
+  {
+    auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+  }
+  ASSERT_FALSE(guarded_var);
+}
+
+TEST(scopeguard, disabled) {
+  bool guarded_var = true;
+  {
+    auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+    scopeguard.Disable();
+  }
+  ASSERT_TRUE(guarded_var);
+}
+
+TEST(scopeguard, moved) {
+  int guarded_var = true;
+  auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+  { decltype(scopeguard) new_guard(std::move(scopeguard)); }
+  EXPECT_FALSE(scopeguard.active());
+  ASSERT_FALSE(guarded_var);
+}
diff --git a/base/utf8.cpp b/base/utf8.cpp
old mode 100755
new mode 100644
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
old mode 100755
new mode 100644
diff --git a/bootstat/.clang-format b/bootstat/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/bootstat/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index f744ad1..bc90a6e 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -16,7 +16,6 @@
 
 bootstat_lib_src_files = [
     "boot_event_record_store.cpp",
-    "uptime_parser.cpp",
 ]
 
 cc_defaults {
@@ -75,6 +74,7 @@
 // -----------------------------------------------------------------------------
 cc_test {
     name: "bootstat_tests",
+    test_suites: ["device-tests"],
     defaults: ["bootstat_defaults"],
     host_supported: true,
     static_libs: [
diff --git a/bootstat/AndroidTest.xml b/bootstat/AndroidTest.xml
new file mode 100644
index 0000000..f3783fa
--- /dev/null
+++ b/bootstat/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for bootstat_tests">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="bootstat_tests->/data/local/tmp/bootstat_tests" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="bootstat_tests" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 2648594..99d9405 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -20,13 +20,16 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <utime.h>
+
+#include <chrono>
 #include <cstdlib>
 #include <string>
 #include <utility>
+
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
-#include "uptime_parser.h"
 
 namespace {
 
@@ -55,7 +58,9 @@
 }
 
 void BootEventRecordStore::AddBootEvent(const std::string& event) {
-  AddBootEventWithValue(event, bootstat::ParseUptime());
+    auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
+        android::base::boot_clock::now().time_since_epoch());
+    AddBootEventWithValue(event, uptime.count());
 }
 
 // The implementation of AddBootEventValue makes use of the mtime file
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 90f6513..d98169b 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -21,15 +21,18 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <unistd.h>
+
+#include <chrono>
 #include <cstdint>
 #include <cstdlib>
+
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
 #include <gmock/gmock.h>
-#include "uptime_parser.h"
+#include <gtest/gtest.h>
 
 using testing::UnorderedElementsAreArray;
 
@@ -89,6 +92,13 @@
   rmdir(path.c_str());
 }
 
+// Returns the time in seconds since boot.
+time_t GetUptimeSeconds() {
+    return std::chrono::duration_cast<std::chrono::seconds>(
+               android::base::boot_clock::now().time_since_epoch())
+        .count();
+}
+
 class BootEventRecordStoreTest : public ::testing::Test {
  public:
   BootEventRecordStoreTest() {
@@ -126,7 +136,7 @@
   BootEventRecordStore store;
   store.SetStorePath(GetStorePathForTesting());
 
-  time_t uptime = bootstat::ParseUptime();
+  time_t uptime = GetUptimeSeconds();
   ASSERT_NE(-1, uptime);
 
   store.AddBootEvent("cenozoic");
@@ -141,7 +151,7 @@
   BootEventRecordStore store;
   store.SetStorePath(GetStorePathForTesting());
 
-  time_t uptime = bootstat::ParseUptime();
+  time_t uptime = GetUptimeSeconds();
   ASSERT_NE(-1, uptime);
 
   store.AddBootEvent("cretaceous");
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a4cc5f2..a4c2160 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -21,6 +21,7 @@
 #include <getopt.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <cmath>
 #include <cstddef>
 #include <cstdio>
@@ -30,15 +31,15 @@
 #include <string>
 #include <vector>
 
-#include <android/log.h>
+#include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
+#include <android/log.h>
 #include <cutils/properties.h>
 #include <metricslogger/metrics_logger.h>
 
 #include "boot_event_record_store.h"
-#include "uptime_parser.h"
 
 namespace {
 
@@ -200,8 +201,10 @@
 
   BootEventRecordStore boot_event_store;
   BootEventRecordStore::BootEventRecord record;
-  if (!boot_event_store.GetBootEvent(kBuildDateKey, &record) ||
-      build_date != record.second) {
+  if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) {
+    boot_complete_prefix = "factory_reset_" + boot_complete_prefix;
+    boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
+  } else if (build_date != record.second) {
     boot_complete_prefix = "ota_" + boot_complete_prefix;
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
   }
@@ -220,42 +223,78 @@
   }
 }
 
-// Parses and records the set of bootloader stages and associated boot times
-// from the ro.boot.boottime system property.
-void RecordBootloaderTimings(BootEventRecordStore* boot_event_store) {
-  // |ro.boot.boottime| is of the form 'stage1:time1,...,stageN:timeN'.
+// A map from bootloader timing stage to the time that stage took during boot.
+typedef std::map<std::string, int32_t> BootloaderTimingMap;
+
+// Returns a mapping from bootloader stage names to the time those stages
+// took to boot.
+const BootloaderTimingMap GetBootLoaderTimings() {
+  BootloaderTimingMap timings;
+
+  // |ro.boot.boottime| is of the form 'stage1:time1,...,stageN:timeN',
+  // where timeN is in milliseconds.
   std::string value = GetProperty("ro.boot.boottime");
   if (value.empty()) {
     // ro.boot.boottime is not reported on all devices.
-    return;
+    return BootloaderTimingMap();
   }
 
-  int32_t total_time = 0;
   auto stages = android::base::Split(value, ",");
-  for (auto const &stageTiming : stages) {
+  for (const auto& stageTiming : stages) {
     // |stageTiming| is of the form 'stage:time'.
     auto stageTimingValues = android::base::Split(stageTiming, ":");
-    DCHECK_EQ(2, stageTimingValues.size());
+    DCHECK_EQ(2U, stageTimingValues.size());
 
     std::string stageName = stageTimingValues[0];
     int32_t time_ms;
     if (android::base::ParseInt(stageTimingValues[1], &time_ms)) {
-      total_time += time_ms;
-      boot_event_store->AddBootEventWithValue(
-          "boottime.bootloader." + stageName, time_ms);
+      timings[stageName] = time_ms;
     }
   }
 
+  return timings;
+}
+
+// Parses and records the set of bootloader stages and associated boot times
+// from the ro.boot.boottime system property.
+void RecordBootloaderTimings(BootEventRecordStore* boot_event_store,
+                             const BootloaderTimingMap& bootloader_timings) {
+  int32_t total_time = 0;
+  for (const auto& timing : bootloader_timings) {
+    total_time += timing.second;
+    boot_event_store->AddBootEventWithValue("boottime.bootloader." + timing.first, timing.second);
+  }
+
   boot_event_store->AddBootEventWithValue("boottime.bootloader.total", total_time);
 }
 
+// Records the closest estimation to the absolute device boot time, i.e.,
+// from power on to boot_complete, including bootloader times.
+void RecordAbsoluteBootTime(BootEventRecordStore* boot_event_store,
+                            const BootloaderTimingMap& bootloader_timings,
+                            std::chrono::milliseconds uptime) {
+  int32_t bootloader_time_ms = 0;
+
+  for (const auto& timing : bootloader_timings) {
+    if (timing.first.compare("SW") != 0) {
+      bootloader_time_ms += timing.second;
+    }
+  }
+
+  auto bootloader_duration = std::chrono::milliseconds(bootloader_time_ms);
+  auto absolute_total =
+      std::chrono::duration_cast<std::chrono::seconds>(bootloader_duration + uptime);
+  boot_event_store->AddBootEventWithValue("absolute_boot_time", absolute_total.count());
+}
+
 // Records several metrics related to the time it takes to boot the device,
 // including disambiguating boot time on encrypted or non-encrypted devices.
 void RecordBootComplete() {
   BootEventRecordStore boot_event_store;
   BootEventRecordStore::BootEventRecord record;
 
-  time_t uptime = bootstat::ParseUptime();
+  auto time_since_epoch = android::base::boot_clock::now().time_since_epoch();
+  auto uptime = std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch);
   time_t current_time_utc = time(nullptr);
 
   if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
@@ -282,28 +321,30 @@
     // Log the amount of time elapsed until the device is decrypted, which
     // includes the variable amount of time the user takes to enter the
     // decryption password.
-    boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime);
+    boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime.count());
 
     // Subtract the decryption time to normalize the boot cycle timing.
-    time_t boot_complete = uptime - record.second;
+    std::chrono::seconds boot_complete = std::chrono::seconds(uptime.count() - record.second);
     boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt",
-                                           boot_complete);
-
-
+                                           boot_complete.count());
   } else {
-    boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
-                                           uptime);
+      boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
+                                             uptime.count());
   }
 
   // Record the total time from device startup to boot complete, regardless of
   // encryption state.
-  boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime);
+  boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime.count());
 
   RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init");
   RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.selinux");
   RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.cold_boot_wait");
 
-  RecordBootloaderTimings(&boot_event_store);
+  const BootloaderTimingMap bootloader_timings = GetBootLoaderTimings();
+  RecordBootloaderTimings(&boot_event_store, bootloader_timings);
+
+  auto uptime_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_since_epoch);
+  RecordAbsoluteBootTime(&boot_event_store, bootloader_timings, uptime_ms);
 }
 
 // Records the boot_reason metric by querying the ro.boot.bootreason system
diff --git a/bootstat/uptime_parser.cpp b/bootstat/uptime_parser.cpp
deleted file mode 100644
index 7c2034c..0000000
--- a/bootstat/uptime_parser.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "uptime_parser.h"
-
-#include <time.h>
-#include <cstdlib>
-#include <string>
-#include <android-base/file.h>
-#include <android-base/logging.h>
-
-namespace bootstat {
-
-time_t ParseUptime() {
-  std::string uptime_str;
-  if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
-    PLOG(ERROR) << "Failed to read /proc/uptime";
-    return -1;
-  }
-
-  // Cast intentionally rounds down.
-  return static_cast<time_t>(strtod(uptime_str.c_str(), NULL));
-}
-
-}  // namespace bootstat
\ No newline at end of file
diff --git a/bootstat/uptime_parser.h b/bootstat/uptime_parser.h
deleted file mode 100644
index 756ae9b..0000000
--- a/bootstat/uptime_parser.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef UPTIME_PARSER_H_
-#define UPTIME_PARSER_H_
-
-#include <time.h>
-
-namespace bootstat {
-
-// Returns the number of seconds the system has been on since reboot.
-time_t ParseUptime();
-
-}  // namespace bootstat
-
-#endif  // UPTIME_PARSER_H_
\ No newline at end of file
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 4783d6e..5565cfd 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -1,6 +1,5 @@
 cc_defaults {
     name: "debuggerd_defaults",
-    defaults: ["linux_bionic_supported"],
     cflags: [
         "-Wall",
         "-Wextra",
@@ -9,27 +8,56 @@
         "-Os",
     ],
 
-    // util.cpp gets async signal safe logging via libc_logging,
-    // which defines its interface in bionic private headers.
-    include_dirs: ["bionic/libc"],
-
     local_include_dirs: ["include"],
 }
 
+cc_library_headers {
+    name: "libdebuggerd_common_headers",
+    export_include_dirs: ["common/include"]
+}
+
+cc_library_shared {
+    name: "libtombstoned_client",
+    defaults: ["debuggerd_defaults"],
+    srcs: [
+        "tombstoned/tombstoned_client.cpp",
+        "util.cpp",
+    ],
+
+    header_libs: ["libdebuggerd_common_headers"],
+
+    static_libs: [
+        "libasync_safe",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+    ],
+
+    export_header_lib_headers: ["libdebuggerd_common_headers"],
+    export_include_dirs: ["tombstoned/include"]
+}
+
 // Utility library to tombstoned and get an output fd.
 cc_library_static {
-    name: "libtombstoned_client",
+    name: "libtombstoned_client_static",
     defaults: ["debuggerd_defaults"],
     srcs: [
-        "tombstoned_client.cpp",
+        "tombstoned/tombstoned_client.cpp",
         "util.cpp",
     ],
 
+    header_libs: ["libdebuggerd_common_headers"],
+
     whole_static_libs: [
-        "libc_logging",
+        "libasync_safe",
         "libcutils",
         "libbase",
     ],
+
+    export_header_lib_headers: ["libdebuggerd_common_headers"],
+    export_include_dirs: ["tombstoned/include"]
 }
 
 // Core implementation, linked into libdebuggerd_handler and the dynamic linker.
@@ -38,11 +66,14 @@
     defaults: ["debuggerd_defaults"],
     srcs: ["handler/debuggerd_handler.cpp"],
 
+    header_libs: ["libdebuggerd_common_headers"],
+
     whole_static_libs: [
-        "libc_logging",
+        "libasync_safe",
         "libdebuggerd",
     ],
 
+    export_header_lib_headers: ["libdebuggerd_common_headers"],
     export_include_dirs: ["include"],
 }
 
@@ -69,7 +100,8 @@
 
     whole_static_libs: [
         "libdebuggerd_handler_core",
-        "libtombstoned_client",
+        "libtombstoned_client_static",
+        "libasync_safe",
         "libbase",
         "libdebuggerd",
         "libbacktrace",
@@ -89,11 +121,14 @@
         "util.cpp",
     ],
 
+    header_libs: ["libdebuggerd_common_headers"],
+
     shared_libs: [
         "libbase",
         "libcutils",
     ],
 
+    export_header_lib_headers: ["libdebuggerd_common_headers"],
     export_include_dirs: ["include"],
 }
 
@@ -163,9 +198,8 @@
             srcs: [
                 "client/debuggerd_client_test.cpp",
                 "debuggerd_test.cpp",
-                "tombstoned_client.cpp",
-                "util.cpp"
             ],
+            static_libs: ["libasync_safe", "libtombstoned_client_static"],
         },
     },
 
@@ -174,11 +208,12 @@
         "libbase",
         "libcutils",
         "libdebuggerd_client",
+        "liblog",
+        "libnativehelper"
     ],
 
     static_libs: [
         "libdebuggerd",
-        "libc_logging",
     ],
 
     local_include_dirs: [
@@ -215,7 +250,7 @@
     },
 
     static_libs: [
-        "libtombstoned_client",
+        "libtombstoned_client_static",
         "libdebuggerd",
         "libcutils",
     ],
@@ -225,7 +260,6 @@
         "libbase",
         "liblog",
         "libprocinfo",
-        "libselinux",
     ],
 }
 
@@ -255,6 +289,8 @@
     ],
     defaults: ["debuggerd_defaults"],
 
+    header_libs: ["libdebuggerd_common_headers"],
+
     static_libs: [
         "libbase",
         "libcutils",
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index d1df088..cb7cbbe 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -33,18 +33,21 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
-#include <debuggerd/handler.h>
-#include <debuggerd/protocol.h>
-#include <debuggerd/util.h>
+
+#include "debuggerd/handler.h"
+#include "protocol.h"
+#include "util.h"
 
 using namespace std::chrono_literals;
 
 using android::base::unique_fd;
 
-static bool send_signal(pid_t pid, bool backtrace) {
+static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
+  const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : DEBUGGER_SIGNAL;
   sigval val;
-  val.sival_int = backtrace;
-  if (sigqueue(pid, DEBUGGER_SIGNAL, val) != 0) {
+  val.sival_int = (dump_type == kDebuggerdNativeBacktrace) ? 1 : 0;
+
+  if (sigqueue(pid, signal, val) != 0) {
     PLOG(ERROR) << "libdebuggerd_client: failed to send signal to pid " << pid;
     return false;
   }
@@ -59,8 +62,8 @@
   tv->tv_usec = static_cast<long>(microseconds.count());
 }
 
-bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
-                            unsigned int timeout_ms) {
+bool debuggerd_trigger_dump(pid_t pid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
+                            unique_fd output_fd) {
   LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
   unique_fd sockfd;
   const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
@@ -103,7 +106,7 @@
     return false;
   }
 
-  InterceptRequest req = {.pid = pid };
+  InterceptRequest req = {.pid = pid, .dump_type = dump_type};
   if (!set_timeout(sockfd)) {
     PLOG(ERROR) << "libdebugger_client: failed to set timeout";
     return false;
@@ -155,8 +158,9 @@
     return false;
   }
 
-  bool backtrace = dump_type == kDebuggerdBacktrace;
-  send_signal(pid, backtrace);
+  if (!send_signal(pid, dump_type)) {
+    return false;
+  }
 
   rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
   if (rc == 0) {
@@ -223,15 +227,16 @@
   return true;
 }
 
-int dump_backtrace_to_file(pid_t tid, int fd) {
-  return dump_backtrace_to_file_timeout(tid, fd, 0);
+int dump_backtrace_to_file(pid_t tid, DebuggerdDumpType dump_type, int fd) {
+  return dump_backtrace_to_file_timeout(tid, dump_type, 0, fd);
 }
 
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
+int dump_backtrace_to_file_timeout(pid_t tid, DebuggerdDumpType dump_type, int timeout_secs,
+                                   int fd) {
   android::base::unique_fd copy(dup(fd));
   if (copy == -1) {
     return -1;
   }
   int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
-  return debuggerd_trigger_dump(tid, std::move(copy), kDebuggerdBacktrace, timeout_ms) ? 0 : -1;
+  return debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
 }
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index aff03e5..8420f03 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -31,7 +31,7 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 
-#include <debuggerd/util.h>
+#include "util.h"
 
 using namespace std::chrono_literals;
 using android::base::unique_fd;
@@ -67,7 +67,8 @@
   // Wait for a bit to let the child spawn all of its threads.
   std::this_thread::sleep_for(250ms);
 
-  ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(pipe_write), kDebuggerdBacktrace, 10000));
+  ASSERT_TRUE(
+      debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 10000, std::move(pipe_write)));
   // Immediately kill the forked child, to make sure that the dump didn't return early.
   ASSERT_EQ(0, kill(forkpid, SIGKILL)) << strerror(errno);
 
@@ -107,5 +108,6 @@
 
   unique_fd output_read, output_write;
   ASSERT_TRUE(Pipe(&output_read, &output_write));
-  ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(output_write), kDebuggerdBacktrace, 0));
+  ASSERT_TRUE(
+      debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 0, std::move(output_write)));
 }
diff --git a/debuggerd/common/include/dump_type.h b/debuggerd/common/include/dump_type.h
new file mode 100644
index 0000000..203269e
--- /dev/null
+++ b/debuggerd/common/include/dump_type.h
@@ -0,0 +1,49 @@
+#pragma once
+
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+
+#include <ostream>
+
+enum DebuggerdDumpType : uint8_t {
+  kDebuggerdNativeBacktrace,
+  kDebuggerdTombstone,
+  kDebuggerdJavaBacktrace,
+  kDebuggerdAnyIntercept
+};
+
+inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) {
+  switch (rhs) {
+    case kDebuggerdNativeBacktrace:
+      stream << "kDebuggerdNativeBacktrace";
+      break;
+    case kDebuggerdTombstone:
+      stream << "kDebuggerdTombstone";
+      break;
+    case kDebuggerdJavaBacktrace:
+      stream << "kDebuggerdJavaBacktrace";
+      break;
+    case kDebuggerdAnyIntercept:
+      stream << "kDebuggerdAnyIntercept";
+      break;
+    default:
+      stream << "[unknown]";
+  }
+
+  return stream;
+}
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 0a29844..5db0e5f 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -43,16 +43,15 @@
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
 #include <procinfo/process.h>
-#include <selinux/selinux.h>
 
 #include "backtrace.h"
 #include "tombstone.h"
 #include "utility.h"
 
 #include "debuggerd/handler.h"
-#include "debuggerd/protocol.h"
-#include "debuggerd/tombstoned.h"
-#include "debuggerd/util.h"
+#include "protocol.h"
+#include "tombstoned/tombstoned.h"
+#include "util.h"
 
 using android::base::unique_fd;
 using android::base::ReadFileToString;
@@ -153,13 +152,13 @@
   _exit(1);
 }
 
-static void abort_handler(pid_t target, const bool& tombstoned_connected,
+static void abort_handler(pid_t target, const bool tombstoned_connected,
                           unique_fd& tombstoned_socket, unique_fd& output_fd,
                           const char* abort_msg) {
   // If we abort before we get an output fd, contact tombstoned to let any
   // potential listeners know that we failed.
   if (!tombstoned_connected) {
-    if (!tombstoned_connect(target, &tombstoned_socket, &output_fd)) {
+    if (!tombstoned_connect(target, &tombstoned_socket, &output_fd, kDebuggerdAnyIntercept)) {
       // We failed to connect, not much we can do.
       LOG(ERROR) << "failed to connected to tombstoned to report failure";
       _exit(1);
@@ -210,18 +209,19 @@
   action.sa_handler = signal_handler;
   debuggerd_register_handlers(&action);
 
-  if (argc != 3) {
-    return 1;
-  }
-
   sigset_t mask;
   sigemptyset(&mask);
   if (sigprocmask(SIG_SETMASK, &mask, nullptr) != 0) {
     PLOG(FATAL) << "failed to set signal mask";
   }
 
+  if (argc != 4) {
+    LOG(FATAL) << "Wrong number of args: " << argc << " (expected 4)";
+  }
+
   pid_t main_tid;
   pid_t pseudothread_tid;
+  int dump_type;
 
   if (!android::base::ParseInt(argv[1], &main_tid, 1, std::numeric_limits<pid_t>::max())) {
     LOG(FATAL) << "invalid main tid: " << argv[1];
@@ -231,6 +231,10 @@
     LOG(FATAL) << "invalid pseudothread tid: " << argv[2];
   }
 
+  if (!android::base::ParseInt(argv[3], &dump_type, 0, 1)) {
+    LOG(FATAL) << "invalid requested dump type: " << argv[3];
+  }
+
   if (target == 1) {
     LOG(FATAL) << "target died before we could attach (received main tid = " << main_tid << ")";
   }
@@ -317,8 +321,9 @@
   // Drop our capabilities now that we've attached to the threads we care about.
   drop_capabilities();
 
-  LOG(INFO) << "obtaining output fd from tombstoned";
-  tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd);
+  const DebuggerdDumpType dump_type_enum = static_cast<DebuggerdDumpType>(dump_type);
+  LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum;
+  tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum);
 
   // Write a '\1' to stdout to tell the crashing process to resume.
   // It also restores the value of PR_SET_DUMPABLE at this point.
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 492e9f0..6298ace 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -27,8 +27,8 @@
 #include <android-base/parseint.h>
 #include <android-base/unique_fd.h>
 #include <debuggerd/client.h>
-#include <debuggerd/util.h>
 #include <selinux/selinux.h>
+#include "util.h"
 
 using android::base::unique_fd;
 
@@ -72,8 +72,8 @@
   }
 
   std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
-  if (!debuggerd_trigger_dump(pid, std::move(pipewrite),
-                              backtrace_only ? kDebuggerdBacktrace : kDebuggerdTombstone, 0)) {
+  if (!debuggerd_trigger_dump(pid, backtrace_only ? kDebuggerdNativeBacktrace : kDebuggerdTombstone,
+                              0, std::move(pipewrite))) {
     redirect_thread.join();
     errx(1, "failed to dump process %d", pid);
   }
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index fa0626b..2015157 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -35,12 +35,13 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
-#include <debuggerd/handler.h>
-#include <debuggerd/protocol.h>
-#include <debuggerd/tombstoned.h>
-#include <debuggerd/util.h>
 #include <gtest/gtest.h>
 
+#include "debuggerd/handler.h"
+#include "protocol.h"
+#include "tombstoned/tombstoned.h"
+#include "util.h"
+
 using namespace std::chrono_literals;
 using android::base::unique_fd;
 
@@ -87,14 +88,15 @@
     }                                                                                       \
   } while (0)
 
-static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd) {
+static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
+                                 DebuggerdDumpType intercept_type) {
   intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
                                           ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
   if (intercept_fd->get() == -1) {
     FAIL() << "failed to contact tombstoned: " << strerror(errno);
   }
 
-  InterceptRequest req = {.pid = target_pid};
+  InterceptRequest req = {.pid = target_pid, .dump_type = intercept_type};
 
   unique_fd output_pipe_write;
   if (!Pipe(output_fd, &output_pipe_write)) {
@@ -147,7 +149,7 @@
   CrasherTest();
   ~CrasherTest();
 
-  void StartIntercept(unique_fd* output_fd);
+  void StartIntercept(unique_fd* output_fd, DebuggerdDumpType intercept_type = kDebuggerdTombstone);
 
   // Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
   void FinishIntercept(int* result);
@@ -173,12 +175,12 @@
   android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
 }
 
-void CrasherTest::StartIntercept(unique_fd* output_fd) {
+void CrasherTest::StartIntercept(unique_fd* output_fd, DebuggerdDumpType intercept_type) {
   if (crasher_pid == -1) {
     FAIL() << "crasher hasn't been started";
   }
 
-  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd);
+  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, intercept_type);
 }
 
 void CrasherTest::FinishIntercept(int* result) {
@@ -303,7 +305,7 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(abort)");
 }
 
 TEST_F(CrasherTest, signal) {
@@ -429,7 +431,7 @@
   StartProcess([]() {
     abort();
   });
-  StartIntercept(&output_fd);
+  StartIntercept(&output_fd, kDebuggerdNativeBacktrace);
 
   std::this_thread::sleep_for(500ms);
 
@@ -450,7 +452,7 @@
   FinishIntercept(&intercept_result);
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(abort)");
 }
 
 TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
@@ -470,7 +472,7 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(abort)");
 }
 
 TEST_F(CrasherTest, capabilities) {
@@ -596,11 +598,11 @@
     pid_t pid = 123'456'789 + i;
 
     unique_fd intercept_fd, output_fd;
-    tombstoned_intercept(pid, &intercept_fd, &output_fd);
+    tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
 
     {
       unique_fd tombstoned_socket, input_fd;
-      ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+      ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd, kDebuggerdTombstone));
       ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
     }
 
@@ -628,7 +630,7 @@
       pid_t pid = pid_base + dump;
 
       unique_fd intercept_fd, output_fd;
-      tombstoned_intercept(pid, &intercept_fd, &output_fd);
+      tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
 
       // Pretend to crash, and then immediately close the socket.
       unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
@@ -659,11 +661,11 @@
       pid_t pid = pid_base + dump;
 
       unique_fd intercept_fd, output_fd;
-      tombstoned_intercept(pid, &intercept_fd, &output_fd);
+      tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
 
       {
         unique_fd tombstoned_socket, input_fd;
-        ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+        ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd, kDebuggerdTombstone));
         ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
         tombstoned_notify_completion(tombstoned_socket.get());
       }
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 5c6c59c..43104ec 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -39,16 +39,15 @@
 
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
+#include <async_safe/log.h>
 
 #include "debuggerd/handler.h"
-#include "debuggerd/tombstoned.h"
-#include "debuggerd/util.h"
+#include "tombstoned/tombstoned.h"
+#include "util.h"
 
 #include "backtrace.h"
 #include "tombstone.h"
 
-#include "private/libc_logging.h"
-
 using android::base::unique_fd;
 
 extern "C" void __linker_enable_fallback_allocator();
@@ -81,7 +80,7 @@
   DIR* dir = opendir(buf);
 
   if (!dir) {
-    __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
     return;
   }
 
@@ -145,13 +144,14 @@
   static pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER;
   int ret = pthread_mutex_trylock(&trace_mutex);
   if (ret != 0) {
-    __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s", strerror(ret));
+    async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s",
+                          strerror(ret));
     return;
   }
 
   // Fetch output fd from tombstoned.
   unique_fd tombstone_socket, output_fd;
-  if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd)) {
+  if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdNativeBacktrace)) {
     goto exit;
   }
 
@@ -167,7 +167,8 @@
       // receiving our signal.
       unique_fd pipe_read, pipe_write;
       if (!Pipe(&pipe_read, &pipe_write)) {
-        __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s", strerror(errno));
+        async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
+                              strerror(errno));
         return false;
       }
 
@@ -180,8 +181,8 @@
       siginfo.si_uid = getuid();
 
       if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) {
-        __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s", tid,
-                          strerror(errno));
+        async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
+                              tid, strerror(errno));
         return false;
       }
 
@@ -209,12 +210,13 @@
   static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
   int ret = pthread_mutex_lock(&crash_mutex);
   if (ret != 0) {
-    __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
+    async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
     return;
   }
 
   unique_fd tombstone_socket, output_fd;
-  bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd);
+  bool tombstoned_connected =
+      tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdTombstone);
   debuggerd_fallback_tombstone(output_fd.get(), ucontext, info, abort_message);
   if (tombstoned_connected) {
     tombstoned_notify_completion(tombstone_socket.get());
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index d58c73d..1275229 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -48,8 +48,9 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include "private/bionic_futex.h"
-#include "private/libc_logging.h"
+#include <async_safe/log.h>
+
+#include "dump_type.h"
 
 // see man(2) prctl, specifically the section about PR_GET_NAME
 #define MAX_TASK_NAME_LEN (16)
@@ -72,6 +73,10 @@
   return syscall(__NR_gettid);
 }
 
+static inline void futex_wait(volatile void* ftx, int value) {
+  syscall(__NR_futex, ftx, FUTEX_WAIT, value, nullptr, nullptr, 0);
+}
+
 class ErrnoRestorer {
  public:
   ErrnoRestorer() : saved_errno_(errno) {
@@ -92,11 +97,12 @@
 // Mutex to ensure only one crashing thread dumps itself.
 static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
 
-// Don't use __libc_fatal because it exits via abort, which might put us back into a signal handler.
+// Don't use async_safe_fatal because it exits via abort, which might put us back into
+// a signal handler.
 static void __noreturn __printflike(1, 2) fatal(const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
-  __libc_format_log_va_list(ANDROID_LOG_FATAL, "libc", fmt, args);
+  async_safe_format_log_va_list(ANDROID_LOG_FATAL, "libc", fmt, args);
   _exit(1);
 }
 
@@ -106,10 +112,30 @@
   va_start(args, fmt);
 
   char buf[4096];
-  __libc_format_buffer_va_list(buf, sizeof(buf), fmt, args);
+  async_safe_format_buffer_va_list(buf, sizeof(buf), fmt, args);
   fatal("%s: %s", buf, strerror(err));
 }
 
+static bool get_main_thread_name(char* buf, size_t len) {
+  int fd = open("/proc/self/comm", O_RDONLY | O_CLOEXEC);
+  if (fd == -1) {
+    return false;
+  }
+
+  ssize_t rc = read(fd, buf, len);
+  close(fd);
+  if (rc == -1) {
+    return false;
+  } else if (rc == 0) {
+    // Should never happen?
+    return false;
+  }
+
+  // There's a trailing newline, replace it with a NUL.
+  buf[rc - 1] = '\0';
+  return true;
+}
+
 /*
  * Writes a summary of the signal to the log file.  We do this so that, if
  * for some reason we're not able to contact debuggerd, there is still some
@@ -130,8 +156,8 @@
   }
 
   if (signum == DEBUGGER_SIGNAL) {
-    __libc_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
-                      thread_name);
+    async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
+                          thread_name);
     return;
   }
 
@@ -176,14 +202,20 @@
   char addr_desc[32];  // ", fault addr 0x1234"
   addr_desc[0] = code_desc[0] = 0;
   if (info != nullptr) {
-    __libc_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code);
+    async_safe_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code);
     if (has_address) {
-      __libc_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
+      async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
     }
   }
 
-  __libc_format_log(ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s)", signum,
-                    signal_name, code_desc, addr_desc, __gettid(), thread_name);
+  char main_thread_name[MAX_TASK_NAME_LEN + 1];
+  if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
+    strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
+  }
+
+  async_safe_format_log(
+      ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s), pid %d (%s)", signum,
+      signal_name, code_desc, addr_desc, __gettid(), thread_name, __getpid(), main_thread_name);
 }
 
 /*
@@ -192,8 +224,8 @@
 static bool have_siginfo(int signum) {
   struct sigaction old_action;
   if (sigaction(signum, nullptr, &old_action) < 0) {
-    __libc_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
-                      strerror(errno));
+    async_safe_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
+                          strerror(errno));
     return false;
   }
   return (old_action.sa_flags & SA_SIGINFO) != 0;
@@ -217,7 +249,7 @@
     capdata[1].inheritable = capdata[1].permitted;
 
     if (capset(&capheader, &capdata[0]) == -1) {
-      __libc_format_log(ANDROID_LOG_ERROR, "libc", "capset failed: %s", strerror(errno));
+      async_safe_format_log(ANDROID_LOG_ERROR, "libc", "capset failed: %s", strerror(errno));
     }
   }
 
@@ -227,8 +259,8 @@
   for (unsigned long i = 0; i < 64; ++i) {
     if (capmask & (1ULL << i)) {
       if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) != 0) {
-        __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to raise ambient capability %lu: %s",
-                          i, strerror(errno));
+        async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                              "failed to raise ambient capability %lu: %s", i, strerror(errno));
       }
     }
   }
@@ -249,6 +281,14 @@
 // process.
 static void* pseudothread_stack;
 
+static DebuggerdDumpType get_dump_type(const debugger_thread_info* thread_info) {
+  if (thread_info->signal_number == DEBUGGER_SIGNAL && thread_info->info->si_value.sival_int) {
+    return kDebuggerdNativeBacktrace;
+  }
+
+  return kDebuggerdTombstone;
+}
+
 static int debuggerd_dispatch_pseudothread(void* arg) {
   debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
 
@@ -270,8 +310,8 @@
   // Don't use fork(2) to avoid calling pthread_atfork handlers.
   int forkpid = clone(nullptr, nullptr, 0, nullptr);
   if (forkpid == -1) {
-    __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to fork in debuggerd signal handler: %s",
-                      strerror(errno));
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc",
+                          "failed to fork in debuggerd signal handler: %s", strerror(errno));
   } else if (forkpid == 0) {
     TEMP_FAILURE_RETRY(dup2(pipefds[1], STDOUT_FILENO));
     close(pipefds[0]);
@@ -281,10 +321,15 @@
 
     char main_tid[10];
     char pseudothread_tid[10];
-    __libc_format_buffer(main_tid, sizeof(main_tid), "%d", thread_info->crashing_tid);
-    __libc_format_buffer(pseudothread_tid, sizeof(pseudothread_tid), "%d", thread_info->pseudothread_tid);
+    char debuggerd_dump_type[10];
+    async_safe_format_buffer(main_tid, sizeof(main_tid), "%d", thread_info->crashing_tid);
+    async_safe_format_buffer(pseudothread_tid, sizeof(pseudothread_tid), "%d",
+                             thread_info->pseudothread_tid);
+    async_safe_format_buffer(debuggerd_dump_type, sizeof(debuggerd_dump_type), "%d",
+                             get_dump_type(thread_info));
 
-    execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, nullptr);
+    execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
+          nullptr);
 
     fatal_errno("exec failed");
   } else {
@@ -292,15 +337,16 @@
     char buf[4];
     ssize_t rc = TEMP_FAILURE_RETRY(read(pipefds[0], &buf, sizeof(buf)));
     if (rc == -1) {
-      __libc_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", strerror(errno));
+      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s",
+                            strerror(errno));
     } else if (rc == 0) {
-      __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
+      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
     } else if (rc != 1) {
-      __libc_format_log(ANDROID_LOG_FATAL, "libc",
-                        "read of IPC pipe returned unexpected value: %zd", rc);
+      async_safe_format_log(ANDROID_LOG_FATAL, "libc",
+                            "read of IPC pipe returned unexpected value: %zd", rc);
     } else {
       if (buf[0] != '\1') {
-        __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
+        async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
       } else {
         thread_info->crash_dump_started = true;
       }
@@ -310,10 +356,10 @@
     // Don't leave a zombie child.
     int status;
     if (TEMP_FAILURE_RETRY(waitpid(forkpid, &status, 0)) == -1) {
-      __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
-                        strerror(errno));
+      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
+                            strerror(errno));
     } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) {
-      __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
+      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
       thread_info->crash_dump_started = false;
     }
   }
@@ -393,7 +439,7 @@
   // Only allow one thread to handle a signal at a time.
   int ret = pthread_mutex_lock(&crash_mutex);
   if (ret != 0) {
-    __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
+    async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
     return;
   }
 
@@ -429,10 +475,10 @@
   }
 
   // Wait for the child to start...
-  __futex_wait(&thread_info.pseudothread_tid, -1, nullptr);
+  futex_wait(&thread_info.pseudothread_tid, -1);
 
   // and then wait for it to finish.
-  __futex_wait(&thread_info.pseudothread_tid, child_pid, nullptr);
+  futex_wait(&thread_info.pseudothread_tid, child_pid);
 
   // Restore PR_SET_DUMPABLE to its original value.
   if (prctl(PR_SET_DUMPABLE, orig_dumpable) != 0) {
diff --git a/debuggerd/include/debuggerd/client.h b/debuggerd/include/debuggerd/client.h
index 01de57b..b7284b0 100644
--- a/debuggerd/include/debuggerd/client.h
+++ b/debuggerd/include/debuggerd/client.h
@@ -22,15 +22,13 @@
 
 #include <android-base/unique_fd.h>
 
-enum DebuggerdDumpType {
-  kDebuggerdBacktrace,
-  kDebuggerdTombstone,
-};
+#include "dump_type.h"
 
 // Trigger a dump of specified process to output_fd.
 // output_fd is consumed, timeout of 0 will wait forever.
-bool debuggerd_trigger_dump(pid_t pid, android::base::unique_fd output_fd,
-                            enum DebuggerdDumpType dump_type, unsigned int timeout_ms);
+bool debuggerd_trigger_dump(pid_t pid, enum DebuggerdDumpType dump_type, unsigned int timeout_ms,
+                            android::base::unique_fd output_fd);
 
-int dump_backtrace_to_file(pid_t tid, int fd);
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
+int dump_backtrace_to_file(pid_t tid, enum DebuggerdDumpType dump_type, int output_fd);
+int dump_backtrace_to_file_timeout(pid_t tid, enum DebuggerdDumpType dump_type, int timeout_secs,
+                                   int output_fd);
diff --git a/debuggerd/include/debuggerd/protocol.h b/debuggerd/protocol.h
similarity index 85%
rename from debuggerd/include/debuggerd/protocol.h
rename to debuggerd/protocol.h
index 0756876..7e1961e 100644
--- a/debuggerd/include/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -18,9 +18,12 @@
 
 #include <stdint.h>
 
+#include "dump_type.h"
+
 // Sockets in the ANDROID_SOCKET_NAMESPACE_RESERVED namespace.
 // Both sockets are SOCK_SEQPACKET sockets, so no explicit length field is needed.
 constexpr char kTombstonedCrashSocketName[] = "tombstoned_crash";
+constexpr char kTombstonedJavaTraceSocketName[] = "tombstoned_java_trace";
 constexpr char kTombstonedInterceptSocketName[] = "tombstoned_intercept";
 
 enum class CrashPacketType : uint8_t {
@@ -39,6 +42,7 @@
 };
 
 struct DumpRequest {
+  DebuggerdDumpType dump_type;
   int32_t pid;
 };
 
@@ -53,10 +57,15 @@
 // Comes with a file descriptor via SCM_RIGHTS.
 // This packet should be sent before an actual dump happens.
 struct InterceptRequest {
+  DebuggerdDumpType dump_type;
   int32_t pid;
 };
 
 enum class InterceptStatus : uint8_t {
+  // Returned when an intercept of a different type has already been
+  // registered (and is active) for a given PID.
+  kFailedAlreadyRegistered,
+  // Returned in all other failure cases.
   kFailed,
   kStarted,
   kRegistered,
diff --git a/debuggerd/include/debuggerd/tombstoned.h b/debuggerd/tombstoned/include/tombstoned/tombstoned.h
similarity index 87%
rename from debuggerd/include/debuggerd/tombstoned.h
rename to debuggerd/tombstoned/include/tombstoned/tombstoned.h
index d158d50..6403dbe 100644
--- a/debuggerd/include/debuggerd/tombstoned.h
+++ b/debuggerd/tombstoned/include/tombstoned/tombstoned.h
@@ -20,7 +20,9 @@
 
 #include <android-base/unique_fd.h>
 
+#include "dump_type.h"
+
 bool tombstoned_connect(pid_t pid, android::base::unique_fd* tombstoned_socket,
-                        android::base::unique_fd* output_fd);
+                        android::base::unique_fd* output_fd, DebuggerdDumpType dump_type);
 
 bool tombstoned_notify_completion(int tombstoned_socket);
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index dff942c..24960bc 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -28,8 +28,8 @@
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 
-#include "debuggerd/protocol.h"
-#include "debuggerd/util.h"
+#include "protocol.h"
+#include "util.h"
 
 using android::base::unique_fd;
 
@@ -61,11 +61,24 @@
       reason = "due to input";
     }
 
-    LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " terminated " << reason;
+    LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " and type "
+              << intercept->dump_type << " terminated: " << reason;
     intercept_manager->intercepts.erase(it);
   }
 }
 
+static bool is_intercept_request_valid(const InterceptRequest& request) {
+  if (request.pid <= 0 || request.pid > std::numeric_limits<pid_t>::max()) {
+    return false;
+  }
+
+  if (request.dump_type < 0 || request.dump_type > kDebuggerdJavaBacktrace) {
+    return false;
+  }
+
+  return true;
+}
+
 static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
   auto intercept = reinterpret_cast<Intercept*>(arg);
   InterceptManager* intercept_manager = intercept->intercept_manager;
@@ -103,23 +116,24 @@
     rcv_fd.reset(moved_fd);
 
     // We trust the other side, so only do minimal validity checking.
-    if (intercept_request.pid <= 0 || intercept_request.pid > std::numeric_limits<pid_t>::max()) {
+    if (!is_intercept_request_valid(intercept_request)) {
       InterceptResponse response = {};
       response.status = InterceptStatus::kFailed;
-      snprintf(response.error_message, sizeof(response.error_message), "invalid pid %" PRId32,
-               intercept_request.pid);
+      snprintf(response.error_message, sizeof(response.error_message), "invalid intercept request");
       TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
       goto fail;
     }
 
     intercept->intercept_pid = intercept_request.pid;
+    intercept->dump_type = intercept_request.dump_type;
 
     // Check if it's already registered.
     if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
       InterceptResponse response = {};
-      response.status = InterceptStatus::kFailed;
+      response.status = InterceptStatus::kFailedAlreadyRegistered;
       snprintf(response.error_message, sizeof(response.error_message),
-               "pid %" PRId32 " already intercepted", intercept_request.pid);
+               "pid %" PRId32 " already intercepted, type %d", intercept_request.pid,
+               intercept_request.dump_type);
       TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
       LOG(WARNING) << response.error_message;
       goto fail;
@@ -138,7 +152,8 @@
     intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
     intercept->registered = true;
 
-    LOG(INFO) << "tombstoned registered intercept for pid " << intercept_request.pid;
+    LOG(INFO) << "registered intercept for pid " << intercept_request.pid << " and type "
+              << intercept_request.dump_type;
 
     // Register a different read event on the socket so that we can remove intercepts if the socket
     // closes (e.g. if a user CTRL-C's the process that requested the intercept).
@@ -174,16 +189,27 @@
                                       intercept_socket);
 }
 
-bool InterceptManager::GetIntercept(pid_t pid, android::base::unique_fd* out_fd) {
+bool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type,
+                                    android::base::unique_fd* out_fd) {
   auto it = this->intercepts.find(pid);
   if (it == this->intercepts.end()) {
     return false;
   }
 
+  if (dump_type == kDebuggerdAnyIntercept) {
+    LOG(INFO) << "found registered intercept of type " << it->second->dump_type
+              << " for requested type kDebuggerdAnyIntercept";
+  } else if (it->second->dump_type != dump_type) {
+    LOG(WARNING) << "found non-matching intercept of type " << it->second->dump_type
+                 << " for requested type: " << dump_type;
+    return false;
+  }
+
   auto intercept = std::move(it->second);
   this->intercepts.erase(it);
 
-  LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid;
+  LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid
+            << " and type " << intercept->dump_type;
   InterceptResponse response = {};
   response.status = InterceptStatus::kStarted;
   TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
diff --git a/debuggerd/tombstoned/intercept_manager.h b/debuggerd/tombstoned/intercept_manager.h
index cb5db62..a11d565 100644
--- a/debuggerd/tombstoned/intercept_manager.h
+++ b/debuggerd/tombstoned/intercept_manager.h
@@ -25,6 +25,8 @@
 
 #include <android-base/unique_fd.h>
 
+#include "dump_type.h"
+
 struct InterceptManager;
 
 struct Intercept {
@@ -39,6 +41,7 @@
   pid_t intercept_pid = -1;
   android::base::unique_fd output_fd;
   bool registered = false;
+  DebuggerdDumpType dump_type = kDebuggerdNativeBacktrace;
 };
 
 struct InterceptManager {
@@ -50,5 +53,5 @@
   InterceptManager(InterceptManager& copy) = delete;
   InterceptManager(InterceptManager&& move) = delete;
 
-  bool GetIntercept(pid_t pid, android::base::unique_fd* out_fd);
+  bool GetIntercept(pid_t pid, DebuggerdDumpType dump_type, android::base::unique_fd* out_fd);
 };
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 0e5dcef..09cff45 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -23,23 +23,28 @@
 
 #include <array>
 #include <deque>
+#include <string>
 #include <unordered_map>
+#include <utility>
 
 #include <event2/event.h>
 #include <event2/listener.h>
 #include <event2/thread.h>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 
 #include "debuggerd/handler.h"
-#include "debuggerd/protocol.h"
-#include "debuggerd/util.h"
+#include "dump_type.h"
+#include "protocol.h"
+#include "util.h"
 
 #include "intercept_manager.h"
 
+using android::base::GetIntProperty;
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
@@ -54,82 +59,152 @@
 // It's either owned by an active event that must have a timeout, or owned by
 // queued_requests, in the case that multiple crashes come in at the same time.
 struct Crash {
-  ~Crash() {
-    event_free(crash_event);
-  }
+  ~Crash() { event_free(crash_event); }
 
   unique_fd crash_fd;
   pid_t crash_pid;
   event* crash_event = nullptr;
   std::string crash_path;
+
+  DebuggerdDumpType crash_type;
 };
 
-static constexpr char kTombstoneDirectory[] = "/data/tombstones/";
-static constexpr size_t kTombstoneCount = 10;
-static int tombstone_directory_fd = -1;
-static int next_tombstone = 0;
+class CrashQueue {
+ public:
+  CrashQueue(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts,
+             size_t max_concurrent_dumps)
+      : file_name_prefix_(file_name_prefix),
+        dir_path_(dir_path),
+        dir_fd_(open(dir_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)),
+        max_artifacts_(max_artifacts),
+        next_artifact_(0),
+        max_concurrent_dumps_(max_concurrent_dumps),
+        num_concurrent_dumps_(0) {
+    if (dir_fd_ == -1) {
+      PLOG(FATAL) << "failed to open directory: " << dir_path;
+    }
 
-static constexpr size_t kMaxConcurrentDumps = 1;
-static size_t num_concurrent_dumps = 0;
+    // NOTE: If max_artifacts_ <= max_concurrent_dumps_, then theoretically the
+    // same filename could be handed out to multiple processes.
+    CHECK(max_artifacts_ > max_concurrent_dumps_);
 
-static std::deque<Crash*> queued_requests;
+    find_oldest_artifact();
+  }
+
+  static CrashQueue* for_crash(const Crash* crash) {
+    return (crash->crash_type == kDebuggerdJavaBacktrace) ? for_anrs() : for_tombstones();
+  }
+
+  static CrashQueue* for_tombstones() {
+    static CrashQueue queue("/data/tombstones", "tombstone_" /* file_name_prefix */,
+                            GetIntProperty("tombstoned.max_tombstone_count", 10),
+                            1 /* max_concurrent_dumps */);
+    return &queue;
+  }
+
+  static CrashQueue* for_anrs() {
+    static CrashQueue queue("/data/anr", "anr_" /* file_name_prefix */,
+                            GetIntProperty("tombstoned.max_anr_count", 64),
+                            4 /* max_concurrent_dumps */);
+    return &queue;
+  }
+
+  std::pair<unique_fd, std::string> get_output() {
+    unique_fd result;
+    std::string file_name = StringPrintf("%s%02d", file_name_prefix_.c_str(), next_artifact_);
+
+    // Unlink and create the file, instead of using O_TRUNC, to avoid two processes
+    // interleaving their output in case we ever get into that situation.
+    if (unlinkat(dir_fd_, file_name.c_str(), 0) != 0 && errno != ENOENT) {
+      PLOG(FATAL) << "failed to unlink tombstone at " << dir_path_ << "/" << file_name;
+    }
+
+    result.reset(openat(dir_fd_, file_name.c_str(),
+                        O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
+    if (result == -1) {
+      PLOG(FATAL) << "failed to create tombstone at " << dir_path_ << "/" << file_name;
+    }
+
+    next_artifact_ = (next_artifact_ + 1) % max_artifacts_;
+    return {std::move(result), dir_path_ + "/" + file_name};
+  }
+
+  bool maybe_enqueue_crash(Crash* crash) {
+    if (num_concurrent_dumps_ == max_concurrent_dumps_) {
+      queued_requests_.push_back(crash);
+      return true;
+    }
+
+    return false;
+  }
+
+  void maybe_dequeue_crashes(void (*handler)(Crash* crash)) {
+    while (!queued_requests_.empty() && num_concurrent_dumps_ < max_concurrent_dumps_) {
+      Crash* next_crash = queued_requests_.front();
+      queued_requests_.pop_front();
+      handler(next_crash);
+    }
+  }
+
+  void on_crash_started() { ++num_concurrent_dumps_; }
+
+  void on_crash_completed() { --num_concurrent_dumps_; }
+
+ private:
+  void find_oldest_artifact() {
+    size_t oldest_tombstone = 0;
+    time_t oldest_time = std::numeric_limits<time_t>::max();
+
+    for (size_t i = 0; i < max_artifacts_; ++i) {
+      std::string path = StringPrintf("%s/%s%02zu", dir_path_.c_str(), file_name_prefix_.c_str(), i);
+      struct stat st;
+      if (stat(path.c_str(), &st) != 0) {
+        if (errno == ENOENT) {
+          oldest_tombstone = i;
+          break;
+        } else {
+          PLOG(ERROR) << "failed to stat " << path;
+          continue;
+        }
+      }
+
+      if (st.st_mtime < oldest_time) {
+        oldest_tombstone = i;
+        oldest_time = st.st_mtime;
+      }
+    }
+
+    next_artifact_ = oldest_tombstone;
+  }
+
+  const std::string file_name_prefix_;
+
+  const std::string dir_path_;
+  const int dir_fd_;
+
+  const size_t max_artifacts_;
+  int next_artifact_;
+
+  const size_t max_concurrent_dumps_;
+  size_t num_concurrent_dumps_;
+
+  std::deque<Crash*> queued_requests_;
+
+  DISALLOW_COPY_AND_ASSIGN(CrashQueue);
+};
+
+// Whether java trace dumps are produced via tombstoned.
+static constexpr bool kJavaTraceDumpsEnabled = false;
 
 // Forward declare the callbacks so they can be placed in a sensible order.
 static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int, void*);
 static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg);
 static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg);
 
-static void find_oldest_tombstone() {
-  size_t oldest_tombstone = 0;
-  time_t oldest_time = std::numeric_limits<time_t>::max();
-
-  for (size_t i = 0; i < kTombstoneCount; ++i) {
-    std::string path = android::base::StringPrintf("%stombstone_%02zu", kTombstoneDirectory, i);
-    struct stat st;
-    if (stat(path.c_str(), &st) != 0) {
-      if (errno == ENOENT) {
-        oldest_tombstone = i;
-        break;
-      } else {
-        PLOG(ERROR) << "failed to stat " << path;
-        continue;
-      }
-    }
-
-    if (st.st_mtime < oldest_time) {
-      oldest_tombstone = i;
-      oldest_time = st.st_mtime;
-    }
-  }
-
-  next_tombstone = oldest_tombstone;
-}
-
-static std::pair<unique_fd, std::string> get_tombstone() {
-  // If kMaxConcurrentDumps is greater than 1, then theoretically the same
-  // filename could be handed out to multiple processes. Unlink and create the
-  // file, instead of using O_TRUNC, to avoid two processes interleaving their
-  // output.
-  unique_fd result;
-  std::string file_name = StringPrintf("tombstone_%02d", next_tombstone);
-  if (unlinkat(tombstone_directory_fd, file_name.c_str(), 0) != 0 && errno != ENOENT) {
-    PLOG(FATAL) << "failed to unlink tombstone at " << kTombstoneDirectory << "/" << file_name;
-  }
-
-  result.reset(openat(tombstone_directory_fd, file_name.c_str(),
-                      O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
-  if (result == -1) {
-    PLOG(FATAL) << "failed to create tombstone at " << kTombstoneDirectory << "/" << file_name;
-  }
-
-  next_tombstone = (next_tombstone + 1) % kTombstoneCount;
-  return {std::move(result), std::string(kTombstoneDirectory) + "/" + file_name};
-}
-
 static void perform_request(Crash* crash) {
   unique_fd output_fd;
-  if (!intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
-    std::tie(output_fd, crash->crash_path) = get_tombstone();
+  if (!intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd)) {
+    std::tie(output_fd, crash->crash_path) = CrashQueue::for_crash(crash)->get_output();
   }
 
   TombstonedCrashPacket response = {
@@ -152,26 +227,20 @@
     event_add(crash->crash_event, &timeout);
   }
 
-  ++num_concurrent_dumps;
+  CrashQueue::for_crash(crash)->on_crash_started();
   return;
 
 fail:
   delete crash;
 }
 
-static void dequeue_requests() {
-  while (!queued_requests.empty() && num_concurrent_dumps < kMaxConcurrentDumps) {
-    Crash* next_crash = queued_requests.front();
-    queued_requests.pop_front();
-    perform_request(next_crash);
-  }
-}
-
 static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
                             void*) {
   event_base* base = evconnlistener_get_base(listener);
   Crash* crash = new Crash();
 
+  // TODO: Make sure that only java crashes come in on the java socket
+  // and only native crashes on the native socket.
   struct timeval timeout = { 1, 0 };
   event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);
   crash->crash_fd.reset(sockfd);
@@ -182,6 +251,7 @@
 static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
   ssize_t rc;
   Crash* crash = static_cast<Crash*>(arg);
+
   TombstonedCrashPacket request = {};
 
   if ((ev & EV_TIMEOUT) != 0) {
@@ -208,12 +278,33 @@
     goto fail;
   }
 
-  crash->crash_pid = request.packet.dump_request.pid;
+  crash->crash_type = request.packet.dump_request.dump_type;
+  if (crash->crash_type < 0 || crash->crash_type > kDebuggerdAnyIntercept) {
+    LOG(WARNING) << "unexpected crash dump type: " << crash->crash_type;
+    goto fail;
+  }
+
+  if (crash->crash_type != kDebuggerdJavaBacktrace) {
+    crash->crash_pid = request.packet.dump_request.pid;
+  } else {
+    // Requests for java traces are sent from untrusted processes, so we
+    // must not trust the PID sent down with the request. Instead, we ask the
+    // kernel.
+    ucred cr = {};
+    socklen_t len = sizeof(cr);
+    int ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
+    if (ret != 0) {
+      PLOG(ERROR) << "Failed to getsockopt(..SO_PEERCRED)";
+      goto fail;
+    }
+
+    crash->crash_pid = cr.pid;
+  }
+
   LOG(INFO) << "received crash request for pid " << crash->crash_pid;
 
-  if (num_concurrent_dumps == kMaxConcurrentDumps) {
+  if (CrashQueue::for_crash(crash)->maybe_enqueue_crash(crash)) {
     LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
-    queued_requests.push_back(crash);
   } else {
     perform_request(crash);
   }
@@ -229,7 +320,7 @@
   Crash* crash = static_cast<Crash*>(arg);
   TombstonedCrashPacket request = {};
 
-  --num_concurrent_dumps;
+  CrashQueue::for_crash(crash)->on_crash_completed();
 
   if ((ev & EV_READ) == 0) {
     goto fail;
@@ -256,10 +347,11 @@
   }
 
 fail:
+  CrashQueue* queue = CrashQueue::for_crash(crash);
   delete crash;
 
   // If there's something queued up, let them proceed.
-  dequeue_requests();
+  queue->maybe_dequeue_crashes(perform_request);
 }
 
 int main(int, char* []) {
@@ -273,13 +365,6 @@
   };
   debuggerd_register_handlers(&action);
 
-  tombstone_directory_fd = open(kTombstoneDirectory, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
-  if (tombstone_directory_fd == -1) {
-    PLOG(FATAL) << "failed to open tombstone directory";
-  }
-
-  find_oldest_tombstone();
-
   int intercept_socket = android_get_control_socket(kTombstonedInterceptSocketName);
   int crash_socket = android_get_control_socket(kTombstonedCrashSocketName);
 
@@ -297,10 +382,24 @@
 
   intercept_manager = new InterceptManager(base, intercept_socket);
 
-  evconnlistener* listener =
-    evconnlistener_new(base, crash_accept_cb, nullptr, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
-  if (!listener) {
-    LOG(FATAL) << "failed to create evconnlistener";
+  evconnlistener* tombstone_listener = evconnlistener_new(
+      base, crash_accept_cb, CrashQueue::for_tombstones(), -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
+  if (!tombstone_listener) {
+    LOG(FATAL) << "failed to create evconnlistener for tombstones.";
+  }
+
+  if (kJavaTraceDumpsEnabled) {
+    const int java_trace_socket = android_get_control_socket(kTombstonedJavaTraceSocketName);
+    if (java_trace_socket == -1) {
+      PLOG(FATAL) << "failed to get socket from init";
+    }
+
+    evutil_make_socket_nonblocking(java_trace_socket);
+    evconnlistener* java_trace_listener = evconnlistener_new(
+        base, crash_accept_cb, CrashQueue::for_anrs(), -1, LEV_OPT_CLOSE_ON_FREE, java_trace_socket);
+    if (!java_trace_listener) {
+      LOG(FATAL) << "failed to create evconnlistener for java traces.";
+    }
   }
 
   LOG(INFO) << "tombstoned successfully initialized";
diff --git a/debuggerd/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp
similarity index 62%
rename from debuggerd/tombstoned_client.cpp
rename to debuggerd/tombstoned/tombstoned_client.cpp
index 03b4a20..bdb4c1a 100644
--- a/debuggerd/tombstoned_client.cpp
+++ b/debuggerd/tombstoned/tombstoned_client.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "debuggerd/tombstoned.h"
+#include "tombstoned/tombstoned.h"
 
 #include <fcntl.h>
 #include <unistd.h>
@@ -22,43 +22,47 @@
 #include <utility>
 
 #include <android-base/unique_fd.h>
+#include <async_safe/log.h>
 #include <cutils/sockets.h>
 
-#include "debuggerd/protocol.h"
-#include "debuggerd/util.h"
-#include "private/libc_logging.h"
+#include "protocol.h"
+#include "util.h"
 
 using android::base::unique_fd;
 
-bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd) {
-  unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
-                                       ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
+                        DebuggerdDumpType dump_type) {
+  unique_fd sockfd(
+      socket_local_client((dump_type != kDebuggerdJavaBacktrace ? kTombstonedCrashSocketName
+                                                                : kTombstonedJavaTraceSocketName),
+                          ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
   if (sockfd == -1) {
-    __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to tombstoned: %s",
-                      strerror(errno));
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to tombstoned: %s",
+                          strerror(errno));
     return false;
   }
 
   TombstonedCrashPacket packet = {};
   packet.packet_type = CrashPacketType::kDumpRequest;
   packet.packet.dump_request.pid = pid;
+  packet.packet.dump_request.dump_type = dump_type;
   if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
-    __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to write DumpRequest packet: %s",
-                      strerror(errno));
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write DumpRequest packet: %s",
+                          strerror(errno));
     return false;
   }
 
   unique_fd tmp_output_fd;
   ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd);
   if (rc == -1) {
-    __libc_format_log(ANDROID_LOG_ERROR, "libc",
-                      "failed to read response to DumpRequest packet: %s", strerror(errno));
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                          "failed to read response to DumpRequest packet: %s", strerror(errno));
     return false;
   } else if (rc != sizeof(packet)) {
-    __libc_format_log(
-      ANDROID_LOG_ERROR, "libc",
-      "received DumpRequest response packet of incorrect length (expected %zu, got %zd)",
-      sizeof(packet), rc);
+    async_safe_format_log(
+        ANDROID_LOG_ERROR, "libc",
+        "received DumpRequest response packet of incorrect length (expected %zu, got %zd)",
+        sizeof(packet), rc);
     return false;
   }
 
@@ -67,8 +71,8 @@
   // a regular fd, and writing to an fd with O_APPEND).
   int flags = fcntl(tmp_output_fd.get(), F_GETFL);
   if (fcntl(tmp_output_fd.get(), F_SETFL, flags | O_APPEND) != 0) {
-    __libc_format_log(ANDROID_LOG_WARN, "libc", "failed to set output fd flags: %s",
-                      strerror(errno));
+    async_safe_format_log(ANDROID_LOG_WARN, "libc", "failed to set output fd flags: %s",
+                          strerror(errno));
   }
 
   *tombstoned_socket = std::move(sockfd);
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 4c015d7..c6a997b 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "debuggerd/util.h"
+#include "util.h"
 
 #include <sys/socket.h>
 
@@ -22,9 +22,7 @@
 
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
-#include <debuggerd/protocol.h>
-
-#include "private/libc_logging.h"
+#include "protocol.h"
 
 using android::base::unique_fd;
 
diff --git a/debuggerd/include/debuggerd/util.h b/debuggerd/util.h
similarity index 100%
rename from debuggerd/include/debuggerd/util.h
rename to debuggerd/util.h
diff --git a/demangle/Android.bp b/demangle/Android.bp
index 96ab57d..ce617a7 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -28,6 +28,9 @@
 
 cc_library {
     name: "libdemangle",
+
+    vendor_available: true,
+
     defaults: ["libdemangle_defaults"],
 
     srcs: [
diff --git a/trusty/storage/interface/Android.mk b/demangle/Android.mk
similarity index 65%
rename from trusty/storage/interface/Android.mk
rename to demangle/Android.mk
index 15cb6f3..e3cfc2a 100644
--- a/trusty/storage/interface/Android.mk
+++ b/demangle/Android.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (C) 2015 The Android Open-Source Project
+# Copyright (C) 2017 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,12 +14,19 @@
 # limitations under the License.
 #
 
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := libtrustystorageinterface
+LOCAL_MODULE := demangle_fuzzer
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := \
+    Demangler.cpp \
+    demangle_fuzzer.cpp \
 
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := \
+    -Wall \
+    -Werror \
+    -Wextra \
 
-include $(BUILD_STATIC_LIBRARY)
+include $(BUILD_FUZZ_TEST)
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
index fb68119..f56a9be 100644
--- a/demangle/DemangleTest.cpp
+++ b/demangle/DemangleTest.cpp
@@ -22,7 +22,14 @@
 
 #include "Demangler.h"
 
-TEST(DemangleTest, VoidArgumentTest) {
+TEST(DemangleTest, IllegalArgumentModifiers) {
+  Demangler demangler;
+
+  ASSERT_EQ("_Zpp4FUNKK", demangler.Parse("_Zpp4FUNKK"));
+  ASSERT_EQ("_Zpp4FUNVV", demangler.Parse("_Zpp4FUNVV"));
+}
+
+TEST(DemangleTest, VoidArgument) {
   Demangler demangler;
 
   ASSERT_EQ("func()", demangler.Parse("_ZN4funcEv"));
@@ -189,6 +196,14 @@
   ASSERT_EQ("value(one, signed char)", demangler.Parse("_Z5value3onea"));
 }
 
+TEST(DemangleTest, FunctionStartsWithLPlusNumber) {
+  Demangler demangler;
+
+  ASSERT_EQ("value(char, int)", demangler.Parse("_ZL5valueci"));
+  ASSERT_EQ("abcdefjklmn(signed char)", demangler.Parse("_ZL11abcdefjklmna"));
+  ASSERT_EQ("value(one, signed char)", demangler.Parse("_ZL5value3onea"));
+}
+
 TEST(DemangleTest, StdTypes) {
   Demangler demangler;
 
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
index 77cfd3b..c0a96aa 100644
--- a/demangle/Demangler.cpp
+++ b/demangle/Demangler.cpp
@@ -542,9 +542,8 @@
     } else {
       suffix = " volatile";
     }
-    if (name[-1] == 'K' || name[-1] == 'V') {
+    if (!cur_state_.suffixes.empty() && (name[-1] == 'K' || name[-1] == 'V')) {
       // Special case, const/volatile apply as a single entity.
-      assert(!cur_state_.suffixes.empty());
       size_t index = cur_state_.suffixes.size();
       cur_state_.suffixes[index-1].insert(0, suffix);
     } else {
@@ -699,6 +698,8 @@
 
   if (std::isdigit(*name)) {
     name = GetStringFromLength(name, &function_name_);
+  } else if (*name == 'L' && std::isdigit(name[1])) {
+    name = GetStringFromLength(name + 1, &function_name_);
   } else {
     name = AppendOperatorString(name);
     function_name_ = cur_state_.str;
@@ -723,7 +724,8 @@
       && static_cast<size_t>(cur_name - name) < max_length) {
     cur_name = (this->*parse_func_)(cur_name);
   }
-  if (cur_name == nullptr || *cur_name != '\0' || function_name_.empty()) {
+  if (cur_name == nullptr || *cur_name != '\0' || function_name_.empty() ||
+      !cur_state_.suffixes.empty()) {
     return name;
   }
 
diff --git a/demangle/demangle.cpp b/demangle/demangle.cpp
index be4d2dd..66e5e58 100644
--- a/demangle/demangle.cpp
+++ b/demangle/demangle.cpp
@@ -20,22 +20,25 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <cctype>
 #include <string>
 
 #include <demangle.h>
 
 extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
 
-void usage(const char* prog_name) {
-  printf("Usage: %s [-c] <NAME_TO_DEMANGLE>\n", prog_name);
-  printf("  -c\n");
-  printf("    Compare the results of __cxa_demangle against the current\n");
-  printf("    demangler.\n");
+static void Usage(const char* prog_name) {
+  printf("usage: %s [-c] [NAME_TO_DEMANGLE...]\n", prog_name);
+  printf("\n");
+  printf("Demangles C++ mangled names if supplied on the command-line, or found\n");
+  printf("reading from stdin otherwise.\n");
+  printf("\n");
+  printf("-c\tCompare against __cxa_demangle\n");
+  printf("\n");
 }
 
-std::string DemangleWithCxa(const char* name) {
+static std::string DemangleWithCxa(const char* name) {
   const char* cxa_demangle = __cxa_demangle(name, nullptr, nullptr, nullptr);
-
   if (cxa_demangle == nullptr) {
     return name;
   }
@@ -54,6 +57,49 @@
   return demangled_str;
 }
 
+static void Compare(const char* name, const std::string& demangled_name) {
+  std::string cxa_demangled_name(DemangleWithCxa(name));
+  if (cxa_demangled_name != demangled_name) {
+    printf("\nMismatch!\n");
+    printf("\tmangled name: %s\n", name);
+    printf("\tour demangle: %s\n", demangled_name.c_str());
+    printf("\tcxa demangle: %s\n", cxa_demangled_name.c_str());
+    exit(1);
+  }
+}
+
+static int Filter(bool compare) {
+  char* line = nullptr;
+  size_t line_length = 0;
+
+  while ((getline(&line, &line_length, stdin)) != -1) {
+    char* p = line;
+    char* name;
+    while ((name = strstr(p, "_Z")) != nullptr) {
+      // Output anything before the identifier.
+      *name = 0;
+      printf("%s", p);
+      *name = '_';
+
+      // Extract the identifier.
+      p = name;
+      while (*p && (std::isalnum(*p) || *p == '_' || *p == '.' || *p == '$')) ++p;
+
+      // Demangle and output.
+      std::string identifier(name, p);
+      std::string demangled_name = demangle(identifier.c_str());
+      printf("%s", demangled_name.c_str());
+
+      if (compare) Compare(identifier.c_str(), demangled_name);
+    }
+    // Output anything after the last identifier.
+    printf("%s", p);
+  }
+
+  free(line);
+  return 0;
+}
+
 int main(int argc, char** argv) {
 #ifdef __BIONIC__
   const char* prog_name = getprogname();
@@ -67,31 +113,21 @@
     if (opt_char == 'c') {
       compare = true;
     } else {
-      usage(prog_name);
+      Usage(prog_name);
       return 1;
     }
   }
-  if (optind >= argc || argc - optind != 1) {
-    printf("Must supply a single argument.\n\n");
-    usage(prog_name);
-    return 1;
-  }
-  const char* name = argv[optind];
 
-  std::string demangled_name = demangle(name);
+  // With no arguments, act as a filter.
+  if (optind == argc) return Filter(compare);
 
-  printf("%s\n", demangled_name.c_str());
+  // Otherwise demangle each argument.
+  while (optind < argc) {
+    const char* name = argv[optind++];
+    std::string demangled_name = demangle(name);
+    printf("%s\n", demangled_name.c_str());
 
-  if (compare) {
-    std::string cxa_demangle_str(DemangleWithCxa(name));
-
-    if (cxa_demangle_str != demangled_name) {
-      printf("Mismatch\n");
-      printf("cxa demangle: %s\n", cxa_demangle_str.c_str());
-      return 1;
-    } else {
-      printf("Match\n");
-    }
+    if (compare) Compare(name, demangled_name);
   }
   return 0;
 }
diff --git a/demangle/demangle_fuzzer.cpp b/demangle/demangle_fuzzer.cpp
new file mode 100644
index 0000000..83fafc2
--- /dev/null
+++ b/demangle/demangle_fuzzer.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+
+#include "Demangler.h"
+
+extern "C" void LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  std::vector<char> data_str(size + 1);
+  memcpy(data_str.data(), data, size);
+  data_str[size] = '\0';
+
+  Demangler demangler;
+  std::string demangled_name = demangler.Parse(data_str.data());
+  if (size != 0 && data_str[0] != '\0' && demangled_name.empty()) {
+    abort();
+  }
+}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 5610cc0..dd8bad9 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -14,10 +14,12 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-fastboot_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
+include $(LOCAL_PATH)/../platform_tools_tool_version.mk
 
 include $(CLEAR_VARS)
 
+LOCAL_CFLAGS += -DFASTBOOT_VERSION="\"$(tool_version)\""
+
 LOCAL_C_INCLUDES := \
   $(LOCAL_PATH)/../adb \
   $(LOCAL_PATH)/../mkbootimg \
@@ -38,8 +40,7 @@
 LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
-
-LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
+LOCAL_REQUIRED_MODULES := mke2fs e2fsdroid
 
 LOCAL_SRC_FILES_linux := usb_linux.cpp
 LOCAL_STATIC_LIBRARIES_linux := libselinux
@@ -51,7 +52,7 @@
 
 LOCAL_SRC_FILES_windows := usb_windows.cpp
 LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
-LOCAL_REQUIRED_MODULES_windows := AdbWinApi
+LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
 LOCAL_LDLIBS_windows := -lws2_32
 LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
 
@@ -85,6 +86,8 @@
 include $(BUILD_HOST_EXECUTABLE)
 
 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
diff --git a/fastboot/README.md b/fastboot/README.md
index 022d34b..ec7dcb4 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -126,6 +126,16 @@
                        space in RAM or "FAIL" if not.  The size of
                        the download is remembered.
 
+    upload             Read data from memory which was staged by the last
+                       command, e.g. an oem command.  The client will reply
+                       with "DATA%08x" if it is ready to send %08x bytes of
+                       data.  If no data was staged in the last command,
+                       the client must reply with "FAIL".  After the client
+                       successfully sends %08x bytes, the client shall send
+                       a single packet starting with "OKAY".  Clients
+                       should not support "upload" unless it supports an
+                       oem command that requires "upload" capabilities.
+
     verify:%08x        Send a digital signature to verify the downloaded
                        data.  Required if the bootloader is "secure"
                        otherwise "flash" and "boot" will be ignored.
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 728dcb8..7e10cc9 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -44,6 +44,8 @@
 #define OP_NOTICE     4
 #define OP_DOWNLOAD_SPARSE 5
 #define OP_WAIT_FOR_DISCONNECT 6
+#define OP_DOWNLOAD_FD 7
+#define OP_UPLOAD 8
 
 typedef struct Action Action;
 
@@ -56,6 +58,7 @@
     char cmd[CMD_SIZE];
     const char* prod;
     void* data;
+    int fd;
 
     // The protocol only supports 32-bit sizes, so you'll have to break
     // anything larger into chunks.
@@ -142,7 +145,20 @@
     a->msg = mkmsg("erasing '%s'", ptn);
 }
 
-void fb_queue_flash(const char *ptn, void *data, unsigned sz)
+void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz)
+{
+    Action *a;
+
+    a = queue_action(OP_DOWNLOAD_FD, "");
+    a->fd = fd;
+    a->size = sz;
+    a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
+
+    a = queue_action(OP_COMMAND, "flash:%s", ptn);
+    a->msg = mkmsg("writing '%s'", ptn);
+}
+
+void fb_queue_flash(const char *ptn, void *data, uint32_t sz)
 {
     Action *a;
 
@@ -155,7 +171,7 @@
     a->msg = mkmsg("writing '%s'", ptn);
 }
 
-void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, unsigned sz, size_t current,
+void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
                            size_t total) {
     Action *a;
 
@@ -242,6 +258,12 @@
     return cb_check(a, status, resp, 1);
 }
 
+static char* xstrdup(const char* s) {
+    char* result = strdup(s);
+    if (!result) die("out of memory");
+    return result;
+}
+
 void fb_queue_require(const char *prod, const char *var,
                       bool invert, size_t nvalues, const char **value)
 {
@@ -260,16 +282,14 @@
         fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
         return status;
     }
-    fprintf(stderr, "%s: %s\n", (char*) a->data, resp);
+    fprintf(stderr, "%s: %s\n", static_cast<const char*>(a->data), resp);
+    free(static_cast<char*>(a->data));
     return 0;
 }
 
-void fb_queue_display(const char *var, const char *prettyname)
-{
-    Action *a;
-    a = queue_action(OP_QUERY, "getvar:%s", var);
-    a->data = strdup(prettyname);
-    if (a->data == nullptr) die("out of memory");
+void fb_queue_display(const char* var, const char* prettyname) {
+    Action* a = queue_action(OP_QUERY, "getvar:%s", var);
+    a->data = xstrdup(prettyname);
     a->func = cb_display;
 }
 
@@ -282,11 +302,9 @@
     return 0;
 }
 
-void fb_queue_query_save(const char *var, char *dest, unsigned dest_size)
-{
-    Action *a;
-    a = queue_action(OP_QUERY, "getvar:%s", var);
-    a->data = (void *)dest;
+void fb_queue_query_save(const char* var, char* dest, uint32_t dest_size) {
+    Action* a = queue_action(OP_QUERY, "getvar:%s", var);
+    a->data = dest;
     a->size = dest_size;
     a->func = cb_save;
 }
@@ -309,7 +327,7 @@
     a->msg = msg;
 }
 
-void fb_queue_download(const char *name, void *data, unsigned size)
+void fb_queue_download(const char *name, void *data, uint32_t size)
 {
     Action *a = queue_action(OP_DOWNLOAD, "");
     a->data = data;
@@ -317,8 +335,22 @@
     a->msg = mkmsg("downloading '%s'", name);
 }
 
-void fb_queue_notice(const char *notice)
+void fb_queue_download_fd(const char *name, int fd, uint32_t sz)
 {
+    Action *a;
+    a = queue_action(OP_DOWNLOAD_FD, "");
+    a->fd = fd;
+    a->size = sz;
+    a->msg = mkmsg("sending '%s' (%d KB)", name, sz / 1024);
+}
+
+void fb_queue_upload(const char* outfile) {
+    Action* a = queue_action(OP_UPLOAD, "");
+    a->data = xstrdup(outfile);
+    a->msg = mkmsg("uploading '%s'", outfile);
+}
+
+void fb_queue_notice(const char* notice) {
     Action *a = queue_action(OP_NOTICE, "");
     a->data = (void*) notice;
 }
@@ -328,11 +360,11 @@
     queue_action(OP_WAIT_FOR_DISCONNECT, "");
 }
 
-int fb_execute_queue(Transport* transport)
+int64_t fb_execute_queue(Transport* transport)
 {
     Action *a;
     char resp[FB_RESPONSE_SZ+1];
-    int status = 0;
+    int64_t status = 0;
 
     a = action_list;
     if (!a)
@@ -351,6 +383,10 @@
             status = fb_download_data(transport, a->data, a->size);
             status = a->func(a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
+        } else if (a->op == OP_DOWNLOAD_FD) {
+            status = fb_download_data_fd(transport, a->fd, a->size);
+            status = a->func(a, status, status ? fb_get_error().c_str() : "");
+            if (status) break;
         } else if (a->op == OP_COMMAND) {
             status = fb_command(transport, a->cmd);
             status = a->func(a, status, status ? fb_get_error().c_str() : "");
@@ -367,6 +403,9 @@
             if (status) break;
         } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
             transport->WaitForDisconnect();
+        } else if (a->op == OP_UPLOAD) {
+            status = fb_upload_data(transport, reinterpret_cast<char*>(a->data));
+            status = a->func(a, status, status ? fb_get_error().c_str() : "");
         } else {
             die("bogus action");
         }
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 2927b16..271ca95 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -26,8 +26,6 @@
  * SUCH DAMAGE.
  */
 
-#define _LARGEFILE64_SOURCE
-
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -55,6 +53,8 @@
 #include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/test_utils.h>
+#include <android-base/unique_fd.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
 
@@ -67,6 +67,8 @@
 #include "udp.h"
 #include "usb.h"
 
+using android::base::unique_fd;
+
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
@@ -74,10 +76,13 @@
 char cur_product[FB_RESPONSE_SZ + 1];
 
 static const char* serial = nullptr;
-static const char* product = nullptr;
 static const char* cmdline = nullptr;
 static unsigned short vendor_id = 0;
 static int long_listing = 0;
+// Don't resparse files in too-big chunks.
+// libsparse will support INT_MAX, but this results in large allocations, so
+// let's keep it at 1GB to avoid memory pressure on the host.
+static constexpr int64_t RESPARSE_LIMIT = 1 * 1024 * 1024 * 1024;
 static int64_t sparse_limit = -1;
 static int64_t target_sparse_limit = -1;
 
@@ -91,7 +96,7 @@
 static const std::string convert_fbe_marker_filename("convert_fbe");
 
 enum fb_buffer_type {
-    FB_BUFFER,
+    FB_BUFFER_FD,
     FB_BUFFER_SPARSE,
 };
 
@@ -99,63 +104,51 @@
     enum fb_buffer_type type;
     void* data;
     int64_t sz;
+    int fd;
 };
 
 static struct {
-    char img_name[17];
-    char sig_name[17];
-    char part_name[9];
+    const char* nickname;
+    const char* img_name;
+    const char* sig_name;
+    const char* part_name;
     bool is_optional;
     bool is_secondary;
 } images[] = {
-    {"boot.img", "boot.sig", "boot", false, false},
-    {"boot_other.img", "boot.sig", "boot", true, true},
-    {"recovery.img", "recovery.sig", "recovery", true, false},
-    {"system.img", "system.sig", "system", false, false},
-    {"system_other.img", "system.sig", "system", true, true},
-    {"vendor.img", "vendor.sig", "vendor", true, false},
-    {"vendor_other.img", "vendor.sig", "vendor", true, true},
+    // clang-format off
+    { "boot",     "boot.img",         "boot.sig",     "boot",     false, false },
+    { nullptr,    "boot_other.img",   "boot.sig",     "boot",     true,  true  },
+    { "dtbo",     "dtbo.img",         "dtbo.sig",     "dtbo",     true,  false },
+    { "dts",      "dt.img",           "dt.sig",       "dts",      true,  false },
+    { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  false },
+    { "system",   "system.img",       "system.sig",   "system",   false, false },
+    { nullptr,    "system_other.img", "system.sig",   "system",   true,  true  },
+    { "vbmeta",   "vbmeta.img",       "vbmeta.sig",   "vbmeta",   true,  false },
+    { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  false },
+    { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  true  },
+    // clang-format on
 };
 
-static std::string find_item_given_name(const char* img_name, const char* product) {
-    if(product) {
-        std::string path = android::base::GetExecutablePath();
-        path.erase(path.find_last_of('/'));
-        return android::base::StringPrintf("%s/../../../target/product/%s/%s",
-                                           path.c_str(), product, img_name);
-    }
-
-    char *dir = getenv("ANDROID_PRODUCT_OUT");
+static std::string find_item_given_name(const char* img_name) {
+    char* dir = getenv("ANDROID_PRODUCT_OUT");
     if (dir == nullptr || dir[0] == '\0') {
-        die("neither -p product specified nor ANDROID_PRODUCT_OUT set");
+        die("ANDROID_PRODUCT_OUT not set");
     }
-
     return android::base::StringPrintf("%s/%s", dir, img_name);
 }
 
-std::string find_item(const char* item, const char* product) {
-    const char *fn;
-
-    if (!strcmp(item,"boot")) {
-        fn = "boot.img";
-    } else if(!strcmp(item,"recovery")) {
-        fn = "recovery.img";
-    } else if(!strcmp(item,"system")) {
-        fn = "system.img";
-    } else if(!strcmp(item,"vendor")) {
-        fn = "vendor.img";
-    } else if(!strcmp(item,"userdata")) {
-        fn = "userdata.img";
-    } else if(!strcmp(item,"cache")) {
-        fn = "cache.img";
-    } else if(!strcmp(item,"info")) {
-        fn = "android-info.txt";
-    } else {
-        fprintf(stderr,"unknown partition '%s'\n", item);
-        return "";
+static std::string find_item(const std::string& item) {
+    for (size_t i = 0; i < arraysize(images); ++i) {
+        if (images[i].nickname && item == images[i].nickname) {
+            return find_item_given_name(images[i].img_name);
+        }
     }
 
-    return find_item_given_name(fn, product);
+    if (item == "userdata") return find_item_given_name("userdata.img");
+    if (item == "cache") return find_item_given_name("cache.img");
+
+    fprintf(stderr, "unknown partition '%s'\n", item.c_str());
+    return "";
 }
 
 static int64_t get_file_size(int fd) {
@@ -314,8 +307,21 @@
     usb_open(list_devices_callback);
 }
 
-static void usage() {
-    fprintf(stderr,
+static void syntax_error(const char* fmt, ...) {
+    fprintf(stderr, "fastboot: usage: ");
+
+    va_list ap;
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+
+    fprintf(stderr, "\n");
+    exit(1);
+}
+
+static int show_help() {
+    // clang-format off
+    fprintf(stdout,
 /*           1234567890123456789012345678901234567890123456789012345678901234567890123456 */
             "usage: fastboot [ <option> ] <command>\n"
             "\n"
@@ -353,13 +359,20 @@
             "  set_active <slot>                        Sets the active slot. If slots are\n"
             "                                           not supported, this does nothing.\n"
             "  boot <kernel> [ <ramdisk> [ <second> ] ] Download and boot kernel.\n"
-            "  flash:raw boot <kernel> [ <ramdisk> [ <second> ] ]\n"
+            "  flash:raw <bootable-partition> <kernel> [ <ramdisk> [ <second> ] ]\n"
             "                                           Create bootimage and flash it.\n"
             "  devices [-l]                             List all connected devices [with\n"
             "                                           device paths].\n"
             "  continue                                 Continue with autoboot.\n"
             "  reboot [bootloader|emergency]            Reboot device [into bootloader or emergency mode].\n"
             "  reboot-bootloader                        Reboot device into bootloader.\n"
+            "  oem <parameter1> ... <parameterN>        Executes oem specific command.\n"
+            "  stage <infile>                           Sends contents of <infile> to stage for\n"
+            "                                           the next command. Supported only on\n"
+            "                                           Android Things devices.\n"
+            "  get_staged <outfile>                     Receives data to <outfile> staged by the\n"
+            "                                           last command. Supported only on Android\n"
+            "                                           Things devices.\n"
             "  help                                     Show this help message.\n"
             "\n"
             "options:\n"
@@ -372,7 +385,6 @@
             "                                           For ethernet, provide an address in the\n"
             "                                           form <protocol>:<hostname>[:port] where\n"
             "                                           <protocol> is either tcp or udp.\n"
-            "  -p <product>                             Specify product name.\n"
             "  -c <cmdline>                             Override kernel commandline.\n"
             "  -i <vendor id>                           Specify a custom USB vendor id.\n"
             "  -b, --base <base_addr>                   Specify a custom kernel base\n"
@@ -416,31 +428,22 @@
             "  --version                                Display version.\n"
             "  -h, --help                               show this message.\n"
         );
+    // clang-format off
+    return 0;
 }
 
-static void* load_bootable_image(const char* kernel, const char* ramdisk,
-                                 const char* secondstage, int64_t* sz,
+static void* load_bootable_image(const std::string& kernel, const std::string& ramdisk,
+                                 const std::string& second_stage, int64_t* sz,
                                  const char* cmdline) {
-    if (kernel == nullptr) {
-        fprintf(stderr, "no image specified\n");
-        return 0;
-    }
-
     int64_t ksize;
-    void* kdata = load_file(kernel, &ksize);
-    if (kdata == nullptr) {
-        fprintf(stderr, "cannot load '%s': %s\n", kernel, strerror(errno));
-        return 0;
-    }
+    void* kdata = load_file(kernel.c_str(), &ksize);
+    if (kdata == nullptr) die("cannot load '%s': %s\n", kernel.c_str(), strerror(errno));
 
     // Is this actually a boot image?
-    if(!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+    if (!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
         if (cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
 
-        if (ramdisk) {
-            fprintf(stderr, "cannot boot a boot.img *and* ramdisk\n");
-            return 0;
-        }
+        if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk\n");
 
         *sz = ksize;
         return kdata;
@@ -448,22 +451,16 @@
 
     void* rdata = nullptr;
     int64_t rsize = 0;
-    if (ramdisk) {
-        rdata = load_file(ramdisk, &rsize);
-        if (rdata == nullptr) {
-            fprintf(stderr,"cannot load '%s': %s\n", ramdisk, strerror(errno));
-            return  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));
     }
 
     void* sdata = nullptr;
     int64_t ssize = 0;
-    if (secondstage) {
-        sdata = load_file(secondstage, &ssize);
-        if (sdata == nullptr) {
-            fprintf(stderr,"cannot load '%s': %s\n", secondstage, strerror(errno));
-            return  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));
     }
 
     fprintf(stderr,"creating boot image...\n");
@@ -472,10 +469,8 @@
                       rdata, rsize, ramdisk_offset,
                       sdata, ssize, second_offset,
                       page_size, base_addr, tags_offset, &bsize);
-    if (bdata == nullptr) {
-        fprintf(stderr,"failed to create boot.img\n");
-        return 0;
-    }
+    if (bdata == nullptr) die("failed to create boot.img\n");
+
     if (cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
     fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", bsize);
     *sz = bsize;
@@ -483,8 +478,7 @@
     return bdata;
 }
 
-static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, int64_t* sz)
-{
+static void* unzip_file(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) {
@@ -494,6 +488,7 @@
 
     *sz = zip_entry.uncompressed_length;
 
+    fprintf(stderr, "extracting %s (%" PRId64 " MB)...\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);
@@ -542,22 +537,39 @@
     return "";
 }
 
+static int make_temporary_fd() {
+    // TODO: reimplement to avoid leaking a FILE*.
+    return fileno(tmpfile());
+}
+
 #else
 
+static std::string make_temporary_template() {
+    const char* tmpdir = getenv("TMPDIR");
+    if (tmpdir == nullptr) tmpdir = P_tmpdir;
+    return std::string(tmpdir) + "/fastboot_userdata_XXXXXX";
+}
+
 static std::string make_temporary_directory() {
-    const char *tmpdir = getenv("TMPDIR");
-    if (tmpdir == nullptr) {
-        tmpdir = P_tmpdir;
-    }
-    std::string result = std::string(tmpdir) + "/fastboot_userdata_XXXXXX";
-    if (mkdtemp(&result[0]) == NULL) {
-        fprintf(stderr, "Unable to create temporary directory: %s\n",
-            strerror(errno));
+    std::string result(make_temporary_template());
+    if (mkdtemp(&result[0]) == nullptr) {
+        fprintf(stderr, "Unable to create temporary directory: %s\n", strerror(errno));
         return "";
     }
     return result;
 }
 
+static int make_temporary_fd() {
+    std::string path_template(make_temporary_template());
+    int fd = mkstemp(&path_template[0]);
+    if (fd == -1) {
+        fprintf(stderr, "Unable to create temporary file: %s\n", strerror(errno));
+        return -1;
+    }
+    unlink(path_template.c_str());
+    return fd;
+}
+
 #endif
 
 static std::string create_fbemarker_tmpdir() {
@@ -591,9 +603,9 @@
     }
 }
 
-static int unzip_to_file(ZipArchiveHandle zip, char* entry_name) {
-    FILE* fp = tmpfile();
-    if (fp == nullptr) {
+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;
@@ -603,21 +615,20 @@
     ZipEntry zip_entry;
     if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name);
-        fclose(fp);
         return -1;
     }
 
-    int fd = fileno(fp);
+    fprintf(stderr, "extracting %s (%" PRIu32 " MB)...\n", entry_name,
+            zip_entry.uncompressed_length / 1024 / 1024);
     int error = ExtractEntryToFile(zip, &zip_entry, fd);
     if (error != 0) {
         fprintf(stderr, "failed to extract '%s': %s\n", entry_name, ErrorCodeString(error));
-        fclose(fp);
         return -1;
     }
 
     lseek(fd, 0, SEEK_SET);
     // TODO: We're leaking 'fp' here.
-    return fd;
+    return fd.release();
 }
 
 static char *strip(char *s)
@@ -786,7 +797,7 @@
     }
 
     if (size > limit) {
-        return limit;
+        return std::min(limit, RESPARSE_LIMIT);
     }
 
     return 0;
@@ -819,10 +830,9 @@
         buf->type = FB_BUFFER_SPARSE;
         buf->data = s;
     } else {
-        void* data = load_fd(fd, &sz);
-        if (data == nullptr) return -1;
-        buf->type = FB_BUFFER;
-        buf->data = data;
+        buf->type = FB_BUFFER_FD;
+        buf->data = nullptr;
+        buf->fd = fd;
         buf->sz = sz;
     }
 
@@ -830,11 +840,22 @@
 }
 
 static bool load_buf(Transport* transport, const char* fname, struct fastboot_buffer* buf) {
-    int fd = open(fname, O_RDONLY | O_BINARY);
+    unique_fd fd(TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_BINARY)));
+
     if (fd == -1) {
         return false;
     }
-    return load_buf_fd(transport, fd, buf);
+
+    struct stat s;
+    if (fstat(fd, &s)) {
+        return false;
+    }
+    if (!S_ISREG(s.st_mode)) {
+        errno = S_ISDIR(s.st_mode) ? EISDIR : EINVAL;
+        return false;
+    }
+
+    return load_buf_fd(transport, fd.release(), buf);
 }
 
 static void flash_buf(const char *pname, struct fastboot_buffer *buf)
@@ -857,9 +878,8 @@
             }
             break;
         }
-
-        case FB_BUFFER:
-            fb_queue_flash(pname, buf->data, buf->sz);
+        case FB_BUFFER_FD:
+            fb_queue_flash_fd(pname, buf->fd, buf->sz);
             break;
         default:
             die("unknown buffer type: %d", buf->type);
@@ -1037,9 +1057,9 @@
     flash_buf(pname, &buf);
 }
 
-static void do_update_signature(ZipArchiveHandle zip, char* fn) {
+static void do_update_signature(ZipArchiveHandle zip, const char* filename) {
     int64_t sz;
-    void* data = unzip_file(zip, fn, &sz);
+    void* data = unzip_file(zip, filename, &sz);
     if (data == nullptr) return;
     fb_queue_download("signature", data, sz);
     fb_queue_command("signature", "installing signature");
@@ -1131,7 +1151,7 @@
             }
             flash_buf(partition.c_str(), &buf);
             /* not closing the fd here since the sparse code keeps the fd around
-             * but hasn't mmaped data yet. The tmpfile will get cleaned up when the
+             * but hasn't mmaped data yet. The temporary file will get cleaned up when the
              * program exits.
              */
         };
@@ -1166,7 +1186,7 @@
 
     fb_queue_query_save("product", cur_product, sizeof(cur_product));
 
-    fname = find_item("info", product);
+    fname = find_item_given_name("android-info.txt");
     if (fname.empty()) die("cannot find android-info.txt");
 
     int64_t sz;
@@ -1198,7 +1218,7 @@
             slot = slot_override.c_str();
         }
         if (!slot) continue;
-        fname = find_item_given_name(images[i].img_name, product);
+        fname = find_item_given_name(images[i].img_name);
         fastboot_buffer buf;
         if (!load_buf(transport, fname.c_str(), &buf)) {
             if (images[i].is_optional) continue;
@@ -1222,41 +1242,33 @@
     }
 }
 
-#define skip(n) do { argc -= (n); argv += (n); } while (0)
-#define require(n) do { if (argc < (n)) {usage(); exit(1);}} while (0)
-
-static int do_bypass_unlock_command(int argc, char **argv)
-{
-    if (argc <= 2) return 0;
-    skip(2);
-
-    /*
-     * Process unlock_bootloader, we have to load the message file
-     * and send that to the remote device.
-     */
-    require(1);
-
-    int64_t sz;
-    void* data = load_file(*argv, &sz);
-    if (data == nullptr) die("could not load '%s': %s", *argv, strerror(errno));
-    fb_queue_download("unlock_message", data, sz);
-    fb_queue_command("flashing unlock_bootloader", "unlocking bootloader");
-    skip(1);
-    return 0;
+static std::string next_arg(std::vector<std::string>* args) {
+    if (args->empty()) syntax_error("expected argument");
+    std::string result = args->front();
+    args->erase(args->begin());
+    return result;
 }
 
-static int do_oem_command(int argc, char** argv) {
-    if (argc <= 1) return 0;
+static void do_bypass_unlock_command(std::vector<std::string>* args) {
+    if (args->empty()) syntax_error("missing unlock_bootloader request");
 
-    std::string command;
-    while (argc > 0) {
-        command += *argv;
-        skip(1);
-        if (argc != 0) command += " ";
+    std::string filename = next_arg(args);
+
+    int64_t sz;
+    void* data = load_file(filename.c_str(), &sz);
+    if (data == nullptr) die("could not load '%s': %s", filename.c_str(), strerror(errno));
+    fb_queue_download("unlock_message", data, sz);
+    fb_queue_command("flashing unlock_bootloader", "unlocking bootloader");
+}
+
+static void do_oem_command(const std::string& cmd, std::vector<std::string>* args) {
+    if (args->empty()) syntax_error("empty oem command");
+
+    std::string command(cmd);
+    while (!args->empty()) {
+        command += " " + next_arg(args);
     }
-
     fb_queue_command(command.c_str(), "");
-    return 0;
 }
 
 static int64_t parse_num(const char *arg)
@@ -1332,14 +1344,15 @@
 
 static void fb_perform_format(Transport* transport,
                               const char* partition, int skip_if_not_supported,
-                              const char* type_override, const char* size_override,
+                              const std::string& type_override, const std::string& size_override,
                               const std::string& initial_dir) {
     std::string partition_type, partition_size;
 
     struct fastboot_buffer buf;
     const char* errMsg = nullptr;
     const struct fs_generator* gen = nullptr;
-    int fd;
+    TemporaryFile output;
+    unique_fd fd;
 
     unsigned int limit = INT_MAX;
     if (target_sparse_limit > 0 && target_sparse_limit < limit) {
@@ -1353,10 +1366,10 @@
         errMsg = "Can't determine partition type.\n";
         goto failed;
     }
-    if (type_override) {
+    if (!type_override.empty()) {
         if (partition_type != type_override) {
             fprintf(stderr, "Warning: %s type is %s, but %s was requested for formatting.\n",
-                    partition, partition_type.c_str(), type_override);
+                    partition, partition_type.c_str(), type_override.c_str());
         }
         partition_type = type_override;
     }
@@ -1365,10 +1378,10 @@
         errMsg = "Unable to get partition size\n";
         goto failed;
     }
-    if (size_override) {
+    if (!size_override.empty()) {
         if (partition_size != size_override) {
             fprintf(stderr, "Warning: %s size is %s, but %s was requested for formatting.\n",
-                    partition, partition_size.c_str(), size_override);
+                    partition, partition_size.c_str(), size_override.c_str());
         }
         partition_size = size_override;
     }
@@ -1392,21 +1405,23 @@
         return;
     }
 
-    fd = fileno(tmpfile());
-
     unsigned eraseBlkSize, logicalBlkSize;
     eraseBlkSize = fb_get_flash_block_size(transport, "erase-block-size");
     logicalBlkSize = fb_get_flash_block_size(transport, "logical-block-size");
 
-    if (fs_generator_generate(gen, fd, size, initial_dir, eraseBlkSize, logicalBlkSize)) {
+    if (fs_generator_generate(gen, output.path, size, initial_dir,
+            eraseBlkSize, logicalBlkSize)) {
         fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
-        close(fd);
         return;
     }
 
-    if (!load_buf_fd(transport, fd, &buf)) {
+    fd.reset(open(output.path, O_RDONLY));
+    if (fd == -1) {
+        fprintf(stderr, "Cannot open generated image: %s\n", strerror(errno));
+        return;
+    }
+    if (!load_buf_fd(transport, fd.release(), &buf)) {
         fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
-        close(fd);
         return;
     }
     flash_buf(partition, &buf);
@@ -1464,7 +1479,7 @@
     serial = getenv("ANDROID_SERIAL");
 
     while (1) {
-        int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:ha::", longopts, &longindex);
+        int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lc:i:m:ha::", longopts, &longindex);
         if (c < 0) {
             break;
         }
@@ -1482,8 +1497,7 @@
             cmdline = optarg;
             break;
         case 'h':
-            usage();
-            return 1;
+            return show_help();
         case 'i': {
                 char *endptr = nullptr;
                 unsigned long val;
@@ -1504,9 +1518,6 @@
             page_size = (unsigned)strtoul(optarg, nullptr, 0);
             if (!page_size) die("invalid page size");
             break;
-        case 'p':
-            product = optarg;
-            break;
         case 'r':
             ramdisk_offset = strtoul(optarg, 0, 16);
             break;
@@ -1535,7 +1546,8 @@
                 setvbuf(stdout, nullptr, _IONBF, 0);
                 setvbuf(stderr, nullptr, _IONBF, 0);
             } else if (strcmp("version", longopts[longindex].name) == 0) {
-                fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
+                fprintf(stdout, "fastboot version %s\n", FASTBOOT_VERSION);
+                fprintf(stdout, "Installed as %s\n", android::base::GetExecutablePath().c_str());
                 return 0;
             } else if (strcmp("slot", longopts[longindex].name) == 0) {
                 slot_override = std::string(optarg);
@@ -1562,20 +1574,15 @@
     argc -= optind;
     argv += optind;
 
-    if (argc == 0 && !wants_wipe && !wants_set_active) {
-        usage();
-        return 1;
-    }
+    if (argc == 0 && !wants_wipe && !wants_set_active) syntax_error("no command");
 
     if (argc > 0 && !strcmp(*argv, "devices")) {
-        skip(1);
         list_devices();
         return 0;
     }
 
     if (argc > 0 && !strcmp(*argv, "help")) {
-        usage();
-        return 0;
+        return show_help();
     }
 
     Transport* transport = open_device();
@@ -1604,17 +1611,19 @@
         }
     }
 
-    while (argc > 0) {
-        if (!strcmp(*argv, "getvar")) {
-            require(2);
-            fb_queue_display(argv[1], argv[1]);
-            skip(2);
-        } else if(!strcmp(*argv, "erase")) {
-            require(2);
+    std::vector<std::string> args(argv, argv + argc);
+    while (!args.empty()) {
+        std::string command = next_arg(&args);
 
-            auto erase = [&](const std::string &partition) {
+        if (command == "getvar") {
+            std::string variable = next_arg(&args);
+            fb_queue_display(variable.c_str(), variable.c_str());
+        } else if (command == "erase") {
+            std::string partition = next_arg(&args);
+            auto erase = [&](const std::string& partition) {
                 std::string partition_type;
-                if (fb_getvar(transport, std::string("partition-type:") + argv[1], &partition_type) &&
+                if (fb_getvar(transport, std::string("partition-type:") + partition,
+                              &partition_type) &&
                     fs_get_generator(partition_type) != nullptr) {
                     fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
                             partition_type.c_str());
@@ -1622,106 +1631,79 @@
 
                 fb_queue_erase(partition.c_str());
             };
-            do_for_partitions(transport, argv[1], slot_override, erase, true);
-            skip(2);
-        } else if(!strncmp(*argv, "format", strlen("format"))) {
-            char *overrides;
-            char *type_override = nullptr;
-            char *size_override = nullptr;
-            require(2);
-            /*
-             * Parsing for: "format[:[type][:[size]]]"
-             * Some valid things:
-             *  - select ontly the size, and leave default fs type:
-             *    format::0x4000000 userdata
-             *  - default fs type and size:
-             *    format userdata
-             *    format:: userdata
-             */
-            overrides = strchr(*argv, ':');
-            if (overrides) {
-                overrides++;
-                size_override = strchr(overrides, ':');
-                if (size_override) {
-                    size_override[0] = '\0';
-                    size_override++;
-                }
-                type_override = overrides;
-            }
-            if (type_override && !type_override[0]) type_override = nullptr;
-            if (size_override && !size_override[0]) size_override = nullptr;
+            do_for_partitions(transport, partition, slot_override, erase, true);
+        } else if (android::base::StartsWith(command, "format")) {
+            // Parsing for: "format[:[type][:[size]]]"
+            // Some valid things:
+            //  - select only the size, and leave default fs type:
+            //    format::0x4000000 userdata
+            //  - default fs type and size:
+            //    format userdata
+            //    format:: userdata
+            std::vector<std::string> pieces = android::base::Split(command, ":");
+            std::string type_override;
+            if (pieces.size() > 1) type_override = pieces[1].c_str();
+            std::string size_override;
+            if (pieces.size() > 2) size_override = pieces[2].c_str();
 
-            auto format = [&](const std::string &partition) {
+            std::string partition = next_arg(&args);
+
+            auto format = [&](const std::string& partition) {
                 if (erase_first && needs_erase(transport, partition.c_str())) {
                     fb_queue_erase(partition.c_str());
                 }
-                fb_perform_format(transport, partition.c_str(), 0,
-                    type_override, size_override, "");
+                fb_perform_format(transport, partition.c_str(), 0, type_override, size_override,
+                                  "");
             };
-            do_for_partitions(transport, argv[1], slot_override, format, true);
-            skip(2);
-        } else if(!strcmp(*argv, "signature")) {
-            require(2);
-            data = load_file(argv[1], &sz);
-            if (data == nullptr) die("could not load '%s': %s", argv[1], strerror(errno));
+            do_for_partitions(transport, partition.c_str(), slot_override, format, true);
+        } else if (command == "signature") {
+            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");
             fb_queue_download("signature", data, sz);
             fb_queue_command("signature", "installing signature");
-            skip(2);
-        } else if(!strcmp(*argv, "reboot")) {
+        } else if (command == "reboot") {
             wants_reboot = true;
-            skip(1);
-            if (argc > 0) {
-                if (!strcmp(*argv, "bootloader")) {
+
+            if (args.size() == 1) {
+                std::string what = next_arg(&args);
+                if (what == "bootloader") {
                     wants_reboot = false;
                     wants_reboot_bootloader = true;
-                    skip(1);
-                } else if (!strcmp(*argv, "emergency")) {
+                } else if (what == "emergency") {
                     wants_reboot = false;
                     wants_reboot_emergency = true;
-                    skip(1);
+                } else {
+                    syntax_error("unknown reboot target %s", what.c_str());
                 }
+
             }
-            require(0);
-        } else if(!strcmp(*argv, "reboot-bootloader")) {
+            if (!args.empty()) syntax_error("junk after reboot command");
+        } else if (command == "reboot-bootloader") {
             wants_reboot_bootloader = true;
-            skip(1);
-        } else if (!strcmp(*argv, "continue")) {
+        } else if (command == "continue") {
             fb_queue_command("continue", "resuming boot");
-            skip(1);
-        } else if(!strcmp(*argv, "boot")) {
-            char *kname = 0;
-            char *rname = 0;
-            char *sname = 0;
-            skip(1);
-            if (argc > 0) {
-                kname = argv[0];
-                skip(1);
-            }
-            if (argc > 0) {
-                rname = argv[0];
-                skip(1);
-            }
-            if (argc > 0) {
-                sname = argv[0];
-                skip(1);
-            }
-            data = load_bootable_image(kname, rname, sname, &sz, cmdline);
-            if (data == 0) return 1;
+        } else if (command == "boot") {
+            std::string kernel = next_arg(&args);
+            std::string ramdisk;
+            if (!args.empty()) ramdisk = next_arg(&args);
+            std::string second_stage;
+            if (!args.empty()) second_stage = next_arg(&args);
+
+            data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline);
             fb_queue_download("boot.img", data, sz);
             fb_queue_command("boot", "booting");
-        } else if(!strcmp(*argv, "flash")) {
-            char* pname = argv[1];
+        } else if (command == "flash") {
+            std::string pname = next_arg(&args);
+
             std::string fname;
-            require(2);
-            if (argc > 2) {
-                fname = argv[2];
-                skip(3);
+            if (!args.empty()) {
+                fname = next_arg(&args);
             } else {
-                fname = find_item(pname, product);
-                skip(2);
+                fname = find_item(pname);
             }
-            if (fname.empty()) die("cannot determine image filename for '%s'", pname);
+            if (fname.empty()) die("cannot determine image filename for '%s'", pname.c_str());
 
             auto flash = [&](const std::string &partition) {
                 if (erase_first && needs_erase(transport, partition.c_str())) {
@@ -1729,29 +1711,21 @@
                 }
                 do_flash(transport, partition.c_str(), fname.c_str());
             };
-            do_for_partitions(transport, pname, slot_override, flash, true);
-        } else if(!strcmp(*argv, "flash:raw")) {
-            char *kname = argv[2];
-            char *rname = 0;
-            char *sname = 0;
-            require(3);
-            skip(3);
-            if (argc > 0) {
-                rname = argv[0];
-                skip(1);
-            }
-            if (argc > 0) {
-                sname = argv[0];
-                skip(1);
-            }
-            data = load_bootable_image(kname, rname, sname, &sz, cmdline);
-            if (data == 0) die("cannot load bootable image");
-            auto flashraw = [&](const std::string &partition) {
+            do_for_partitions(transport, pname.c_str(), slot_override, flash, true);
+        } else if (command == "flash:raw") {
+            std::string partition = next_arg(&args);
+            std::string kernel = next_arg(&args);
+            std::string ramdisk;
+            if (!args.empty()) ramdisk = next_arg(&args);
+            std::string second_stage;
+            if (!args.empty()) second_stage = next_arg(&args);
+
+            data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline);
+            auto flashraw = [&](const std::string& partition) {
                 fb_queue_flash(partition.c_str(), data, sz);
             };
-            do_for_partitions(transport, argv[1], slot_override, flashraw, true);
-        } else if(!strcmp(*argv, "flashall")) {
-            skip(1);
+            do_for_partitions(transport, partition, slot_override, flashraw, true);
+        } else if (command == "flashall") {
             if (slot_override == "all") {
                 fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
                 do_flashall(transport, slot_override, erase_first, true);
@@ -1759,22 +1733,21 @@
                 do_flashall(transport, slot_override, erase_first, skip_secondary);
             }
             wants_reboot = true;
-        } else if(!strcmp(*argv, "update")) {
+        } else if (command == "update") {
             bool slot_all = (slot_override == "all");
             if (slot_all) {
                 fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
             }
-            if (argc > 1) {
-                do_update(transport, argv[1], slot_override, erase_first, skip_secondary || slot_all);
-                skip(2);
-            } else {
-                do_update(transport, "update.zip", slot_override, erase_first, skip_secondary || slot_all);
-                skip(1);
+            std::string filename = "update.zip";
+            if (!args.empty()) {
+                filename = next_arg(&args);
             }
+            do_update(transport, filename.c_str(), slot_override, erase_first,
+                      skip_secondary || slot_all);
             wants_reboot = true;
-        } else if(!strcmp(*argv, "set_active")) {
-            require(2);
-            std::string slot = verify_slot(transport, std::string(argv[1]), false);
+        } else if (command == "set_active") {
+            std::string slot = verify_slot(transport, next_arg(&args), false);
+
             // Legacy support: verify_slot() removes leading underscores, we need to put them back
             // in for old bootloaders. Legacy bootloaders do not have the slot-count variable but
             // do have slot-suffixes.
@@ -1784,28 +1757,36 @@
                 slot = "_" + slot;
             }
             fb_set_active(slot.c_str());
-            skip(2);
-        } else if(!strcmp(*argv, "oem")) {
-            argc = do_oem_command(argc, argv);
-        } else if(!strcmp(*argv, "flashing")) {
-            if (argc == 2 && (!strcmp(*(argv+1), "unlock") ||
-                              !strcmp(*(argv+1), "lock") ||
-                              !strcmp(*(argv+1), "unlock_critical") ||
-                              !strcmp(*(argv+1), "lock_critical") ||
-                              !strcmp(*(argv+1), "get_unlock_ability") ||
-                              !strcmp(*(argv+1), "get_unlock_bootloader_nonce") ||
-                              !strcmp(*(argv+1), "lock_bootloader"))) {
-                argc = do_oem_command(argc, argv);
-            } else
-            if (argc == 3 && !strcmp(*(argv+1), "unlock_bootloader")) {
-                argc = do_bypass_unlock_command(argc, argv);
+        } else if (command == "stage") {
+            std::string filename = next_arg(&args);
+
+            struct fastboot_buffer buf;
+            if (!load_buf(transport, filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
+                die("cannot load '%s'", filename.c_str());
+            }
+            fb_queue_download_fd(filename.c_str(), buf.fd, buf.sz);
+        } else if (command == "get_staged") {
+            std::string filename = next_arg(&args);
+            fb_queue_upload(filename.c_str());
+        } else if (command == "oem") {
+            do_oem_command("oem", &args);
+        } else if (command == "flashing") {
+            if (args.empty()) {
+                syntax_error("missing 'flashing' command");
+            } else if (args.size() == 1 && (args[0] == "unlock" || args[0] == "lock" ||
+                                            args[0] == "unlock_critical" ||
+                                            args[0] == "lock_critical" ||
+                                            args[0] == "get_unlock_ability" ||
+                                            args[0] == "get_unlock_bootloader_nonce" ||
+                                            args[0] == "lock_bootloader")) {
+                do_oem_command("flashing", &args);
+            } else if (args.size() == 2 && args[0] == "unlock_bootloader") {
+                do_bypass_unlock_command(&args);
             } else {
-              usage();
-              return 1;
+                syntax_error("unknown 'flashing' command %s", args[0].c_str());
             }
         } else {
-            usage();
-            return 1;
+            syntax_error("unknown command %s", command.c_str());
         }
     }
 
@@ -1818,17 +1799,17 @@
             if (initial_userdata_dir.empty()) {
                 return 1;
             }
-            fb_perform_format(transport, "userdata", 1, nullptr, nullptr, initial_userdata_dir);
+            fb_perform_format(transport, "userdata", 1, "", "", initial_userdata_dir);
             delete_fbemarker_tmpdir(initial_userdata_dir);
         } else {
-            fb_perform_format(transport, "userdata", 1, nullptr, nullptr, "");
+            fb_perform_format(transport, "userdata", 1, "", "", "");
         }
 
         std::string cache_type;
         if (fb_getvar(transport, "partition-type:cache", &cache_type) && !cache_type.empty()) {
             fprintf(stderr, "wiping cache...\n");
             fb_queue_erase("cache");
-            fb_perform_format(transport, "cache", 1, nullptr, nullptr, "");
+            fb_perform_format(transport, "cache", 1, "", "", "");
         }
     }
     if (wants_set_active) {
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 6699b6a..e3c60ae 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -41,8 +41,10 @@
 /* protocol.c - fastboot protocol */
 int fb_command(Transport* transport, const char* cmd);
 int fb_command_response(Transport* transport, const char* cmd, char* response);
-int fb_download_data(Transport* transport, const void* data, uint32_t size);
+int64_t fb_download_data(Transport* transport, const void* data, uint32_t size);
+int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size);
 int fb_download_data_sparse(Transport* transport, struct sparse_file* s);
+int64_t fb_upload_data(Transport* transport, const char* outfile);
 const std::string fb_get_error();
 
 #define FB_COMMAND_SZ 64
@@ -51,6 +53,7 @@
 /* engine.c - high level command queue engine */
 bool fb_getvar(Transport* transport, const std::string& key, std::string* value);
 void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
+void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz);
 void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
                            size_t total);
 void fb_queue_erase(const char *ptn);
@@ -62,9 +65,11 @@
 void fb_queue_reboot(void);
 void fb_queue_command(const char *cmd, const char *msg);
 void fb_queue_download(const char *name, void *data, uint32_t size);
+void fb_queue_download_fd(const char *name, int fd, uint32_t sz);
+void fb_queue_upload(const char* outfile);
 void fb_queue_notice(const char *notice);
 void fb_queue_wait_for_disconnect(void);
-int fb_execute_queue(Transport* transport);
+int64_t fb_execute_queue(Transport* transport);
 void fb_set_active(const char *slot);
 
 /* util stuff */
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 5d9ccfe..4a4a7c0 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -4,34 +4,166 @@
 #include "make_f2fs.h"
 
 #include <errno.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#ifndef WIN32
+#include <sys/wait.h>
+#else
+#include <tchar.h>
+#include <windows.h>
+#endif
 #include <unistd.h>
+#include <vector>
 
+#include <android-base/errors.h>
+#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>
 
-static int generate_ext4_image(int fd, long long partSize, const std::string& initial_dir,
-                                       unsigned eraseBlkSize, unsigned logicalBlkSize)
-{
-    if (initial_dir.empty()) {
-        make_ext4fs_sparse_fd_align(fd, partSize, NULL, NULL, eraseBlkSize, logicalBlkSize);
-    } else {
-        make_ext4fs_sparse_fd_directory_align(fd, partSize, NULL, NULL, initial_dir.c_str(),
-                                              eraseBlkSize, logicalBlkSize);
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+#ifdef WIN32
+static int exec_e2fs_cmd(const char* path, char* const argv[]) {
+    std::string cmd;
+    int i = 0;
+    while (argv[i] != nullptr) {
+        cmd += argv[i++];
+        cmd += " ";
     }
+    cmd = cmd.substr(0, cmd.size() - 1);
+
+    STARTUPINFO si;
+    PROCESS_INFORMATION pi;
+    DWORD exit_code = 0;
+
+    ZeroMemory(&si, sizeof(si));
+    si.cb = sizeof(si);
+    ZeroMemory(&pi, sizeof(pi));
+
+    SetEnvironmentVariableA("MKE2FS_CONFIG", "");
+
+    if (!CreateProcessA(nullptr,                         // No module name (use command line)
+                        const_cast<char*>(cmd.c_str()),  // Command line
+                        nullptr,                         // Process handle not inheritable
+                        nullptr,                         // Thread handle not inheritable
+                        FALSE,                           // Set handle inheritance to FALSE
+                        0,                               // No creation flags
+                        nullptr,                         // Use parent's environment block
+                        nullptr,                         // Use parent's starting directory
+                        &si,                             // Pointer to STARTUPINFO structure
+                        &pi)                             // Pointer to PROCESS_INFORMATION structure
+    ) {
+        fprintf(stderr, "CreateProcess failed: %s\n",
+                android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        return -1;
+    }
+
+    WaitForSingleObject(pi.hProcess, INFINITE);
+
+    GetExitCodeProcess(pi.hProcess, &exit_code);
+
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+
+    return exit_code != 0;
+}
+#else
+static int exec_e2fs_cmd(const char* path, char* const argv[]) {
+    int status;
+    pid_t child;
+    if ((child = fork()) == 0) {
+        setenv("MKE2FS_CONFIG", "", 1);
+        execvp(path, argv);
+        _exit(EXIT_FAILURE);
+    }
+    if (child < 0) {
+        fprintf(stderr, "%s failed with fork %s\n", path, strerror(errno));
+        return -1;
+    }
+    if (TEMP_FAILURE_RETRY(waitpid(child, &status, 0)) == -1) {
+        fprintf(stderr, "%s failed with waitpid %s\n", path, strerror(errno));
+        return -1;
+    }
+    int ret = -1;
+    if (WIFEXITED(status)) {
+        ret = WEXITSTATUS(status);
+        if (ret != 0) {
+            fprintf(stderr, "%s failed with status %d\n", path, ret);
+        }
+    }
+    return ret;
+}
+#endif
+
+static int generate_ext4_image(const char* fileName, long long partSize,
+                               const std::string& initial_dir, unsigned eraseBlkSize,
+                               unsigned logicalBlkSize) {
+    static constexpr int block_size = 4096;
+    const std::string exec_dir = android::base::GetExecutableDirectory();
+
+    const std::string mke2fs_path = exec_dir + "/mke2fs";
+    std::vector<const char*> mke2fs_args = {mke2fs_path.c_str(), "-t", "ext4", "-b"};
+
+    std::string block_size_str = std::to_string(block_size);
+    mke2fs_args.push_back(block_size_str.c_str());
+
+    std::string ext_attr = "android_sparse";
+    if (eraseBlkSize != 0 && logicalBlkSize != 0) {
+        int raid_stride = logicalBlkSize / block_size;
+        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;
+        ext_attr += StringPrintf(",stride=%d,stripe-width=%d", raid_stride, raid_stripe_width);
+    }
+    mke2fs_args.push_back("-E");
+    mke2fs_args.push_back(ext_attr.c_str());
+    mke2fs_args.push_back(fileName);
+
+    std::string size_str = std::to_string(partSize / block_size);
+    mke2fs_args.push_back(size_str.c_str());
+    mke2fs_args.push_back(nullptr);
+
+    int ret = exec_e2fs_cmd(mke2fs_args[0], const_cast<char**>(mke2fs_args.data()));
+    if (ret != 0) {
+        fprintf(stderr, "mke2fs failed: %d\n", ret);
+        return -1;
+    }
+
+    if (initial_dir.empty()) {
+        return 0;
+    }
+
+    const std::string e2fsdroid_path = exec_dir + "/e2fsdroid";
+    std::vector<const char*> e2fsdroid_args = {e2fsdroid_path.c_str(), "-f", initial_dir.c_str(),
+                                               fileName, nullptr};
+
+    ret = exec_e2fs_cmd(e2fsdroid_args[0], const_cast<char**>(e2fsdroid_args.data()));
+    if (ret != 0) {
+        fprintf(stderr, "e2fsdroid failed: %d\n", ret);
+        return -1;
+    }
+
     return 0;
 }
 
 #ifdef USE_F2FS
-static int generate_f2fs_image(int fd, long long partSize, const std::string& initial_dir,
+static int generate_f2fs_image(const char* fileName, long long partSize, const std::string& initial_dir,
                                unsigned /* unused */, unsigned /* unused */)
 {
     if (!initial_dir.empty()) {
-        fprintf(stderr, "Unable to set initial directory on F2FS filesystem\n");
+        fprintf(stderr, "Unable to set initial directory on F2FS filesystem: %s\n", strerror(errno));
+        return -1;
+    }
+    unique_fd fd(open(fileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
+    if (fd == -1) {
+        fprintf(stderr, "Unable to open output file for F2FS filesystem: %s\n", strerror(errno));
         return -1;
     }
     return make_f2fs_sparse_fd(fd, partSize, NULL, NULL);
@@ -42,7 +174,7 @@
     const char* fs_type;  //must match what fastboot reports for partition type
 
     //returns 0 or error value
-    int (*generate)(int fd, long long partSize, const std::string& initial_dir,
+    int (*generate)(const char* fileName, long long partSize, const std::string& initial_dir,
                     unsigned eraseBlkSize, unsigned logicalBlkSize);
 
 } generators[] = {
@@ -61,8 +193,8 @@
     return nullptr;
 }
 
-int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
+int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
     const std::string& initial_dir, unsigned eraseBlkSize, unsigned logicalBlkSize)
 {
-    return gen->generate(tmpFileNo, partSize, initial_dir, eraseBlkSize, logicalBlkSize);
+    return gen->generate(fileName, partSize, initial_dir, eraseBlkSize, logicalBlkSize);
 }
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 0a5f5a4..c6baa7f 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -7,7 +7,7 @@
 struct fs_generator;
 
 const struct fs_generator* fs_get_generator(const std::string& fs_type);
-int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
+int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
     const std::string& initial_dir, unsigned eraseBlkSize = 0, unsigned logicalBlkSize = 0);
 
 #endif
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
index bfa83b0..dcdf8f0 100644
--- a/fastboot/protocol.cpp
+++ b/fastboot/protocol.cpp
@@ -29,26 +29,34 @@
 #define round_down(a, b) \
     ({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); })
 
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 
 #include <algorithm>
+#include <vector>
 
+#include <android-base/file.h>
 #include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
 #include <sparse/sparse.h>
+#include <utils/FileMap.h>
 
 #include "fastboot.h"
 #include "transport.h"
 
 static std::string g_error;
 
+using android::base::unique_fd;
+using android::base::WriteStringToFile;
+
 const std::string fb_get_error() {
     return g_error;
 }
 
-static int check_response(Transport* transport, uint32_t size, char* response) {
+static int64_t check_response(Transport* transport, uint32_t size, char* response) {
     char status[65];
 
     while (true) {
@@ -105,7 +113,7 @@
     return -1;
 }
 
-static int _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
+static int64_t _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
     size_t cmdsize = strlen(cmd);
     if (cmdsize > 64) {
         g_error = android::base::StringPrintf("command too large (%zu)", cmdsize);
@@ -125,37 +133,51 @@
     return check_response(transport, size, response);
 }
 
-static int _command_data(Transport* transport, const void* data, uint32_t size) {
-    int r = transport->Write(data, size);
+static int64_t _command_write_data(Transport* transport, const void* data, uint32_t size) {
+    int64_t r = transport->Write(data, size);
     if (r < 0) {
-        g_error = android::base::StringPrintf("data transfer failure (%s)", strerror(errno));
+        g_error = android::base::StringPrintf("data write failure (%s)", strerror(errno));
         transport->Close();
         return -1;
     }
-    if (r != ((int) size)) {
-        g_error = "data transfer failure (short transfer)";
+    if (r != static_cast<int64_t>(size)) {
+        g_error = "data write failure (short transfer)";
         transport->Close();
         return -1;
     }
     return r;
 }
 
-static int _command_end(Transport* transport) {
+static int64_t _command_read_data(Transport* transport, void* data, uint32_t size) {
+    int64_t r = transport->Read(data, size);
+    if (r < 0) {
+        g_error = android::base::StringPrintf("data read failure (%s)", strerror(errno));
+        transport->Close();
+        return -1;
+    }
+    if (r != (static_cast<int64_t>(size))) {
+        g_error = "data read failure (short transfer)";
+        transport->Close();
+        return -1;
+    }
+    return r;
+}
+
+static int64_t _command_end(Transport* transport) {
     return check_response(transport, 0, 0) < 0 ? -1 : 0;
 }
 
-static int _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
+static int64_t _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
                          char* response) {
     if (size == 0) {
         return -1;
     }
 
-    int r = _command_start(transport, cmd, size, response);
+    int64_t r = _command_start(transport, cmd, size, response);
     if (r < 0) {
         return -1;
     }
-
-    r = _command_data(transport, data, size);
+    r = _command_write_data(transport, data, size);
     if (r < 0) {
         return -1;
     }
@@ -168,6 +190,39 @@
     return size;
 }
 
+static int64_t _command_send_fd(Transport* transport, const char* cmd, int fd, uint32_t size,
+                                char* response) {
+    static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
+    off64_t offset = 0;
+    uint32_t remaining = size;
+
+    if (_command_start(transport, cmd, size, response) < 0) {
+        return -1;
+    }
+
+    while (remaining) {
+        android::FileMap filemap;
+        size_t len = std::min(remaining, MAX_MAP_SIZE);
+
+        if (!filemap.create(NULL, fd, offset, len, true)) {
+            return -1;
+        }
+
+        if (_command_write_data(transport, filemap.getDataPtr(), len) < 0) {
+            return -1;
+        }
+
+        remaining -= len;
+        offset += len;
+    }
+
+    if (_command_end(transport) < 0) {
+        return -1;
+    }
+
+    return size;
+}
+
 static int _command_send_no_data(Transport* transport, const char* cmd, char* response) {
     return _command_start(transport, cmd, 0, response);
 }
@@ -180,10 +235,36 @@
     return _command_send_no_data(transport, cmd, response);
 }
 
-int fb_download_data(Transport* transport, const void* data, uint32_t size) {
-    char cmd[64];
-    snprintf(cmd, sizeof(cmd), "download:%08x", size);
-    return _command_send(transport, cmd, data, size, 0) < 0 ? -1 : 0;
+int64_t fb_download_data(Transport* transport, const void* data, uint32_t size) {
+    std::string cmd(android::base::StringPrintf("download:%08x", size));
+    return _command_send(transport, cmd.c_str(), data, size, 0) < 0 ? -1 : 0;
+}
+
+int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size) {
+    std::string cmd(android::base::StringPrintf("download:%08x", size));
+    return _command_send_fd(transport, cmd.c_str(), fd, size, 0) < 0 ? -1 : 0;
+}
+
+int64_t fb_upload_data(Transport* transport, const char* outfile) {
+    // positive return value is the upload size sent by the device
+    int64_t r = _command_start(transport, "upload", std::numeric_limits<int32_t>::max(), nullptr);
+    if (r <= 0) {
+        g_error = android::base::StringPrintf("command start failed (%s)", strerror(errno));
+        return r;
+    }
+
+    std::string data;
+    data.resize(r);
+    if ((r = _command_read_data(transport, &data[0], data.size())) == -1) {
+        return r;
+    }
+
+    if (!WriteStringToFile(data, outfile, true)) {
+        g_error = android::base::StringPrintf("write to '%s' failed", outfile);
+        return -1;
+    }
+
+    return _command_end(transport);
 }
 
 #define TRANSPORT_BUF_SIZE 1024
@@ -207,7 +288,7 @@
     }
 
     if (transport_buf_len == TRANSPORT_BUF_SIZE) {
-        r = _command_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
+        r = _command_write_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
         if (r != TRANSPORT_BUF_SIZE) {
             return -1;
         }
@@ -220,7 +301,7 @@
             return -1;
         }
         to_write = round_down(len, TRANSPORT_BUF_SIZE);
-        r = _command_data(transport, ptr, to_write);
+        r = _command_write_data(transport, ptr, to_write);
         if (r != to_write) {
             return -1;
         }
@@ -242,7 +323,8 @@
 
 static int fb_download_data_sparse_flush(Transport* transport) {
     if (transport_buf_len > 0) {
-        if (_command_data(transport, transport_buf, transport_buf_len) != transport_buf_len) {
+        int64_t r = _command_write_data(transport, transport_buf, transport_buf_len);
+        if (r != static_cast<int64_t>(transport_buf_len)) {
             return -1;
         }
         transport_buf_len = 0;
@@ -256,9 +338,8 @@
         return -1;
     }
 
-    char cmd[64];
-    snprintf(cmd, sizeof(cmd), "download:%08x", size);
-    int r = _command_start(transport, cmd, size, 0);
+    std::string cmd(android::base::StringPrintf("download:%08x", size));
+    int r = _command_start(transport, cmd.c_str(), size, 0);
     if (r < 0) {
         return -1;
     }
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
new file mode 100644
index 0000000..4441ad0
--- /dev/null
+++ b/fs_mgr/Android.bp
@@ -0,0 +1,64 @@
+//
+// 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_defaults {
+    name: "fs_mgr_defaults",
+    sanitize: {
+        misc_undefined: ["integer"],
+    },
+    local_include_dirs: ["include/"],
+    cppflags: ["-Werror"],
+    static_libs: [
+        "libfec",
+        "libfec_rs",
+        "libbase",
+        "libcrypto_utils",
+        "libcrypto",
+        "libext4_utils",
+        "libsquashfs_utils",
+        "libselinux",
+        "libavb",
+    ],
+    whole_static_libs: [
+        "liblogwrap",
+    ],
+}
+
+cc_library_static {
+    name: "libfs_mgr",
+    defaults: ["fs_mgr_defaults"],
+    export_include_dirs: ["include"],
+    include_dirs: ["system/vold"],
+    srcs: [
+        "fs_mgr.cpp",
+        "fs_mgr_dm_ioctl.cpp",
+        "fs_mgr_format.cpp",
+        "fs_mgr_fstab.cpp",
+        "fs_mgr_slotselect.cpp",
+        "fs_mgr_verity.cpp",
+        "fs_mgr_avb.cpp",
+        "fs_mgr_avb_ops.cpp",
+        "fs_mgr_boot_config.cpp",
+    ],
+    product_variables: {
+        debuggable: {
+            cppflags: ["-DALLOW_ADBD_DISABLE_VERITY=1"],
+        },
+        eng: {
+            cppflags: ["-DALLOW_SKIP_SECURE_CHECK=1"],
+        },
+    },
+}
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index 2863a26..9249343 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -17,39 +17,11 @@
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
 LOCAL_SANITIZE := integer
-LOCAL_SRC_FILES:= \
-    fs_mgr.cpp \
-    fs_mgr_dm_ioctl.cpp \
-    fs_mgr_format.cpp \
-    fs_mgr_fstab.cpp \
-    fs_mgr_slotselect.cpp \
-    fs_mgr_verity.cpp \
-    fs_mgr_avb.cpp \
-    fs_mgr_avb_ops.cpp \
-    fs_mgr_boot_config.cpp
-LOCAL_C_INCLUDES := \
-    $(LOCAL_PATH)/include \
-    system/vold \
-    system/extras/ext4_utils
-LOCAL_MODULE:= libfs_mgr
-LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT)))
-LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
-endif
-ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))
-LOCAL_CFLAGS += -DALLOW_SKIP_SECURE_CHECK=1
-endif
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
 LOCAL_SRC_FILES:= fs_mgr_main.cpp
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_MODULE:= fs_mgr
 LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := mke2fs mke2fs.conf e2fsdroid
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin
 LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 803b894..e009383 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -721,6 +721,12 @@
     return false;
 }
 
+static bool should_use_metadata_encryption(const struct fstab_rec* rec) {
+    if (!(rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE))) return false;
+    if (!(rec->fs_mgr_flags & MF_KEYDIRECTORY)) return false;
+    return true;
+}
+
 // Check to see if a mountable volume has encryption requirements
 static int handle_encryptable(const struct fstab_rec* rec)
 {
@@ -733,8 +739,14 @@
                      << " - allow continue unencrypted";
             return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
         }
+    } else if (should_use_metadata_encryption(rec)) {
+        if (umount(rec->mount_point) == 0) {
+            return FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION;
+        } else {
+            PERROR << "Could not umount " << rec->mount_point << " - fail since can't encrypt";
+            return FS_MGR_MNTALL_FAIL;
+        }
     } else if (rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE)) {
-        // Deal with file level encryption
         LINFO << rec->mount_point << " is file encrypted";
         return FS_MGR_MNTALL_DEV_FILE_ENCRYPTED;
     } else if (fs_mgr_is_encryptable(rec)) {
@@ -790,7 +802,7 @@
     FsManagerAvbUniquePtr avb_handle(nullptr);
 
     if (!fstab) {
-        return -1;
+        return FS_MGR_MNTALL_FAIL;
     }
 
     for (i = 0; i < fstab->num_entries; i++) {
@@ -836,10 +848,11 @@
                 avb_handle = FsManagerAvbHandle::Open(*fstab);
                 if (!avb_handle) {
                     LERROR << "Failed to open FsManagerAvbHandle";
-                    return -1;
+                    return FS_MGR_MNTALL_FAIL;
                 }
             }
-            if (!avb_handle->SetUpAvb(&fstab->recs[i], true /* wait_for_verity_dev */)) {
+            if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
+                SetUpAvbHashtreeResult::kFail) {
                 LERROR << "Failed to set up AVB on partition: "
                        << fstab->recs[i].mount_point << ", skipping!";
                 /* Skips mounting the device. */
@@ -847,7 +860,9 @@
             }
         } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
             int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
-            if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
+            if (__android_log_is_debuggable() &&
+                    (rc == FS_MGR_SETUP_VERITY_DISABLED ||
+                     rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
                 LINFO << "Verity disabled";
             } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
                 LERROR << "Could not set up verified partition, skipping!";
@@ -883,7 +898,6 @@
             continue;
         }
 
-        /* mount(2) returned an error, handle the encryptable/formattable case */
         bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);
         bool crypt_footer = false;
         if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
@@ -923,6 +937,8 @@
                 continue;
             }
         }
+
+        /* mount(2) returned an error, handle the encryptable/formattable case */
         if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
             fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {
             if (wiped) {
@@ -948,6 +964,9 @@
                 }
             }
             encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
+        } else if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
+                   should_use_metadata_encryption(&fstab->recs[attempted_idx])) {
+            encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
         } else {
             // fs_options might be null so we cannot use PERROR << directly.
             // Use StringPrintf to output "(null)" instead.
@@ -970,7 +989,7 @@
     }
 
     if (error_count) {
-        return -1;
+        return FS_MGR_MNTALL_FAIL;
     } else {
         return encryptable;
     }
@@ -1003,14 +1022,13 @@
                     char *tmp_mount_point)
 {
     int i = 0;
-    int ret = FS_MGR_DOMNT_FAILED;
     int mount_errors = 0;
     int first_mount_errno = 0;
-    char *m;
+    char* mount_point;
     FsManagerAvbUniquePtr avb_handle(nullptr);
 
     if (!fstab) {
-        return ret;
+        return FS_MGR_DOMNT_FAILED;
     }
 
     for (i = 0; i < fstab->num_entries; i++) {
@@ -1025,7 +1043,7 @@
             !strcmp(fstab->recs[i].fs_type, "mtd")) {
             LERROR << "Cannot mount filesystem of type "
                    << fstab->recs[i].fs_type << " on " << n_blk_device;
-            goto out;
+            return FS_MGR_DOMNT_FAILED;
         }
 
         /* First check the filesystem if requested */
@@ -1051,10 +1069,11 @@
                 avb_handle = FsManagerAvbHandle::Open(*fstab);
                 if (!avb_handle) {
                     LERROR << "Failed to open FsManagerAvbHandle";
-                    return -1;
+                    return FS_MGR_DOMNT_FAILED;
                 }
             }
-            if (!avb_handle->SetUpAvb(&fstab->recs[i], true /* wait_for_verity_dev */)) {
+            if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
+                SetUpAvbHashtreeResult::kFail) {
                 LERROR << "Failed to set up AVB on partition: "
                        << fstab->recs[i].mount_point << ", skipping!";
                 /* Skips mounting the device. */
@@ -1062,7 +1081,9 @@
             }
         } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
             int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
-            if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
+            if (__android_log_is_debuggable() &&
+                    (rc == FS_MGR_SETUP_VERITY_DISABLED ||
+                     rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
                 LINFO << "Verity disabled";
             } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
                 LERROR << "Could not set up verified partition, skipping!";
@@ -1072,16 +1093,15 @@
 
         /* Now mount it where requested */
         if (tmp_mount_point) {
-            m = tmp_mount_point;
+            mount_point = tmp_mount_point;
         } else {
-            m = fstab->recs[i].mount_point;
+            mount_point = fstab->recs[i].mount_point;
         }
         int retry_count = 2;
         while (retry_count-- > 0) {
-            if (!__mount(n_blk_device, m, &fstab->recs[i])) {
-                ret = 0;
+            if (!__mount(n_blk_device, mount_point, &fstab->recs[i])) {
                 fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
-                goto out;
+                return FS_MGR_DOMNT_SUCCESS;
             } else {
                 if (retry_count <= 0) break;  // run check_fs only once
                 if (!first_mount_errno) first_mount_errno = errno;
@@ -1093,22 +1113,16 @@
         }
         log_fs_stat(fstab->recs[i].blk_device, fs_stat);
     }
+
+    // Reach here means the mount attempt fails.
     if (mount_errors) {
-        PERROR << "Cannot mount filesystem on " << n_blk_device
-               << " at " << m;
-        if (first_mount_errno == EBUSY) {
-            ret = FS_MGR_DOMNT_BUSY;
-        } else {
-            ret = FS_MGR_DOMNT_FAILED;
-        }
+        PERROR << "Cannot mount filesystem on " << n_blk_device << " at " << mount_point;
+        if (first_mount_errno == EBUSY) return FS_MGR_DOMNT_BUSY;
     } else {
         /* We didn't find a match, say so and return an error */
-        LERROR << "Cannot find mount point " << fstab->recs[i].mount_point
-               << " in fstab";
+        LERROR << "Cannot find mount point " << n_name << " in fstab";
     }
-
-out:
-    return ret;
+    return FS_MGR_DOMNT_FAILED;
 }
 
 /*
@@ -1203,7 +1217,7 @@
                 ret = -1;
                 continue;
             }
-            fprintf(zram_fp, "%d\n", fstab->recs[i].zram_size);
+            fprintf(zram_fp, "%u\n", fstab->recs[i].zram_size);
             fclose(zram_fp);
         }
 
@@ -1242,48 +1256,46 @@
     return ret;
 }
 
-/*
- * key_loc must be at least PROPERTY_VALUE_MAX bytes long
- *
- * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
- */
-int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_device, int size)
-{
-    int i = 0;
+struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab) {
+    int i;
 
     if (!fstab) {
-        return -1;
-    }
-    /* Initialize return values to null strings */
-    if (key_loc) {
-        *key_loc = '\0';
-    }
-    if (real_blk_device) {
-        *real_blk_device = '\0';
+        return NULL;
     }
 
     /* Look for the encryptable partition to find the data */
     for (i = 0; i < fstab->num_entries; i++) {
         /* Don't deal with vold managed enryptable partitions here */
-        if (fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) {
-            continue;
+        if (!(fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) &&
+            (fstab->recs[i].fs_mgr_flags &
+             (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE | MF_FILEENCRYPTION))) {
+            return &fstab->recs[i];
         }
-        if (!(fstab->recs[i].fs_mgr_flags
-              & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE))) {
-            continue;
-        }
-
-        /* We found a match */
-        if (key_loc) {
-            strlcpy(key_loc, fstab->recs[i].key_loc, size);
-        }
-        if (real_blk_device) {
-            strlcpy(real_blk_device, fstab->recs[i].blk_device, size);
-        }
-        break;
     }
+    return NULL;
+}
 
-    return 0;
+/*
+ * key_loc must be at least PROPERTY_VALUE_MAX bytes long
+ *
+ * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
+ */
+void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size) {
+    struct fstab_rec const* rec = fs_mgr_get_crypt_entry(fstab);
+    if (key_loc) {
+        if (rec) {
+            strlcpy(key_loc, rec->key_loc, size);
+        } else {
+            *key_loc = '\0';
+        }
+    }
+    if (real_blk_device) {
+        if (rec) {
+            strlcpy(real_blk_device, rec->blk_device, size);
+        } else {
+            *real_blk_device = '\0';
+        }
+    }
 }
 
 bool fs_mgr_load_verity_state(int* mode) {
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 36883e6..2c99aa7 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -16,15 +16,14 @@
 
 #include "fs_mgr_avb.h"
 
-#include <errno.h>
 #include <fcntl.h>
-#include <inttypes.h>
 #include <libgen.h>
-#include <stdio.h>
 #include <string.h>
-#include <sys/stat.h>
+#include <sys/ioctl.h>
 #include <sys/types.h>
-#include <unistd.h>
+
+#include <sstream>
+#include <string>
 #include <vector>
 
 #include <android-base/file.h>
@@ -33,11 +32,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#include <cutils/properties.h>
 #include <libavb/libavb.h>
-#include <openssl/sha.h>
-#include <sys/ioctl.h>
-#include <utils/Compat.h>
 
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
@@ -45,48 +40,6 @@
 #include "fs_mgr_priv_dm_ioctl.h"
 #include "fs_mgr_priv_sha.h"
 
-/* The format of dm-verity construction parameters:
- *     <version> <dev> <hash_dev> <data_block_size> <hash_block_size>
- *     <num_data_blocks> <hash_start_block> <algorithm> <digest> <salt>
- */
-#define VERITY_TABLE_FORMAT \
-    "%u %s %s %u %u "       \
-    "%" PRIu64 " %" PRIu64 " %s %s %s "
-
-#define VERITY_TABLE_PARAMS(hashtree_desc, blk_device, digest, salt)                        \
-    hashtree_desc.dm_verity_version, blk_device, blk_device, hashtree_desc.data_block_size, \
-        hashtree_desc.hash_block_size,                                                      \
-        hashtree_desc.image_size / hashtree_desc.data_block_size,  /* num_data_blocks. */   \
-        hashtree_desc.tree_offset / hashtree_desc.hash_block_size, /* hash_start_block. */  \
-        (char*)hashtree_desc.hash_algorithm, digest, salt
-
-#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
-#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
-
-/* The default format of dm-verity optional parameters:
- *     <#opt_params> ignore_zero_blocks restart_on_corruption
- */
-#define VERITY_TABLE_OPT_DEFAULT_FORMAT "2 %s %s"
-#define VERITY_TABLE_OPT_DEFAULT_PARAMS VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART
-
-/* The FEC (forward error correction) format of dm-verity optional parameters:
- *     <#opt_params> use_fec_from_device <fec_dev>
- *     fec_roots <num> fec_blocks <num> fec_start <offset>
- *     ignore_zero_blocks restart_on_corruption
- */
-#define VERITY_TABLE_OPT_FEC_FORMAT \
-    "10 use_fec_from_device %s fec_roots %u fec_blocks %" PRIu64 " fec_start %" PRIu64 " %s %s"
-
-/* Note that fec_blocks is the size that FEC covers, *not* the
- * size of the FEC data. Since we use FEC for everything up until
- * the FEC data, it's the same as the offset (fec_start).
- */
-#define VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device)                     \
-    blk_device, hashtree_desc.fec_num_roots,                                       \
-        hashtree_desc.fec_offset / hashtree_desc.data_block_size, /* fec_blocks */ \
-        hashtree_desc.fec_offset / hashtree_desc.data_block_size, /* fec_start */  \
-        VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART
-
 static inline bool nibble_value(const char& c, uint8_t* value) {
     FS_MGR_CHECK(value != nullptr);
 
@@ -161,7 +114,6 @@
 
 // Reads the following values from kernel cmdline and provides the
 // VerifyVbmetaImages() to verify AvbSlotVerifyData.
-//   - androidboot.vbmeta.device_state
 //   - androidboot.vbmeta.hash_alg
 //   - androidboot.vbmeta.size
 //   - androidboot.vbmeta.digest
@@ -170,7 +122,6 @@
     // The factory method to return a unique_ptr<FsManagerAvbVerifier>
     static std::unique_ptr<FsManagerAvbVerifier> Create();
     bool VerifyVbmetaImages(const AvbSlotVerifyData& verify_data);
-    bool IsDeviceUnlocked() { return is_device_unlocked_; }
 
   protected:
     FsManagerAvbVerifier() = default;
@@ -185,13 +136,12 @@
     HashAlgorithm hash_alg_;
     uint8_t digest_[SHA512_DIGEST_LENGTH];
     size_t vbmeta_size_;
-    bool is_device_unlocked_;
 };
 
 std::unique_ptr<FsManagerAvbVerifier> FsManagerAvbVerifier::Create() {
     std::string cmdline;
     if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
-        LERROR << "Failed to read /proc/cmdline";
+        PERROR << "Failed to read /proc/cmdline";
         return nullptr;
     }
 
@@ -208,9 +158,7 @@
         const std::string& key = pieces[0];
         const std::string& value = pieces[1];
 
-        if (key == "androidboot.vbmeta.device_state") {
-            avb_verifier->is_device_unlocked_ = (value == "unlocked");
-        } else if (key == "androidboot.vbmeta.hash_alg") {
+        if (key == "androidboot.vbmeta.hash_alg") {
             hash_alg = value;
         } else if (key == "androidboot.vbmeta.size") {
             if (!android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
@@ -280,10 +228,81 @@
     return true;
 }
 
-static bool hashtree_load_verity_table(struct dm_ioctl* io, const std::string& dm_device_name,
-                                       int fd, const std::string& blk_device,
-                                       const AvbHashtreeDescriptor& hashtree_desc,
-                                       const std::string& salt, const std::string& root_digest) {
+// Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
+// See the following link for more details:
+// https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
+static std::string construct_verity_table(const AvbHashtreeDescriptor& hashtree_desc,
+                                          const std::string& salt, const std::string& root_digest,
+                                          const std::string& blk_device) {
+    // Loads androidboot.veritymode from kernel cmdline.
+    std::string verity_mode;
+    if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
+        verity_mode = "enforcing";  // Defaults to enforcing when it's absent.
+    }
+
+    // Converts veritymode to the format used in kernel.
+    std::string dm_verity_mode;
+    if (verity_mode == "enforcing") {
+        dm_verity_mode = "restart_on_corruption";
+    } else if (verity_mode == "logging") {
+        dm_verity_mode = "ignore_corruption";
+    } else if (verity_mode != "eio") {  // Default dm_verity_mode is eio.
+        LERROR << "Unknown androidboot.veritymode: " << verity_mode;
+        return "";
+    }
+
+    // dm-verity construction parameters:
+    //   <version> <dev> <hash_dev>
+    //   <data_block_size> <hash_block_size>
+    //   <num_data_blocks> <hash_start_block>
+    //   <algorithm> <digest> <salt>
+    //   [<#opt_params> <opt_params>]
+    std::ostringstream verity_table;
+    verity_table << hashtree_desc.dm_verity_version << " " << blk_device << " " << blk_device << " "
+                 << hashtree_desc.data_block_size << " " << hashtree_desc.hash_block_size << " "
+                 << hashtree_desc.image_size / hashtree_desc.data_block_size << " "
+                 << hashtree_desc.tree_offset / hashtree_desc.hash_block_size << " "
+                 << hashtree_desc.hash_algorithm << " " << root_digest << " " << salt;
+
+    // Continued from the above optional parameters:
+    //   [<#opt_params> <opt_params>]
+    int optional_argc = 0;
+    std::ostringstream optional_args;
+
+    // dm-verity optional parameters for FEC (forward error correction):
+    //   use_fec_from_device <fec_dev>
+    //   fec_roots <num>
+    //   fec_blocks <num>
+    //   fec_start <offset>
+    if (hashtree_desc.fec_size > 0) {
+        // Note that fec_blocks is the size that FEC covers, *NOT* the
+        // size of the FEC data. Since we use FEC for everything up until
+        // the FEC data, it's the same as the offset (fec_start).
+        optional_argc += 8;
+        // clang-format off
+        optional_args << "use_fec_from_device " << blk_device
+                      << " fec_roots " << hashtree_desc.fec_num_roots
+                      << " fec_blocks " << hashtree_desc.fec_offset / hashtree_desc.data_block_size
+                      << " fec_start " << hashtree_desc.fec_offset / hashtree_desc.data_block_size
+                      << " ";
+        // clang-format on
+    }
+
+    if (!dm_verity_mode.empty()) {
+        optional_argc += 1;
+        optional_args << dm_verity_mode << " ";
+    }
+
+    // Always use ignore_zero_blocks.
+    optional_argc += 1;
+    optional_args << "ignore_zero_blocks";
+
+    verity_table << " " << optional_argc << " " << optional_args.str();
+    return verity_table.str();
+}
+
+static bool load_verity_table(struct dm_ioctl* io, const std::string& dm_device_name, int fd,
+                              uint64_t image_size, const std::string& verity_table) {
     fs_mgr_verity_ioctl_init(io, dm_device_name, DM_STATUS_TABLE_FLAG);
 
     // The buffer consists of [dm_ioctl][dm_target_spec][verity_params].
@@ -294,35 +313,25 @@
     io->target_count = 1;
     dm_target->status = 0;
     dm_target->sector_start = 0;
-    dm_target->length = hashtree_desc.image_size / 512;
+    dm_target->length = image_size / 512;
     strcpy(dm_target->target_type, "verity");
 
     // Builds the verity params.
     char* verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
     size_t bufsize = DM_BUF_SIZE - (verity_params - buffer);
 
-    int res = 0;
-    if (hashtree_desc.fec_size > 0) {
-        res = snprintf(verity_params, bufsize, VERITY_TABLE_FORMAT VERITY_TABLE_OPT_FEC_FORMAT,
-                       VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(), root_digest.c_str(),
-                                           salt.c_str()),
-                       VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device.c_str()));
-    } else {
-        res = snprintf(verity_params, bufsize, VERITY_TABLE_FORMAT VERITY_TABLE_OPT_DEFAULT_FORMAT,
-                       VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(), root_digest.c_str(),
-                                           salt.c_str()),
-                       VERITY_TABLE_OPT_DEFAULT_PARAMS);
-    }
+    LINFO << "Loading verity table: '" << verity_table << "'";
 
-    if (res < 0 || (size_t)res >= bufsize) {
-        LERROR << "Error building verity table; insufficient buffer size?";
+    // Copies verity_table to verity_params (including the terminating null byte).
+    if (verity_table.size() > bufsize - 1) {
+        LERROR << "Verity table size too large: " << verity_table.size()
+               << " (max allowable size: " << bufsize - 1 << ")";
         return false;
     }
-
-    LINFO << "Loading verity table: '" << verity_params << "'";
+    memcpy(verity_params, verity_table.c_str(), verity_table.size() + 1);
 
     // Sets ext target boundary.
-    verity_params += strlen(verity_params) + 1;
+    verity_params += verity_table.size() + 1;
     verity_params = (char*)(((unsigned long)verity_params + 7) & ~7);
     dm_target->next = verity_params - buffer;
 
@@ -362,9 +371,15 @@
         return false;
     }
 
+    std::string verity_table =
+        construct_verity_table(hashtree_desc, salt, root_digest, fstab_entry->blk_device);
+    if (verity_table.empty()) {
+        LERROR << "Failed to construct verity table.";
+        return false;
+    }
+
     // Loads the verity mapping table.
-    if (!hashtree_load_verity_table(io, mount_point, fd, std::string(fstab_entry->blk_device),
-                                    hashtree_desc, salt, root_digest)) {
+    if (!load_verity_table(io, mount_point, fd, hashtree_desc.image_size, verity_table)) {
         LERROR << "Couldn't load verity table!";
         return false;
     }
@@ -458,6 +473,16 @@
     return true;
 }
 
+// Orange state means the device is unlocked, see the following link for details.
+// https://source.android.com/security/verifiedboot/verified-boot#device_state
+static inline bool IsDeviceUnlocked() {
+    std::string verified_boot_state;
+    if (fs_mgr_get_boot_config("verifiedbootstate", &verified_boot_state)) {
+        return verified_boot_state == "orange";
+    }
+    return false;
+}
+
 FsManagerAvbUniquePtr FsManagerAvbHandle::Open(const fstab& fstab) {
     FsManagerAvbOps avb_ops(fstab);
     return DoOpen(&avb_ops);
@@ -473,12 +498,7 @@
 }
 
 FsManagerAvbUniquePtr FsManagerAvbHandle::DoOpen(FsManagerAvbOps* avb_ops) {
-    // Gets the expected hash value of vbmeta images from kernel cmdline.
-    std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
-    if (!avb_verifier) {
-        LERROR << "Failed to create FsManagerAvbVerifier";
-        return nullptr;
-    }
+    bool is_device_unlocked = IsDeviceUnlocked();
 
     FsManagerAvbUniquePtr avb_handle(new FsManagerAvbHandle());
     if (!avb_handle) {
@@ -486,8 +506,10 @@
         return nullptr;
     }
 
-    AvbSlotVerifyResult verify_result = avb_ops->AvbSlotVerify(
-        fs_mgr_get_slot_suffix(), avb_verifier->IsDeviceUnlocked(), &avb_handle->avb_slot_data_);
+    AvbSlotVerifyFlags flags = is_device_unlocked ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
+                                                  : AVB_SLOT_VERIFY_FLAGS_NONE;
+    AvbSlotVerifyResult verify_result =
+        avb_ops->AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->avb_slot_data_);
 
     // Only allow two verify results:
     //   - AVB_SLOT_VERIFY_RESULT_OK.
@@ -503,62 +525,81 @@
     //     for more details.
     switch (verify_result) {
         case AVB_SLOT_VERIFY_RESULT_OK:
-            avb_handle->status_ = kFsManagerAvbHandleSuccess;
+            avb_handle->status_ = kAvbHandleSuccess;
             break;
         case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
-            if (!avb_verifier->IsDeviceUnlocked()) {
+            if (!is_device_unlocked) {
                 LERROR << "ERROR_VERIFICATION isn't allowed when the device is LOCKED";
                 return nullptr;
             }
-            avb_handle->status_ = kFsManagerAvbHandleErrorVerification;
+            avb_handle->status_ = kAvbHandleVerificationError;
             break;
         default:
             LERROR << "avb_slot_verify failed, result: " << verify_result;
             return nullptr;
     }
 
-    // Verifies vbmeta images against the digest passed from bootloader.
-    if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
-        LERROR << "VerifyVbmetaImages failed";
-        return nullptr;
-    }
-
     // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
     avb_handle->avb_version_ =
         android::base::StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
 
-    // Checks whether FLAGS_HASHTREE_DISABLED is set.
+    // Checks whether FLAGS_VERIFICATION_DISABLED is set:
+    //   - Only the top-level vbmeta struct is read.
+    //   - vbmeta struct in other partitions are NOT processed, including AVB HASH descriptor(s)
+    //     and AVB HASHTREE descriptor(s).
     AvbVBMetaImageHeader vbmeta_header;
     avb_vbmeta_image_header_to_host_byte_order(
         (AvbVBMetaImageHeader*)avb_handle->avb_slot_data_->vbmeta_images[0].vbmeta_data,
         &vbmeta_header);
+    bool verification_disabled =
+        ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
 
-    bool hashtree_disabled =
-        ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
-    if (hashtree_disabled) {
-        avb_handle->status_ = kFsManagerAvbHandleHashtreeDisabled;
+    if (verification_disabled) {
+        avb_handle->status_ = kAvbHandleVerificationDisabled;
+    } else {
+        // Verifies vbmeta structs against the digest passed from bootloader in kernel cmdline.
+        std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
+        if (!avb_verifier) {
+            LERROR << "Failed to create FsManagerAvbVerifier";
+            return nullptr;
+        }
+        if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
+            LERROR << "VerifyVbmetaImages failed";
+            return nullptr;
+        }
+
+        // Checks whether FLAGS_HASHTREE_DISABLED is set.
+        bool hashtree_disabled =
+            ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+        if (hashtree_disabled) {
+            avb_handle->status_ = kAvbHandleHashtreeDisabled;
+        }
     }
 
     LINFO << "Returning avb_handle with status: " << avb_handle->status_;
     return avb_handle;
 }
 
-bool FsManagerAvbHandle::SetUpAvb(struct fstab_rec* fstab_entry, bool wait_for_verity_dev) {
-    if (!fstab_entry) return false;
-    if (!avb_slot_data_ || avb_slot_data_->num_vbmeta_images < 1) {
-        return false;
+SetUpAvbHashtreeResult FsManagerAvbHandle::SetUpAvbHashtree(struct fstab_rec* fstab_entry,
+                                                            bool wait_for_verity_dev) {
+    if (!fstab_entry || status_ == kAvbHandleUninitialized || !avb_slot_data_ ||
+        avb_slot_data_->num_vbmeta_images < 1) {
+        return SetUpAvbHashtreeResult::kFail;
     }
 
-    if (status_ == kFsManagerAvbHandleUninitialized) return false;
-    if (status_ == kFsManagerAvbHandleHashtreeDisabled) {
-        LINFO << "AVB HASHTREE disabled on:" << fstab_entry->mount_point;
-        return true;
+    if (status_ == kAvbHandleHashtreeDisabled || status_ == kAvbHandleVerificationDisabled) {
+        LINFO << "AVB HASHTREE disabled on: " << fstab_entry->mount_point;
+        return SetUpAvbHashtreeResult::kDisabled;
     }
 
-    std::string partition_name(basename(fstab_entry->mount_point));
-    if (!avb_validate_utf8((const uint8_t*)partition_name.c_str(), partition_name.length())) {
-        LERROR << "Partition name: " << partition_name.c_str() << " is not valid UTF-8.";
-        return false;
+    // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor
+    // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.
+    std::string partition_name(basename(fstab_entry->blk_device));
+    if (fstab_entry->fs_mgr_flags & MF_SLOTSELECT) {
+        auto ab_suffix = partition_name.rfind(fs_mgr_get_slot_suffix());
+        if (ab_suffix != std::string::npos) {
+            partition_name.erase(ab_suffix);
+        }
     }
 
     AvbHashtreeDescriptor hashtree_descriptor;
@@ -566,13 +607,14 @@
     std::string root_digest;
     if (!get_hashtree_descriptor(partition_name, *avb_slot_data_, &hashtree_descriptor, &salt,
                                  &root_digest)) {
-        return false;
+        return SetUpAvbHashtreeResult::kFail;
     }
 
     // Converts HASHTREE descriptor to verity_table_params.
     if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt, root_digest,
                                   wait_for_verity_dev)) {
-        return false;
+        return SetUpAvbHashtreeResult::kFail;
     }
-    return true;
+
+    return SetUpAvbHashtreeResult::kSuccess;
 }
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index 47df861..ba1262f 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -89,16 +89,28 @@
     return AVB_IO_RESULT_OK;
 }
 
+static AvbIOResult dummy_get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+                                               const char* partition ATTRIBUTE_UNUSED,
+                                               uint64_t* out_size_num_byte) {
+    // The function is for bootloader to load entire content of AVB HASH partitions.
+    // In user-space, returns 0 as we only need to set up AVB HASHTHREE partitions.
+    *out_size_num_byte = 0;
+    return AVB_IO_RESULT_OK;
+}
+
 void FsManagerAvbOps::InitializeAvbOps() {
     // We only need to provide the implementation of read_from_partition()
     // operation since that's all what is being used by the avb_slot_verify().
     // Other I/O operations are only required in bootloader but not in
-    // user-space so we set them as dummy operations.
+    // user-space so we set them as dummy operations. Also zero the entire
+    // struct so operations added in the future will be set to NULL.
+    memset(&avb_ops_, 0, sizeof(AvbOps));
     avb_ops_.read_from_partition = read_from_partition;
     avb_ops_.read_rollback_index = dummy_read_rollback_index;
     avb_ops_.validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
     avb_ops_.read_is_device_unlocked = dummy_read_is_device_unlocked;
     avb_ops_.get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
+    avb_ops_.get_size_of_partition = dummy_get_size_of_partition;
 
     // Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
     avb_ops_.user_data = this;
@@ -148,13 +160,13 @@
     if (offset < 0) {
         off64_t total_size = lseek64(fd, 0, SEEK_END);
         if (total_size == -1) {
-            LERROR << "Failed to lseek64 to end of the partition";
+            PERROR << "Failed to lseek64 to end of the partition";
             return AVB_IO_RESULT_ERROR_IO;
         }
         offset = total_size + offset;
         // Repositions the offset to the beginning.
         if (lseek64(fd, 0, SEEK_SET) == -1) {
-            LERROR << "Failed to lseek64 to the beginning of the partition";
+            PERROR << "Failed to lseek64 to the beginning of the partition";
             return AVB_IO_RESULT_ERROR_IO;
         }
     }
@@ -175,13 +187,15 @@
 }
 
 AvbSlotVerifyResult FsManagerAvbOps::AvbSlotVerify(const std::string& ab_suffix,
-                                                   bool allow_verification_error,
+                                                   AvbSlotVerifyFlags flags,
                                                    AvbSlotVerifyData** out_data) {
     // Invokes avb_slot_verify() to load and verify all vbmeta images.
     // Sets requested_partitions to nullptr as it's to copy the contents
     // of HASH partitions into handle>avb_slot_data_, which is not required as
     // fs_mgr only deals with HASHTREE partitions.
     const char* requested_partitions[] = {nullptr};
-    return avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(),
-                           allow_verification_error, out_data);
+    // The |hashtree_error_mode| field doesn't matter as it only
+    // influences the generated kernel cmdline parameters.
+    return avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags,
+                           AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, out_data);
 }
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index cffa6ce..ab5beed 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -55,8 +55,6 @@
         if (android::base::ReadFileToString(file_name, out_val)) {
             return true;
         }
-
-        LINFO << "Error finding '" << key << "' in device tree";
     }
 
     return false;
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 5705f93..75feee7 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -24,25 +24,21 @@
 #include <cutils/partition_utils.h>
 #include <sys/mount.h>
 
-#include <ext4_utils/ext4_utils.h>
 #include <ext4_utils/ext4.h>
-#include <ext4_utils/make_ext4fs.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
+#include <ext4_utils/ext4_utils.h>
+#include <logwrap/logwrap.h>
 #include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
 
 #include "fs_mgr_priv.h"
 #include "cryptfs.h"
 
-extern "C" {
-extern struct fs_info info;     /* magic global from ext4_utils */
-extern void reset_ext4fs_info();
-}
-
 static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
 {
     uint64_t dev_sz;
     int fd, rc = 0;
+    int status;
 
     if ((fd = open(fs_blkdev, O_WRONLY)) < 0) {
         PERROR << "Cannot open block device";
@@ -55,30 +51,36 @@
         return -1;
     }
 
-    struct selabel_handle *sehandle = selinux_android_file_context_handle();
-    if (!sehandle) {
-        /* libselinux logs specific error */
-        LERROR << "Cannot initialize android file_contexts";
-        close(fd);
-        return -1;
-    }
-
-    /* Format the partition using the calculated length */
-    reset_ext4fs_info();
-    info.len = (off64_t)dev_sz;
-    if (crypt_footer) {
-        info.len -= CRYPT_FOOTER_OFFSET;
-    }
-
-    /* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */
-    rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, sehandle, 0, 0, NULL, NULL, NULL);
-    if (rc) {
-        LERROR << "make_ext4fs returned " << rc;
-    }
     close(fd);
 
-    if (sehandle) {
-        selabel_close(sehandle);
+    /* Format the partition using the calculated length */
+    if (crypt_footer) {
+        dev_sz -= CRYPT_FOOTER_OFFSET;
+    }
+
+    std::string size_str = std::to_string(dev_sz / 4096);
+    const char* const mke2fs_args[] = {
+        "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", fs_blkdev, size_str.c_str(), nullptr};
+
+    rc = android_fork_execvp_ext(arraysize(mke2fs_args), const_cast<char**>(mke2fs_args), &status,
+                                 true, LOG_KLOG, true, nullptr, nullptr, 0);
+    if (rc) {
+        LERROR << "mke2fs returned " << rc;
+        return rc;
+    }
+
+    const char* const e2fsdroid_args[] = {
+        "/system/bin/e2fsdroid",
+        "-e",
+        "-a",
+        fs_mnt_point,
+        fs_blkdev,
+        nullptr};
+
+    rc = android_fork_execvp_ext(arraysize(e2fsdroid_args), const_cast<char**>(e2fsdroid_args),
+                                 &status, true, LOG_KLOG, true, nullptr, nullptr, 0);
+    if (rc) {
+        LERROR << "e2fsdroid returned " << rc;
     }
 
     return rc;
@@ -86,42 +88,11 @@
 
 static int format_f2fs(char *fs_blkdev)
 {
-    char * args[3];
-    int pid;
-    int rc = 0;
+    int status;
+    const char* const args[] = {"/system/bin/make_f2fs", "-f", "-O encrypt", fs_blkdev, nullptr};
 
-    args[0] = (char *)"/sbin/mkfs.f2fs";
-    args[1] = fs_blkdev;
-    args[2] = (char *)0;
-
-    pid = fork();
-    if (pid < 0) {
-       return pid;
-    }
-    if (!pid) {
-        /* This doesn't return */
-        execv("/sbin/mkfs.f2fs", args);
-        exit(1);
-    }
-    for(;;) {
-        pid_t p = waitpid(pid, &rc, 0);
-        if (p != pid) {
-            LERROR << "Error waiting for child process - " << p;
-            rc = -1;
-            break;
-        }
-        if (WIFEXITED(rc)) {
-            rc = WEXITSTATUS(rc);
-            LINFO << args[0] << " done, status " << rc;
-            if (rc) {
-                rc = -1;
-            }
-            break;
-        }
-        LERROR << "Still waiting for " << args[0] << "...";
-    }
-
-    return rc;
+    return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), &status, true,
+                                   LOG_KLOG, true, nullptr, nullptr, 0);
 }
 
 int fs_mgr_do_format(struct fstab_rec *fstab, bool crypt_footer)
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 7d09d01..6c527c5 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -31,6 +31,7 @@
 
 struct fs_mgr_flag_values {
     char *key_loc;
+    char* key_dir;
     char *verity_loc;
     long long part_length;
     char *label;
@@ -70,34 +71,35 @@
 };
 
 static struct flag_list fs_mgr_flags[] = {
-    { "wait",               MF_WAIT },
-    { "check",              MF_CHECK },
-    { "encryptable=",       MF_CRYPT },
-    { "forceencrypt=",      MF_FORCECRYPT },
-    { "fileencryption=",    MF_FILEENCRYPTION },
-    { "forcefdeorfbe=",     MF_FORCEFDEORFBE },
-    { "nonremovable",       MF_NONREMOVABLE },
-    { "voldmanaged=",       MF_VOLDMANAGED},
-    { "length=",            MF_LENGTH },
-    { "recoveryonly",       MF_RECOVERYONLY },
-    { "swapprio=",          MF_SWAPPRIO },
-    { "zramsize=",          MF_ZRAMSIZE },
-    { "max_comp_streams=",  MF_MAX_COMP_STREAMS },
-    { "verifyatboot",       MF_VERIFYATBOOT },
-    { "verify",             MF_VERIFY },
-    { "avb",                MF_AVB },
-    { "noemulatedsd",       MF_NOEMULATEDSD },
-    { "notrim",             MF_NOTRIM },
-    { "formattable",        MF_FORMATTABLE },
-    { "slotselect",         MF_SLOTSELECT },
-    { "nofail",             MF_NOFAIL },
-    { "latemount",          MF_LATEMOUNT },
-    { "reservedsize=",      MF_RESERVEDSIZE },
-    { "quota",              MF_QUOTA },
-    { "eraseblk=",          MF_ERASEBLKSIZE },
-    { "logicalblk=",        MF_LOGICALBLKSIZE },
-    { "defaults",           0 },
-    { 0,                    0 },
+    {"wait", MF_WAIT},
+    {"check", MF_CHECK},
+    {"encryptable=", MF_CRYPT},
+    {"forceencrypt=", MF_FORCECRYPT},
+    {"fileencryption=", MF_FILEENCRYPTION},
+    {"forcefdeorfbe=", MF_FORCEFDEORFBE},
+    {"keydirectory=", MF_KEYDIRECTORY},
+    {"nonremovable", MF_NONREMOVABLE},
+    {"voldmanaged=", MF_VOLDMANAGED},
+    {"length=", MF_LENGTH},
+    {"recoveryonly", MF_RECOVERYONLY},
+    {"swapprio=", MF_SWAPPRIO},
+    {"zramsize=", MF_ZRAMSIZE},
+    {"max_comp_streams=", MF_MAX_COMP_STREAMS},
+    {"verifyatboot", MF_VERIFYATBOOT},
+    {"verify", MF_VERIFY},
+    {"avb", MF_AVB},
+    {"noemulatedsd", MF_NOEMULATEDSD},
+    {"notrim", MF_NOTRIM},
+    {"formattable", MF_FORMATTABLE},
+    {"slotselect", MF_SLOTSELECT},
+    {"nofail", MF_NOFAIL},
+    {"latemount", MF_LATEMOUNT},
+    {"reservedsize=", MF_RESERVEDSIZE},
+    {"quota", MF_QUOTA},
+    {"eraseblk=", MF_ERASEBLKSIZE},
+    {"logicalblk=", MF_LOGICALBLKSIZE},
+    {"defaults", 0},
+    {0, 0},
 };
 
 #define EM_AES_256_XTS  1
@@ -266,6 +268,11 @@
                     } else {
                         flag_vals->file_names_mode = EM_AES_256_CTS;
                     }
+                } else if ((fl[i].flag == MF_KEYDIRECTORY) && flag_vals) {
+                    /* The metadata flag is followed by an = and the
+                     * directory for the keys.  Get it and return it.
+                     */
+                    flag_vals->key_dir = strdup(strchr(p, '=') + 1);
                 } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
                     /* The length flag is followed by an = and the
                      * size of the partition.  Get it and return it.
@@ -382,16 +389,8 @@
 
     dirent* dp;
     while ((dp = readdir(fstabdir.get())) != NULL) {
-        // skip over name and compatible
-        if (dp->d_type != DT_DIR) {
-            continue;
-        }
-
-        // skip if its not 'vendor', 'odm' or 'system'
-        if (strcmp(dp->d_name, "odm") && strcmp(dp->d_name, "system") &&
-            strcmp(dp->d_name, "vendor")) {
-            continue;
-        }
+        // skip over name, compatible and .
+        if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue;
 
         // create <dev> <mnt_point>  <type>  <mnt_flags>  <fsmgr_flags>\n
         std::vector<std::string> fstab_entry;
@@ -566,6 +565,7 @@
         fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
                                                     &flag_vals, NULL, 0);
         fstab->recs[cnt].key_loc = flag_vals.key_loc;
+        fstab->recs[cnt].key_dir = flag_vals.key_dir;
         fstab->recs[cnt].verity_loc = flag_vals.verity_loc;
         fstab->recs[cnt].length = flag_vals.part_length;
         fstab->recs[cnt].label = flag_vals.label;
@@ -725,6 +725,7 @@
         free(fstab->recs[i].fs_type);
         free(fstab->recs[i].fs_options);
         free(fstab->recs[i].key_loc);
+        free(fstab->recs[i].key_dir);
         free(fstab->recs[i].label);
     }
 
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index c985462..3ca507b 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -107,6 +107,7 @@
 #define MF_ERASEBLKSIZE     0x800000
 #define MF_LOGICALBLKSIZE  0X1000000
 #define MF_AVB             0X2000000
+#define MF_KEYDIRECTORY 0X4000000
 
 #define DM_BUF_SIZE 4096
 
diff --git a/fs_mgr/fs_mgr_priv_avb_ops.h b/fs_mgr/fs_mgr_priv_avb_ops.h
index a6b52e4..d1ef2e9 100644
--- a/fs_mgr/fs_mgr_priv_avb_ops.h
+++ b/fs_mgr/fs_mgr_priv_avb_ops.h
@@ -56,7 +56,7 @@
     AvbIOResult ReadFromPartition(const char* partition, int64_t offset, size_t num_bytes,
                                   void* buffer, size_t* out_num_read);
 
-    AvbSlotVerifyResult AvbSlotVerify(const std::string& ab_suffix, bool allow_verification_error,
+    AvbSlotVerifyResult AvbSlotVerify(const std::string& ab_suffix, AvbSlotVerifyFlags flags,
                                       AvbSlotVerifyData** out_data);
 
   private:
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 5fa10bc..8904995 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -738,7 +738,7 @@
     // setup is needed at all.
     if (!is_device_secure()) {
         LINFO << "Verity setup skipped for " << mount_point;
-        return FS_MGR_SETUP_VERITY_SUCCESS;
+        return FS_MGR_SETUP_VERITY_SKIPPED;
     }
 
     if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index fd63dfd..e033d47 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -72,6 +72,7 @@
     char *fs_options;
     int fs_mgr_flags;
     char *key_loc;
+    char* key_dir;
     char *verity_loc;
     long long length;
     char *label;
@@ -95,6 +96,8 @@
 struct fstab *fs_mgr_read_fstab(const char *fstab_path);
 void fs_mgr_free_fstab(struct fstab *fstab);
 
+#define FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED 7
+#define FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION 6
 #define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5
 #define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 4
 #define FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION 3
@@ -106,14 +109,15 @@
 
 #define FS_MGR_DOMNT_FAILED (-1)
 #define FS_MGR_DOMNT_BUSY (-2)
+#define FS_MGR_DOMNT_SUCCESS 0
 
 int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
                     char *tmp_mount_point);
 int fs_mgr_do_mount_one(struct fstab_rec *rec);
 int fs_mgr_do_tmpfs_mount(const char *n_name);
 int fs_mgr_unmount_all(struct fstab *fstab);
-int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc,
-                          char *real_blk_device, int size);
+struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab);
+void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size);
 bool fs_mgr_load_verity_state(int* mode);
 bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback);
 int fs_mgr_add_entry(struct fstab *fstab,
@@ -142,6 +146,7 @@
 
 int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
 
+#define FS_MGR_SETUP_VERITY_SKIPPED  (-3)
 #define FS_MGR_SETUP_VERITY_DISABLED (-2)
 #define FS_MGR_SETUP_VERITY_FAIL (-1)
 #define FS_MGR_SETUP_VERITY_SUCCESS 0
diff --git a/fs_mgr/include/fs_mgr_avb.h b/fs_mgr/include/fs_mgr_avb.h
index bbafe1a..73a22c8 100644
--- a/fs_mgr/include/fs_mgr_avb.h
+++ b/fs_mgr/include/fs_mgr_avb.h
@@ -25,11 +25,10 @@
 
 #include "fs_mgr.h"
 
-enum FsManagerAvbHandleStatus {
-    kFsManagerAvbHandleUninitialized = -1,
-    kFsManagerAvbHandleSuccess = 0,
-    kFsManagerAvbHandleHashtreeDisabled = 1,
-    kFsManagerAvbHandleErrorVerification = 2,
+enum class SetUpAvbHashtreeResult {
+    kSuccess = 0,
+    kFail,
+    kDisabled,
 };
 
 class FsManagerAvbOps;
@@ -40,8 +39,8 @@
 using ByNameSymlinkMap = std::map<std::string, std::string>;
 
 // Provides a factory method to return a unique_ptr pointing to itself and the
-// SetUpAvb() function to extract dm-verity parameters from AVB metadata to
-// load verity table into kernel through ioctl.
+// SetUpAvbHashtree() function to extract dm-verity parameters from AVB HASHTREE
+// descriptors to load verity table into kernel through ioctl.
 class FsManagerAvbHandle {
   public:
     // The factory method to return a FsManagerAvbUniquePtr that holds
@@ -65,12 +64,22 @@
     //   - nullptr: any error when reading and verifying the metadata,
     //     e.g., I/O error, digest value mismatch, size mismatch, etc.
     //
-    //   - a valid unique_ptr with status kFsMgrAvbHandleHashtreeDisabled:
+    //   - a valid unique_ptr with status kAvbHandleHashtreeDisabled:
     //     to support the existing 'adb disable-verity' feature in Android.
     //     It's very helpful for developers to make the filesystem writable to
     //     allow replacing binaries on the device.
     //
-    //   - a valid unique_ptr with status kFsMgrAvbHandleSuccess: the metadata
+    //   - a valid unique_ptr with status kAvbHandleVerificationDisabled:
+    //     to support 'avbctl disable-verification': only the top-level
+    //     vbmeta is read, vbmeta structs in other partitions are not processed.
+    //     It's needed to bypass AVB when using the generic system.img to run
+    //     VTS for project Treble.
+    //
+    //   - a valid unique_ptr with status kAvbHandleVerificationError:
+    //     there is verification error when libavb loads vbmeta from each
+    //     partition. This is only allowed when the device is unlocked.
+    //
+    //   - a valid unique_ptr with status kAvbHandleSuccess: the metadata
     //     is verified and can be trusted.
     //
     static FsManagerAvbUniquePtr Open(const fstab& fstab);
@@ -79,14 +88,15 @@
     // Sets up dm-verity on the given fstab entry.
     // The 'wait_for_verity_dev' parameter makes this function wait for the
     // verity device to get created before return.
-    // Returns true if the mount point is eligible to mount, it includes:
-    //   - status_ is kFsMgrAvbHandleHashtreeDisabled or
-    //   - status_ is kFsMgrAvbHandleSuccess and sending ioctl DM_TABLE_LOAD
-    //     to load verity table is success.
-    // Otherwise, returns false.
-    bool SetUpAvb(fstab_rec* fstab_entry, bool wait_for_verity_dev);
+    //
+    // Return value:
+    //   - kSuccess: successfully loads dm-verity table into kernel.
+    //   - kFailed: failed to setup dm-verity, e.g., vbmeta verification error,
+    //     failed to get the HASHTREE descriptor, runtime error when set up
+    //     device-mapper, etc.
+    //   - kDisabled: hashtree is disabled.
+    SetUpAvbHashtreeResult SetUpAvbHashtree(fstab_rec* fstab_entry, bool wait_for_verity_dev);
 
-    bool hashtree_disabled() const { return status_ == kFsManagerAvbHandleHashtreeDisabled; }
     const std::string& avb_version() const { return avb_version_; }
 
     FsManagerAvbHandle(const FsManagerAvbHandle&) = delete;             // no copy
@@ -102,11 +112,19 @@
     };
 
   private:
-    FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kFsManagerAvbHandleUninitialized) {}
+    enum AvbHandleStatus {
+        kAvbHandleSuccess = 0,
+        kAvbHandleUninitialized,
+        kAvbHandleHashtreeDisabled,
+        kAvbHandleVerificationDisabled,
+        kAvbHandleVerificationError,
+    };
+
+    FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kAvbHandleUninitialized) {}
     static FsManagerAvbUniquePtr DoOpen(FsManagerAvbOps* avb_ops);
 
     AvbSlotVerifyData* avb_slot_data_;
-    FsManagerAvbHandleStatus status_;
+    AvbHandleStatus status_;
     std::string avb_version_;
 };
 
diff --git a/gatekeeperd/IGateKeeperService.cpp b/gatekeeperd/IGateKeeperService.cpp
index 95fbfd1..1c339f4 100644
--- a/gatekeeperd/IGateKeeperService.cpp
+++ b/gatekeeperd/IGateKeeperService.cpp
@@ -158,6 +158,12 @@
             reply->writeNoException();
             return NO_ERROR;
         }
+        case REPORT_DEVICE_SETUP_COMPLETE: {
+            CHECK_INTERFACE(IGateKeeperService, data, reply);
+            reportDeviceSetupComplete();
+            reply->writeNoException();
+            return NO_ERROR;
+        }
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/gatekeeperd/IGateKeeperService.h b/gatekeeperd/IGateKeeperService.h
index f070486..2816efc 100644
--- a/gatekeeperd/IGateKeeperService.h
+++ b/gatekeeperd/IGateKeeperService.h
@@ -33,6 +33,7 @@
         VERIFY_CHALLENGE = IBinder::FIRST_CALL_TRANSACTION + 2,
         GET_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 3,
         CLEAR_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 4,
+        REPORT_DEVICE_SETUP_COMPLETE = IBinder::FIRST_CALL_TRANSACTION + 5,
     };
 
     enum {
@@ -95,6 +96,12 @@
      * Clears the secure user ID associated with the user.
      */
     virtual void clearSecureUserId(uint32_t uid) = 0;
+
+    /**
+     * Notifies gatekeeper that device setup has been completed and any potentially still existing
+     * state from before a factory reset can be cleaned up (if it has not been already).
+     */
+    virtual void reportDeviceSetupComplete() = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index e6eb3bc..c6369f9 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -56,19 +56,13 @@
 class GateKeeperProxy : public BnGateKeeperService {
 public:
     GateKeeperProxy() {
+        clear_state_if_needed_done = false;
         hw_device = IGatekeeper::getService();
 
         if (hw_device == nullptr) {
             ALOGW("falling back to software GateKeeper");
             soft_device.reset(new SoftGateKeeperDevice());
         }
-
-        if (mark_cold_boot()) {
-            ALOGI("cold boot: clearing state");
-            if (hw_device != nullptr) {
-                hw_device->deleteAllUsers([](const GatekeeperResponse &){});
-            }
-        }
     }
 
     virtual ~GateKeeperProxy() {
@@ -86,6 +80,21 @@
         close(fd);
     }
 
+    void clear_state_if_needed() {
+        if (clear_state_if_needed_done) {
+            return;
+        }
+
+        if (mark_cold_boot()) {
+            ALOGI("cold boot: clearing state");
+            if (hw_device != nullptr) {
+                hw_device->deleteAllUsers([](const GatekeeperResponse &){});
+            }
+        }
+
+        clear_state_if_needed_done = true;
+    }
+
     bool mark_cold_boot() {
         const char *filename = ".coldboot";
         if (access(filename, F_OK) == -1) {
@@ -140,6 +149,10 @@
             return PERMISSION_DENIED;
         }
 
+        // Make sure to clear any state from before factory reset as soon as a credential is
+        // enrolled (which may happen during device setup).
+        clear_state_if_needed();
+
         // need a desired password to enroll
         if (desired_password_length == 0) return -EINVAL;
 
@@ -354,6 +367,18 @@
         }
     }
 
+    virtual void reportDeviceSetupComplete() {
+        IPCThreadState* ipc = IPCThreadState::self();
+        const int calling_pid = ipc->getCallingPid();
+        const int calling_uid = ipc->getCallingUid();
+        if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
+            ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
+            return;
+        }
+
+        clear_state_if_needed();
+    }
+
     virtual status_t dump(int fd, const Vector<String16> &) {
         IPCThreadState* ipc = IPCThreadState::self();
         const int pid = ipc->getCallingPid();
@@ -376,6 +401,8 @@
 private:
     sp<IGatekeeper> hw_device;
     UniquePtr<SoftGateKeeperDevice> soft_device;
+
+    bool clear_state_if_needed_done;
 };
 }// namespace android
 
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 8b59964..6b14289 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -33,6 +33,30 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_MODULE := libhealthd_draw
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_STATIC_LIBRARIES := \
+	libminui \
+	libbase
+LOCAL_SRC_FILES := healthd_draw.cpp
+
+ifneq ($(TARGET_HEALTHD_DRAW_SPLIT_SCREEN),)
+LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_SCREEN=$(TARGET_HEALTHD_DRAW_SPLIT_SCREEN)
+else
+LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_SCREEN=0
+endif
+
+ifneq ($(TARGET_HEALTHD_DRAW_SPLIT_OFFSET),)
+LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_OFFSET=$(TARGET_HEALTHD_DRAW_SPLIT_OFFSET)
+else
+LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_OFFSET=0
+endif
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
 LOCAL_CFLAGS := -Werror
 ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true)
 LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK
@@ -58,6 +82,7 @@
     libutils \
     libbase \
     libcutils \
+    libhealthd_draw \
     liblog \
     libm \
     libc \
@@ -101,6 +126,7 @@
 
 LOCAL_STATIC_LIBRARIES := \
     libhealthd_charger \
+    libhealthd_draw \
     libbatterymonitor \
     libbase \
     libutils \
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
new file mode 100644
index 0000000..ea3d991
--- /dev/null
+++ b/healthd/healthd_draw.cpp
@@ -0,0 +1,195 @@
+/*
+ * 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 <android-base/stringprintf.h>
+#include <batteryservice/BatteryService.h>
+#include <cutils/klog.h>
+
+#include "healthd_draw.h"
+
+#define LOGE(x...) KLOG_ERROR("charger", x);
+#define LOGV(x...) KLOG_DEBUG("charger", x);
+
+HealthdDraw::HealthdDraw(animation* anim)
+  : kSplitScreen(HEALTHD_DRAW_SPLIT_SCREEN),
+    kSplitOffset(HEALTHD_DRAW_SPLIT_OFFSET) {
+  gr_init();
+  gr_font_size(gr_sys_font(), &char_width_, &char_height_);
+
+  screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);
+  screen_height_ = gr_fb_height();
+
+  int res;
+  if (!anim->text_clock.font_file.empty() &&
+      (res = gr_init_font(anim->text_clock.font_file.c_str(),
+                          &anim->text_clock.font)) < 0) {
+    LOGE("Could not load time font (%d)\n", res);
+  }
+  if (!anim->text_percent.font_file.empty() &&
+      (res = gr_init_font(anim->text_percent.font_file.c_str(),
+                          &anim->text_percent.font)) < 0) {
+    LOGE("Could not load percent font (%d)\n", res);
+  }
+}
+
+HealthdDraw::~HealthdDraw() {}
+
+void HealthdDraw::redraw_screen(const animation* batt_anim, GRSurface* surf_unknown) {
+  clear_screen();
+
+  /* try to display *something* */
+  if (batt_anim->cur_level < 0 || batt_anim->num_frames == 0)
+    draw_unknown(surf_unknown);
+  else
+    draw_battery(batt_anim);
+  gr_flip();
+}
+
+void HealthdDraw::blank_screen(bool blank) { gr_fb_blank(blank); }
+
+void HealthdDraw::clear_screen(void) {
+  gr_color(0, 0, 0, 255);
+  gr_clear();
+}
+
+int HealthdDraw::draw_surface_centered(GRSurface* surface) {
+  int w = gr_get_width(surface);
+  int h = gr_get_height(surface);
+  int x = (screen_width_ - w) / 2 + kSplitOffset;
+  int y = (screen_height_ - h) / 2;
+
+  LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
+  gr_blit(surface, 0, 0, w, h, x, y);
+  if (kSplitScreen) {
+    x += screen_width_ - 2 * kSplitOffset;
+    LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
+    gr_blit(surface, 0, 0, w, h, x, y);
+  }
+
+  return y + h;
+}
+
+int HealthdDraw::draw_text(const GRFont* font, int x, int y, const char* str) {
+  int str_len_px = gr_measure(font, str);
+
+  if (x < 0) x = (screen_width_ - str_len_px) / 2;
+  if (y < 0) y = (screen_height_ - char_height_) / 2;
+  gr_text(font, x + kSplitOffset, y, str, false /* bold */);
+  if (kSplitScreen)
+    gr_text(font, x - kSplitOffset + screen_width_, y, str, false /* bold */);
+
+  return y + char_height_;
+}
+
+void HealthdDraw::determine_xy(const animation::text_field& field,
+                               const int length, int* x, int* y) {
+  *x = field.pos_x;
+
+  int str_len_px = length * field.font->char_width;
+  if (field.pos_x == CENTER_VAL) {
+    *x = (screen_width_ - str_len_px) / 2;
+  } else if (field.pos_x >= 0) {
+    *x = field.pos_x;
+  } else {  // position from max edge
+    *x = screen_width_ + field.pos_x - str_len_px - kSplitOffset;
+  }
+
+  *y = field.pos_y;
+
+  if (field.pos_y == CENTER_VAL) {
+    *y = (screen_height_ - field.font->char_height) / 2;
+  } else if (field.pos_y >= 0) {
+    *y = field.pos_y;
+  } else {  // position from max edge
+    *y = screen_height_ + field.pos_y - field.font->char_height;
+  }
+}
+
+void HealthdDraw::draw_clock(const animation* anim) {
+  static constexpr char CLOCK_FORMAT[] = "%H:%M";
+  static constexpr int CLOCK_LENGTH = 6;
+
+  const animation::text_field& field = anim->text_clock;
+
+  if (field.font == nullptr || field.font->char_width == 0 ||
+      field.font->char_height == 0)
+    return;
+
+  time_t rawtime;
+  time(&rawtime);
+  tm* time_info = localtime(&rawtime);
+
+  char clock_str[CLOCK_LENGTH];
+  size_t length = strftime(clock_str, CLOCK_LENGTH, CLOCK_FORMAT, time_info);
+  if (length != CLOCK_LENGTH - 1) {
+    LOGE("Could not format time\n");
+    return;
+  }
+
+  int x, y;
+  determine_xy(field, length, &x, &y);
+
+  LOGV("drawing clock %s %d %d\n", clock_str, x, y);
+  gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
+  draw_text(field.font, x, y, clock_str);
+}
+
+void HealthdDraw::draw_percent(const animation* anim) {
+  int cur_level = anim->cur_level;
+  if (anim->cur_status == BATTERY_STATUS_FULL) {
+    cur_level = 100;
+  }
+
+  if (cur_level <= 0) return;
+
+  const animation::text_field& field = anim->text_percent;
+  if (field.font == nullptr || field.font->char_width == 0 ||
+      field.font->char_height == 0) {
+    return;
+  }
+
+  std::string str = base::StringPrintf("%d%%", cur_level);
+
+  int x, y;
+  determine_xy(field, str.size(), &x, &y);
+
+  LOGV("drawing percent %s %d %d\n", str.c_str(), x, y);
+  gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
+  draw_text(field.font, x, y, str.c_str());
+}
+
+void HealthdDraw::draw_battery(const animation* anim) {
+  const animation::frame& frame = anim->frames[anim->cur_frame];
+
+  if (anim->num_frames != 0) {
+    draw_surface_centered(frame.surface);
+    LOGV("drawing frame #%d min_cap=%d time=%d\n", anim->cur_frame,
+         frame.min_level, frame.disp_time);
+  }
+  draw_clock(anim);
+  draw_percent(anim);
+}
+
+void HealthdDraw::draw_unknown(GRSurface* surf_unknown) {
+  int y;
+  if (surf_unknown) {
+    draw_surface_centered(surf_unknown);
+  } else {
+    gr_color(0xa4, 0xc6, 0x39, 255);
+    y = draw_text(gr_sys_font(), -1, -1, "Charging!");
+    draw_text(gr_sys_font(), -1, y + 25, "?\?/100");
+  }
+}
diff --git a/healthd/healthd_draw.h b/healthd/healthd_draw.h
new file mode 100644
index 0000000..6a6ba76
--- /dev/null
+++ b/healthd/healthd_draw.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 HEALTHD_DRAW_H
+#define HEALTHD_DRAW_H
+
+#include <linux/input.h>
+#include <minui/minui.h>
+
+#include "animation.h"
+
+using namespace android;
+
+class HealthdDraw {
+ public:
+  // Configures font using given animation.
+  HealthdDraw(animation* anim);
+  virtual ~HealthdDraw();
+
+  // Redraws screen.
+  void redraw_screen(const animation* batt_anim, GRSurface* surf_unknown);
+
+  // Blanks screen if true, unblanks if false.
+  virtual void blank_screen(bool blank);
+
+ protected:
+  virtual void clear_screen();
+
+  // returns the last y-offset of where the surface ends.
+  virtual int draw_surface_centered(GRSurface* surface);
+  // Negative x or y coordinates center text.
+  virtual int draw_text(const GRFont* font, int x, int y, const char* str);
+
+  // Negative x or y coordinates position the text away from the opposite edge
+  // that positive ones do.
+  virtual void determine_xy(const animation::text_field& field,
+                            const int length, int* x, int* y);
+
+  // Draws battery animation, if it exists.
+  virtual void draw_battery(const animation* anim);
+  // Draws clock text, if animation contains text_field data.
+  virtual void draw_clock(const animation* anim);
+  // Draws battery percentage text if animation contains text_field data.
+  virtual void draw_percent(const animation* anim);
+  // Draws charger->surf_unknown or basic text.
+  virtual void draw_unknown(GRSurface* surf_unknown);
+
+  // Pixel sizes of characters for default font.
+  int char_width_;
+  int char_height_;
+
+  // Width and height of screen in pixels.
+  int screen_width_;
+  int screen_height_;
+
+  // Device screen is split vertically.
+  const bool kSplitScreen;
+  // Pixels to offset graphics towards center split.
+  const int kSplitOffset;
+};
+
+#endif  // HEALTHD_DRAW_H
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 49a534c..e7f3eaf 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2013 The Android Open Source Project
+ * Copyright (C) 2011-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.
@@ -18,7 +18,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
-#include <linux/input.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -33,58 +32,56 @@
 #include <functional>
 
 #include <android-base/file.h>
-#include <android-base/stringprintf.h>
 
-#include <sys/socket.h>
 #include <linux/netlink.h>
+#include <sys/socket.h>
 
-#include <batteryservice/BatteryService.h>
 #include <cutils/klog.h>
 #include <cutils/misc.h>
-#include <cutils/uevent.h>
 #include <cutils/properties.h>
-#include <minui/minui.h>
+#include <cutils/uevent.h>
 #include <sys/reboot.h>
 
 #ifdef CHARGER_ENABLE_SUSPEND
 #include <suspend/autosuspend.h>
 #endif
 
-#include "animation.h"
 #include "AnimationParser.h"
+#include "healthd_draw.h"
 
 #include <healthd/healthd.h>
 
 using namespace android;
 
-char *locale;
+char* locale;
 
 #ifndef max
-#define max(a,b) ((a) > (b) ? (a) : (b))
+#define max(a, b) ((a) > (b) ? (a) : (b))
 #endif
 
 #ifndef min
-#define min(a,b) ((a) < (b) ? (a) : (b))
+#define min(a, b) ((a) < (b) ? (a) : (b))
 #endif
 
-#define ARRAY_SIZE(x)           (sizeof(x)/sizeof((x)[0]))
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
-#define MSEC_PER_SEC            (1000LL)
-#define NSEC_PER_MSEC           (1000000LL)
+#define MSEC_PER_SEC (1000LL)
+#define NSEC_PER_MSEC (1000000LL)
 
-#define BATTERY_UNKNOWN_TIME    (2 * MSEC_PER_SEC)
-#define POWER_ON_KEY_TIME       (2 * MSEC_PER_SEC)
+#define BATTERY_UNKNOWN_TIME (2 * MSEC_PER_SEC)
+#define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)
 #define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
 
-#define LAST_KMSG_PATH          "/proc/last_kmsg"
-#define LAST_KMSG_PSTORE_PATH   "/sys/fs/pstore/console-ramoops"
-#define LAST_KMSG_MAX_SZ        (32 * 1024)
+#define LAST_KMSG_PATH "/proc/last_kmsg"
+#define LAST_KMSG_PSTORE_PATH "/sys/fs/pstore/console-ramoops"
+#define LAST_KMSG_MAX_SZ (32 * 1024)
 
-#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
-#define LOGW(x...) do { KLOG_WARNING("charger", x); } while (0)
-#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0)
+#define LOGE(x...) KLOG_ERROR("charger", x);
+#define LOGW(x...) KLOG_WARNING("charger", x);
+#define LOGV(x...) KLOG_DEBUG("charger", x);
 
-static constexpr const char* animation_desc_path = "/res/values/charger/animation.txt";
+static constexpr const char* animation_desc_path =
+    "/res/values/charger/animation.txt";
 
 struct key_state {
     bool pending;
@@ -99,34 +96,36 @@
     int64_t next_key_check;
     int64_t next_pwr_check;
 
-    struct key_state keys[KEY_MAX + 1];
+    key_state keys[KEY_MAX + 1];
 
-    struct animation *batt_anim;
+    animation* batt_anim;
     GRSurface* surf_unknown;
     int boot_min_cap;
 };
 
-static const struct animation BASE_ANIMATION = {
-    .text_clock = {
-        .pos_x = 0,
-        .pos_y = 0,
+static const animation BASE_ANIMATION = {
+    .text_clock =
+        {
+            .pos_x = 0,
+            .pos_y = 0,
 
-        .color_r = 255,
-        .color_g = 255,
-        .color_b = 255,
-        .color_a = 255,
+            .color_r = 255,
+            .color_g = 255,
+            .color_b = 255,
+            .color_a = 255,
 
-        .font = nullptr,
-    },
-    .text_percent = {
-        .pos_x = 0,
-        .pos_y = 0,
+            .font = nullptr,
+        },
+    .text_percent =
+        {
+            .pos_x = 0,
+            .pos_y = 0,
 
-        .color_r = 255,
-        .color_g = 255,
-        .color_b = 255,
-        .color_a = 255,
-    },
+            .color_r = 255,
+            .color_g = 255,
+            .color_b = 255,
+            .color_a = 255,
+        },
 
     .run = false,
 
@@ -142,8 +141,7 @@
     .cur_status = BATTERY_STATUS_UNKNOWN,
 };
 
-
-static struct animation::frame default_animation_frames[] = {
+static animation::frame default_animation_frames[] = {
     {
         .disp_time = 750,
         .min_level = 0,
@@ -182,45 +180,35 @@
     },
 };
 
-static struct animation battery_animation = BASE_ANIMATION;
+static animation battery_animation = BASE_ANIMATION;
 
-static struct charger charger_state;
-static struct healthd_config *healthd_config;
-static struct android::BatteryProperties *batt_prop;
-static int char_width;
-static int char_height;
-static bool minui_inited;
+static charger charger_state;
+static healthd_config* healthd_config;
+static android::BatteryProperties* batt_prop;
+static std::unique_ptr<HealthdDraw> healthd_draw;
 
 /* current time in milliseconds */
-static int64_t curr_time_ms(void)
-{
-    struct timespec tm;
+static int64_t curr_time_ms() {
+    timespec tm;
     clock_gettime(CLOCK_MONOTONIC, &tm);
     return tm.tv_sec * MSEC_PER_SEC + (tm.tv_nsec / NSEC_PER_MSEC);
 }
 
-static void clear_screen(void)
-{
-    gr_color(0, 0, 0, 255);
-    gr_clear();
-}
-
 #define MAX_KLOG_WRITE_BUF_SZ 256
 
-static void dump_last_kmsg(void)
-{
-    char *buf;
-    char *ptr;
+static void dump_last_kmsg(void) {
+    char* buf;
+    char* ptr;
     unsigned sz = 0;
     int len;
 
     LOGW("\n");
     LOGW("*************** LAST KMSG ***************\n");
     LOGW("\n");
-    buf = (char *)load_file(LAST_KMSG_PSTORE_PATH, &sz);
+    buf = (char*)load_file(LAST_KMSG_PSTORE_PATH, &sz);
 
     if (!buf || !sz) {
-        buf = (char *)load_file(LAST_KMSG_PATH, &sz);
+        buf = (char*)load_file(LAST_KMSG_PATH, &sz);
         if (!buf || !sz) {
             LOGW("last_kmsg not found. Cold reset?\n");
             goto out;
@@ -233,11 +221,10 @@
     while (len > 0) {
         int cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);
         char yoink;
-        char *nl;
+        char* nl;
 
-        nl = (char *)memrchr(ptr, '\n', cnt - 1);
-        if (nl)
-            cnt = nl - ptr + 1;
+        nl = (char*)memrchr(ptr, '\n', cnt - 1);
+        if (nl) cnt = nl - ptr + 1;
 
         yoink = ptr[cnt];
         ptr[cnt] = '\0';
@@ -257,241 +244,59 @@
 }
 
 #ifdef CHARGER_ENABLE_SUSPEND
-static int request_suspend(bool enable)
-{
+static int request_suspend(bool enable) {
     if (enable)
         return autosuspend_enable();
     else
         return autosuspend_disable();
 }
 #else
-static int request_suspend(bool /*enable*/)
-{
+static int request_suspend(bool /*enable*/) {
     return 0;
 }
 #endif
 
-static int draw_text(const char *str, int x, int y)
-{
-    int str_len_px = gr_measure(gr_sys_font(), str);
-
-    if (x < 0)
-        x = (gr_fb_width() - str_len_px) / 2;
-    if (y < 0)
-        y = (gr_fb_height() - char_height) / 2;
-    gr_text(gr_sys_font(), x, y, str, 0);
-
-    return y + char_height;
-}
-
-static void android_green(void)
-{
-    gr_color(0xa4, 0xc6, 0x39, 255);
-}
-
-// Negative x or y coordinates position the text away from the opposite edge that positive ones do.
-void determine_xy(const animation::text_field& field, const int length, int* x, int* y)
-{
-    *x = field.pos_x;
-    *y = field.pos_y;
-
-    int str_len_px = length * field.font->char_width;
-    if (field.pos_x == CENTER_VAL) {
-        *x = (gr_fb_width() - str_len_px) / 2;
-    } else if (field.pos_x >= 0) {
-        *x = field.pos_x;
-    } else {  // position from max edge
-        *x = gr_fb_width() + field.pos_x - str_len_px;
-    }
-
-    if (field.pos_y == CENTER_VAL) {
-        *y = (gr_fb_height() - field.font->char_height) / 2;
-    } else if (field.pos_y >= 0) {
-        *y = field.pos_y;
-    } else {  // position from max edge
-        *y = gr_fb_height() + field.pos_y - field.font->char_height;
-    }
-}
-
-static void draw_clock(const animation& anim)
-{
-    static constexpr char CLOCK_FORMAT[] = "%H:%M";
-    static constexpr int CLOCK_LENGTH = 6;
-
-    const animation::text_field& field = anim.text_clock;
-
-    if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) return;
-
-    time_t rawtime;
-    time(&rawtime);
-    struct tm* time_info = localtime(&rawtime);
-
-    char clock_str[CLOCK_LENGTH];
-    size_t length = strftime(clock_str, CLOCK_LENGTH, CLOCK_FORMAT, time_info);
-    if (length != CLOCK_LENGTH - 1) {
-        LOGE("Could not format time\n");
-        return;
-    }
-
-    int x, y;
-    determine_xy(field, length, &x, &y);
-
-    LOGV("drawing clock %s %d %d\n", clock_str, x, y);
-    gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
-    gr_text(field.font, x, y, clock_str, false);
-}
-
-static void draw_percent(const animation& anim)
-{
-    int cur_level = anim.cur_level;
-    if (anim.cur_status == BATTERY_STATUS_FULL) {
-        cur_level = 100;
-    }
-
-    if (cur_level <= 0) return;
-
-    const animation::text_field& field = anim.text_percent;
-    if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {
-        return;
-    }
-
-    std::string str = base::StringPrintf("%d%%", cur_level);
-
-    int x, y;
-    determine_xy(field, str.size(), &x, &y);
-
-    LOGV("drawing percent %s %d %d\n", str.c_str(), x, y);
-    gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
-    gr_text(field.font, x, y, str.c_str(), false);
-}
-
-/* returns the last y-offset of where the surface ends */
-static int draw_surface_centered(GRSurface* surface)
-{
-    int w;
-    int h;
-    int x;
-    int y;
-
-    w = gr_get_width(surface);
-    h = gr_get_height(surface);
-    x = (gr_fb_width() - w) / 2 ;
-    y = (gr_fb_height() - h) / 2 ;
-
-    LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
-    gr_blit(surface, 0, 0, w, h, x, y);
-    return y + h;
-}
-
-static void draw_unknown(struct charger *charger)
-{
-    int y;
-    if (charger->surf_unknown) {
-        draw_surface_centered(charger->surf_unknown);
-    } else {
-        android_green();
-        y = draw_text("Charging!", -1, -1);
-        draw_text("?\?/100", -1, y + 25);
-    }
-}
-
-static void draw_battery(const struct charger* charger)
-{
-    const struct animation& anim = *charger->batt_anim;
-    const struct animation::frame& frame = anim.frames[anim.cur_frame];
-
-    if (anim.num_frames != 0) {
-        draw_surface_centered(frame.surface);
-        LOGV("drawing frame #%d min_cap=%d time=%d\n",
-             anim.cur_frame, frame.min_level,
-             frame.disp_time);
-    }
-    draw_clock(anim);
-    draw_percent(anim);
-}
-
-static void redraw_screen(struct charger *charger)
-{
-    struct animation *batt_anim = charger->batt_anim;
-
-    clear_screen();
-
-    /* try to display *something* */
-    if (batt_anim->cur_level < 0 || batt_anim->num_frames == 0)
-        draw_unknown(charger);
-    else
-        draw_battery(charger);
-    gr_flip();
-}
-
-static void kick_animation(struct animation *anim)
-{
+static void kick_animation(animation* anim) {
     anim->run = true;
 }
 
-static void reset_animation(struct animation *anim)
-{
+static void reset_animation(animation* anim) {
     anim->cur_cycle = 0;
     anim->cur_frame = 0;
     anim->run = false;
 }
 
-static void init_status_display(struct animation* anim)
-{
-    int res;
-
-    if (!anim->text_clock.font_file.empty()) {
-        if ((res =
-                gr_init_font(anim->text_clock.font_file.c_str(), &anim->text_clock.font)) < 0) {
-            LOGE("Could not load time font (%d)\n", res);
-        }
-    }
-
-    if (!anim->text_percent.font_file.empty()) {
-        if ((res =
-                gr_init_font(anim->text_percent.font_file.c_str(), &anim->text_percent.font)) < 0) {
-            LOGE("Could not load percent font (%d)\n", res);
-        }
-    }
-}
-
-static void update_screen_state(struct charger *charger, int64_t now)
-{
-    struct animation *batt_anim = charger->batt_anim;
+static void update_screen_state(charger* charger, int64_t now) {
+    animation* batt_anim = charger->batt_anim;
     int disp_time;
 
     if (!batt_anim->run || now < charger->next_screen_transition) return;
 
-    if (!minui_inited) {
+    if (healthd_draw == nullptr) {
         if (healthd_config && healthd_config->screen_on) {
             if (!healthd_config->screen_on(batt_prop)) {
                 LOGV("[%" PRId64 "] leave screen off\n", now);
                 batt_anim->run = false;
                 charger->next_screen_transition = -1;
-                if (charger->charger_connected)
-                    request_suspend(true);
+                if (charger->charger_connected) request_suspend(true);
                 return;
             }
         }
 
-        gr_init();
-        gr_font_size(gr_sys_font(), &char_width, &char_height);
-        init_status_display(batt_anim);
+        healthd_draw.reset(new HealthdDraw(batt_anim));
 
 #ifndef CHARGER_DISABLE_INIT_BLANK
-        gr_fb_blank(true);
+        healthd_draw->blank_screen(true);
 #endif
-        minui_inited = true;
     }
 
     /* animation is over, blank screen and leave */
     if (batt_anim->num_cycles > 0 && batt_anim->cur_cycle == batt_anim->num_cycles) {
         reset_animation(batt_anim);
         charger->next_screen_transition = -1;
-        gr_fb_blank(true);
+        healthd_draw->blank_screen(true);
         LOGV("[%" PRId64 "] animation done\n", now);
-        if (charger->charger_connected)
-            request_suspend(true);
+        if (charger->charger_connected) request_suspend(true);
         return;
     }
 
@@ -499,7 +304,6 @@
 
     /* animation starting, set up the animation */
     if (batt_anim->cur_frame == 0) {
-
         LOGV("[%" PRId64 "] animation starting\n", now);
         if (batt_prop) {
             batt_anim->cur_level = batt_prop->batteryLevel;
@@ -516,17 +320,16 @@
 
                 // repeat the first frame first_frame_repeats times
                 disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time *
-                    batt_anim->first_frame_repeats;
+                            batt_anim->first_frame_repeats;
             }
         }
     }
 
     /* unblank the screen  on first cycle */
-    if (batt_anim->cur_cycle == 0)
-        gr_fb_blank(false);
+    if (batt_anim->cur_cycle == 0) healthd_draw->blank_screen(false);
 
     /* draw the new frame (@ cur_frame) */
-    redraw_screen(charger);
+    healthd_draw->redraw_screen(charger->batt_anim, charger->surf_unknown);
 
     /* if we don't have anim frames, we only have one image, so just bump
      * the cycle counter and exit
@@ -570,22 +373,18 @@
     }
 }
 
-static int set_key_callback(struct charger *charger, int code, int value)
-{
+static int set_key_callback(charger* charger, int code, int value) {
     int64_t now = curr_time_ms();
     int down = !!value;
 
-    if (code > KEY_MAX)
-        return -1;
+    if (code > KEY_MAX) return -1;
 
     /* ignore events that don't modify our state */
-    if (charger->keys[code].down == down)
-        return 0;
+    if (charger->keys[code].down == down) return 0;
 
     /* only record the down even timestamp, as the amount
      * of time the key spent not being pressed is not useful */
-    if (down)
-        charger->keys[code].timestamp = now;
+    if (down) charger->keys[code].timestamp = now;
     charger->keys[code].down = down;
     charger->keys[code].pending = true;
     if (down) {
@@ -594,34 +393,27 @@
         int64_t duration = now - charger->keys[code].timestamp;
         int64_t secs = duration / 1000;
         int64_t msecs = duration - secs * 1000;
-        LOGV("[%" PRId64 "] key[%d] up (was down for %" PRId64 ".%" PRId64 "sec)\n",
-             now, code, secs, msecs);
+        LOGV("[%" PRId64 "] key[%d] up (was down for %" PRId64 ".%" PRId64 "sec)\n", now, code,
+             secs, msecs);
     }
 
     return 0;
 }
 
-static void update_input_state(struct charger *charger,
-                               struct input_event *ev)
-{
-    if (ev->type != EV_KEY)
-        return;
+static void update_input_state(charger* charger, input_event* ev) {
+    if (ev->type != EV_KEY) return;
     set_key_callback(charger, ev->code, ev->value);
 }
 
-static void set_next_key_check(struct charger *charger,
-                               struct key_state *key,
-                               int64_t timeout)
-{
+static void set_next_key_check(charger* charger, key_state* key, int64_t timeout) {
     int64_t then = key->timestamp + timeout;
 
     if (charger->next_key_check == -1 || then < charger->next_key_check)
         charger->next_key_check = then;
 }
 
-static void process_key(struct charger *charger, int code, int64_t now)
-{
-    struct key_state *key = &charger->keys[code];
+static void process_key(charger* charger, int code, int64_t now) {
+    key_state* key = &charger->keys[code];
 
     if (code == KEY_POWER) {
         if (key->down) {
@@ -638,8 +430,10 @@
                         LOGW("[%" PRId64 "] rebooting\n", now);
                         reboot(RB_AUTOBOOT);
                     } else {
-                        LOGV("[%" PRId64 "] ignore power-button press, battery level "
-                            "less than minimum\n", now);
+                        LOGV("[%" PRId64
+                             "] ignore power-button press, battery level "
+                             "less than minimum\n",
+                             now);
                     }
                 }
             } else {
@@ -648,9 +442,9 @@
                  */
                 set_next_key_check(charger, key, POWER_ON_KEY_TIME);
 
-               /* Turn on the display and kick animation on power-key press
-                * rather than on key release
-                */
+                /* Turn on the display and kick animation on power-key press
+                 * rather than on key release
+                 */
                 kick_animation(charger->batt_anim);
                 request_suspend(false);
             }
@@ -665,21 +459,17 @@
     key->pending = false;
 }
 
-static void handle_input_state(struct charger *charger, int64_t now)
-{
+static void handle_input_state(charger* charger, int64_t now) {
     process_key(charger, KEY_POWER, now);
 
     if (charger->next_key_check != -1 && now > charger->next_key_check)
         charger->next_key_check = -1;
 }
 
-static void handle_power_supply_state(struct charger *charger, int64_t now)
-{
-    if (!charger->have_battery_state)
-        return;
+static void handle_power_supply_state(charger* charger, int64_t now) {
+    if (!charger->have_battery_state) return;
 
     if (!charger->charger_connected) {
-
         /* Last cycle would have stopped at the extreme top of battery-icon
          * Need to show the correct level corresponding to capacity.
          */
@@ -705,9 +495,8 @@
     }
 }
 
-void healthd_mode_charger_heartbeat()
-{
-    struct charger *charger = &charger_state;
+void healthd_mode_charger_heartbeat() {
+    charger* charger = &charger_state;
     int64_t now = curr_time_ms();
 
     handle_input_state(charger, now);
@@ -719,14 +508,11 @@
     update_screen_state(charger, now);
 }
 
-void healthd_mode_charger_battery_update(
-    struct android::BatteryProperties *props)
-{
-    struct charger *charger = &charger_state;
+void healthd_mode_charger_battery_update(android::BatteryProperties* props) {
+    charger* charger = &charger_state;
 
     charger->charger_connected =
-        props->chargerAcOnline || props->chargerUsbOnline ||
-        props->chargerWirelessOnline;
+        props->chargerAcOnline || props->chargerUsbOnline || props->chargerWirelessOnline;
 
     if (!charger->have_battery_state) {
         charger->have_battery_state = true;
@@ -737,19 +523,16 @@
     batt_prop = props;
 }
 
-int healthd_mode_charger_preparetowait(void)
-{
-    struct charger *charger = &charger_state;
+int healthd_mode_charger_preparetowait(void) {
+    charger* charger = &charger_state;
     int64_t now = curr_time_ms();
     int64_t next_event = INT64_MAX;
     int64_t timeout;
 
-    LOGV("[%" PRId64 "] next screen: %" PRId64 " next key: %" PRId64 " next pwr: %" PRId64 "\n", now,
-         charger->next_screen_transition, charger->next_key_check,
-         charger->next_pwr_check);
+    LOGV("[%" PRId64 "] next screen: %" PRId64 " next key: %" PRId64 " next pwr: %" PRId64 "\n",
+         now, charger->next_screen_transition, charger->next_key_check, charger->next_pwr_check);
 
-    if (charger->next_screen_transition != -1)
-        next_event = charger->next_screen_transition;
+    if (charger->next_screen_transition != -1) next_event = charger->next_screen_transition;
     if (charger->next_key_check != -1 && charger->next_key_check < next_event)
         next_event = charger->next_key_check;
     if (charger->next_pwr_check != -1 && charger->next_pwr_check < next_event)
@@ -760,32 +543,27 @@
     else
         timeout = -1;
 
-   return (int)timeout;
+    return (int)timeout;
 }
 
-static int input_callback(struct charger *charger, int fd, unsigned int epevents)
-{
-    struct input_event ev;
+static int input_callback(charger* charger, int fd, unsigned int epevents) {
+    input_event ev;
     int ret;
 
     ret = ev_get_input(fd, epevents, &ev);
-    if (ret)
-        return -1;
+    if (ret) return -1;
     update_input_state(charger, &ev);
     return 0;
 }
 
-static void charger_event_handler(uint32_t /*epevents*/)
-{
+static void charger_event_handler(uint32_t /*epevents*/) {
     int ret;
 
     ret = ev_wait(-1);
-    if (!ret)
-        ev_dispatch();
+    if (!ret) ev_dispatch();
 }
 
-animation* init_animation()
-{
+animation* init_animation() {
     bool parse_success;
 
     std::string content;
@@ -808,32 +586,29 @@
     }
 
     LOGV("Animation Description:\n");
-    LOGV("  animation: %d %d '%s' (%d)\n",
-        battery_animation.num_cycles, battery_animation.first_frame_repeats,
-        battery_animation.animation_file.c_str(), battery_animation.num_frames);
+    LOGV("  animation: %d %d '%s' (%d)\n", battery_animation.num_cycles,
+         battery_animation.first_frame_repeats, battery_animation.animation_file.c_str(),
+         battery_animation.num_frames);
     LOGV("  fail_file: '%s'\n", battery_animation.fail_file.c_str());
-    LOGV("  clock: %d %d %d %d %d %d '%s'\n",
-        battery_animation.text_clock.pos_x, battery_animation.text_clock.pos_y,
-        battery_animation.text_clock.color_r, battery_animation.text_clock.color_g,
-        battery_animation.text_clock.color_b, battery_animation.text_clock.color_a,
-        battery_animation.text_clock.font_file.c_str());
-    LOGV("  percent: %d %d %d %d %d %d '%s'\n",
-        battery_animation.text_percent.pos_x, battery_animation.text_percent.pos_y,
-        battery_animation.text_percent.color_r, battery_animation.text_percent.color_g,
-        battery_animation.text_percent.color_b, battery_animation.text_percent.color_a,
-        battery_animation.text_percent.font_file.c_str());
+    LOGV("  clock: %d %d %d %d %d %d '%s'\n", battery_animation.text_clock.pos_x,
+         battery_animation.text_clock.pos_y, battery_animation.text_clock.color_r,
+         battery_animation.text_clock.color_g, battery_animation.text_clock.color_b,
+         battery_animation.text_clock.color_a, battery_animation.text_clock.font_file.c_str());
+    LOGV("  percent: %d %d %d %d %d %d '%s'\n", battery_animation.text_percent.pos_x,
+         battery_animation.text_percent.pos_y, battery_animation.text_percent.color_r,
+         battery_animation.text_percent.color_g, battery_animation.text_percent.color_b,
+         battery_animation.text_percent.color_a, battery_animation.text_percent.font_file.c_str());
     for (int i = 0; i < battery_animation.num_frames; i++) {
         LOGV("  frame %.2d: %d %d %d\n", i, battery_animation.frames[i].disp_time,
-            battery_animation.frames[i].min_level, battery_animation.frames[i].max_level);
+             battery_animation.frames[i].min_level, battery_animation.frames[i].max_level);
     }
 
     return &battery_animation;
 }
 
-void healthd_mode_charger_init(struct healthd_config* config)
-{
+void healthd_mode_charger_init(struct healthd_config* config) {
     int ret;
-    struct charger *charger = &charger_state;
+    charger* charger = &charger_state;
     int i;
     int epollfd;
 
@@ -841,14 +616,13 @@
 
     LOGW("--------------- STARTING CHARGER MODE ---------------\n");
 
-    ret = ev_init(std::bind(&input_callback, charger, std::placeholders::_1,
-                            std::placeholders::_2));
+    ret = ev_init(std::bind(&input_callback, charger, std::placeholders::_1, std::placeholders::_2));
     if (!ret) {
         epollfd = ev_get_epollfd();
         healthd_register_event(epollfd, charger_event_handler, EVENT_WAKEUP_FD);
     }
 
-    struct animation* anim = init_animation();
+    animation* anim = init_animation();
     charger->batt_anim = anim;
 
     ret = res_create_display_surface(anim->fail_file.c_str(), &charger->surf_unknown);
@@ -865,15 +639,15 @@
     int scale_count;
     int scale_fps;  // Not in use (charger/battery_scale doesn't have FPS text
                     // chunk). We are using hard-coded frame.disp_time instead.
-    ret = res_create_multi_display_surface(anim->animation_file.c_str(),
-        &scale_count, &scale_fps, &scale_frames);
+    ret = res_create_multi_display_surface(anim->animation_file.c_str(), &scale_count, &scale_fps,
+                                           &scale_frames);
     if (ret < 0) {
         LOGE("Cannot load battery_scale image\n");
         anim->num_frames = 0;
         anim->num_cycles = 1;
     } else if (scale_count != anim->num_frames) {
-        LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n",
-             scale_count, anim->num_frames);
+        LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n", scale_count,
+             anim->num_frames);
         anim->num_frames = 0;
         anim->num_cycles = 1;
     } else {
@@ -881,8 +655,8 @@
             anim->frames[i].surface = scale_frames[i];
         }
     }
-    ev_sync_key_state(std::bind(&set_key_callback, charger, std::placeholders::_1,
-                                std::placeholders::_2));
+    ev_sync_key_state(
+        std::bind(&set_key_callback, charger, std::placeholders::_1, std::placeholders::_2));
 
     charger->next_screen_transition = -1;
     charger->next_key_check = -1;
diff --git a/include/system b/include/system
index b443c36..91d45be 120000
--- a/include/system
+++ b/include/system
@@ -1 +1 @@
-../libsystem/include/system
\ No newline at end of file
+../libsystem/include/system/
\ No newline at end of file
diff --git a/init/Android.bp b/init/Android.bp
new file mode 100644
index 0000000..5d6d979
--- /dev/null
+++ b/init/Android.bp
@@ -0,0 +1,171 @@
+//
+// 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_defaults {
+    name: "init_defaults",
+    cpp_std: "experimental",
+    sanitize: {
+        misc_undefined: ["integer"],
+    },
+    tidy_checks: ["-misc-forwarding-reference-overload"],
+    cppflags: [
+        "-DLOG_UEVENTS=0",
+        "-Wall",
+        "-Wextra",
+        "-Wno-unused-parameter",
+        "-Werror",
+        "-DALLOW_LOCAL_PROP_OVERRIDE=0",
+        "-DALLOW_PERMISSIVE_SELINUX=0",
+        "-DREBOOT_BOOTLOADER_ON_PANIC=0",
+        "-DWORLD_WRITABLE_KMSG=0",
+        "-DDUMP_ON_UMOUNT_FAILURE=0",
+        "-DSHUTDOWN_ZERO_TIMEOUT=0",
+    ],
+    product_variables: {
+        debuggable: {
+            cppflags: [
+                "-UALLOW_LOCAL_PROP_OVERRIDE",
+                "-DALLOW_LOCAL_PROP_OVERRIDE=1",
+                "-UALLOW_PERMISSIVE_SELINUX",
+                "-DALLOW_PERMISSIVE_SELINUX=1",
+                "-UREBOOT_BOOTLOADER_ON_PANIC",
+                "-DREBOOT_BOOTLOADER_ON_PANIC=1",
+                "-UWORLD_WRITABLE_KMSG",
+                "-DWORLD_WRITABLE_KMSG=1",
+                "-UDUMP_ON_UMOUNT_FAILURE",
+                "-DDUMP_ON_UMOUNT_FAILURE=1",
+            ],
+        },
+        eng: {
+            cppflags: [
+                "-USHUTDOWN_ZERO_TIMEOUT",
+                "-DSHUTDOWN_ZERO_TIMEOUT=1",
+            ],
+        },
+    },
+}
+
+cc_library_static {
+    name: "libinit",
+    defaults: ["init_defaults"],
+    srcs: [
+        "action.cpp",
+        "capabilities.cpp",
+        "descriptors.cpp",
+        "devices.cpp",
+        "firmware_handler.cpp",
+        "import_parser.cpp",
+        "init_parser.cpp",
+        "log.cpp",
+        "parser.cpp",
+        "service.cpp",
+        "uevent_listener.cpp",
+        "ueventd_parser.cpp",
+        "util.cpp",
+    ],
+    whole_static_libs: ["libcap"],
+    static_libs: [
+        "libbase",
+        "libselinux",
+        "liblog",
+        "libprocessgroup",
+    ],
+}
+
+/*
+This is not yet ready, see the below TODOs for what is missing
+
+cc_binary {
+    // TODO: Missing,
+    //LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+    //LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
+
+    name: "init",
+    defaults: ["init_defaults"],
+    required: [
+        "e2fsdroid",
+        "mke2fs",
+    ],
+    static_executable: true,
+    srcs: [
+        "bootchart.cpp",
+        "builtins.cpp",
+        "init.cpp",
+        "init_first_stage.cpp",
+        "keychords.cpp",
+        "property_service.cpp",
+        "reboot.cpp",
+        "signal_handler.cpp",
+        "ueventd.cpp",
+        "watchdogd.cpp",
+    ],
+    include_dirs: [
+        "system/core/mkbootimg"
+    ],
+    static_libs: [
+        "libinit",
+        "libbootloader_message",
+        "libfs_mgr",
+        "libfec",
+        "libfec_rs",
+        "libsquashfs_utils",
+        "liblogwrap",
+        "libext4_utils",
+        "libcutils",
+        "libbase",
+        "libc",
+        "libselinux",
+        "liblog",
+        "libcrypto_utils",
+        "libcrypto",
+        "libc++_static",
+        "libdl",
+        "libsparse",
+        "libz",
+        "libprocessgroup",
+        "libavb",
+        "libkeyutils",
+    ],
+    symlinks: [
+        "sbin/ueventd",
+        "sbin/watchdogd",
+    ],
+}
+*/
+
+// Tests
+// ------------------------------------------------------------------------------
+
+cc_test {
+    name: "init_tests",
+    defaults: ["init_defaults"],
+    srcs: [
+        "devices_test.cpp",
+        "init_parser_test.cpp",
+        "init_test.cpp",
+        "property_service_test.cpp",
+        "service_test.cpp",
+        "util_test.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libselinux",
+    ],
+    static_libs: ["libinit"],
+}
+
+subdirs = ["*"]
diff --git a/init/Android.mk b/init/Android.mk
index 42e42ec..bf75f5a 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -37,53 +37,13 @@
 
 # --
 
-# If building on Linux, then build unit test for the host.
-ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
-LOCAL_CPPFLAGS := $(init_cflags)
-LOCAL_SRC_FILES:= \
-    parser/tokenizer.cpp \
-
-LOCAL_MODULE := libinit_parser
-LOCAL_CLANG := true
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := init_parser_tests
-LOCAL_SRC_FILES := \
-    parser/tokenizer_test.cpp \
-
-LOCAL_STATIC_LIBRARIES := libinit_parser
-LOCAL_CLANG := true
-include $(BUILD_HOST_NATIVE_TEST)
-endif
-
-include $(CLEAR_VARS)
-LOCAL_CPPFLAGS := $(init_cflags)
-LOCAL_SRC_FILES:= \
-    action.cpp \
-    capabilities.cpp \
-    descriptors.cpp \
-    import_parser.cpp \
-    init_parser.cpp \
-    log.cpp \
-    parser.cpp \
-    service.cpp \
-    util.cpp \
-
-LOCAL_STATIC_LIBRARIES := libbase libselinux liblog libprocessgroup
-LOCAL_WHOLE_STATIC_LIBRARIES := libcap
-LOCAL_MODULE := libinit
-LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
+# b/38002385, work around clang-tidy segmentation fault.
+LOCAL_TIDY_CHECKS := -misc-forwarding-reference-overload
 LOCAL_CPPFLAGS := $(init_cflags)
 LOCAL_SRC_FILES:= \
     bootchart.cpp \
     builtins.cpp \
-    devices.cpp \
     init.cpp \
     init_first_stage.cpp \
     keychords.cpp \
@@ -91,7 +51,6 @@
     reboot.cpp \
     signal_handler.cpp \
     ueventd.cpp \
-    ueventd_parser.cpp \
     watchdogd.cpp \
 
 LOCAL_MODULE:= init
@@ -123,7 +82,12 @@
     libsparse \
     libz \
     libprocessgroup \
-    libavb
+    libavb \
+    libkeyutils \
+
+LOCAL_REQUIRED_MODULES := \
+    e2fsdroid \
+    mke2fs \
 
 # Create symlinks.
 LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
@@ -133,29 +97,3 @@
 LOCAL_SANITIZE := integer
 LOCAL_CLANG := true
 include $(BUILD_EXECUTABLE)
-
-
-# Unit tests.
-# =========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := init_tests
-LOCAL_SRC_FILES := \
-    init_parser_test.cpp \
-    property_service_test.cpp \
-    service_test.cpp \
-    util_test.cpp \
-
-LOCAL_SHARED_LIBRARIES += \
-    libcutils \
-    libbase \
-
-LOCAL_STATIC_LIBRARIES := libinit
-LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
-LOCAL_CPPFLAGS := -Wall -Wextra -Werror
-include $(BUILD_NATIVE_TEST)
-
-
-# Include targets in subdirs.
-# =========================================================
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/init/README.md b/init/README.md
index 8cb1e52..422fdad 100644
--- a/init/README.md
+++ b/init/README.md
@@ -16,9 +16,7 @@
 or options belong to the section most recently declared.  Commands
 or options before the first section are ignored.
 
-Actions and Services have unique names.  If a second Action is defined
-with the same name as an existing one, its commands are appended to
-the commands of the existing action.  If a second Service is defined
+Services have unique names.  If a second Service is defined
 with the same name as an existing one, it is ignored and an error
 message is logged.
 
@@ -31,13 +29,21 @@
 
 /init.rc is the primary .rc file and is loaded by the init executable
 at the beginning of its execution.  It is responsible for the initial
-set up of the system.  It imports /init.${ro.hardware}.rc which is the
-primary vendor supplied .rc file.
+set up of the system.
 
-During the mount\_all command, the init executable loads all of the
-files contained within the /{system,vendor,odm}/etc/init/ directories.
-These directories are intended for all Actions and Services used after
-file system mounting.
+Devices that mount /system, /vendor through the first stage mount mechanism
+load all of the files contained within the
+/{system,vendor,odm}/etc/init/ directories immediately after loading
+the primary /init.rc.  This is explained in more details in the
+Imports section of this file.
+
+Legacy devices without the first stage mount mechanism do the following:
+1. /init.rc imports /init.${ro.hardware}.rc which is the primary
+   vendor supplied .rc file.
+2. During the mount\_all command, the init executable loads all of the
+   files contained within the /{system,vendor,odm}/etc/init/ directories.
+   These directories are intended for all Actions and Services used after
+   file system mounting.
 
 One may specify paths in the mount\_all command line to have it import
 .rc files at the specified paths instead of the default ones listed above.
@@ -89,7 +95,7 @@
 Actions
 -------
 Actions are named sequences of commands.  Actions have a trigger which
-is used to determine when the action should occur.  When an event
+is used to determine when the action is executed.  When an event
 occurs which matches an action's trigger, that action is added to
 the tail of a to-be-executed queue (unless it is already on the
 queue).
@@ -106,6 +112,34 @@
        <command>
        <command>
 
+Actions are added to the queue and executed based on the order that
+the file that contains them was parsed (see the Imports section), then
+sequentially within an individual file.
+
+For example if a file contains:
+
+    on boot
+       setprop a 1
+       setprop b 2
+
+    on boot && property:true=true
+       setprop c 1
+       setprop d 2
+
+    on boot
+       setprop e 1
+       setprop f 2
+
+Then when the `boot` trigger occurs and assuming the property `true`
+equals `true`, then the order of the commands executed will be:
+
+    setprop a 1
+    setprop b 2
+    setprop c 1
+    setprop d 2
+    setprop e 1
+    setprop f 2
+
 
 Services
 --------
@@ -226,6 +260,14 @@
 > Sets the child's /proc/self/oom\_score\_adj to the specified value,
   which must range from -1000 to 1000.
 
+`shutdown <shutdown_behavior>`
+> Set shutdown behavior of the service process. When this is not specified,
+  the service is killed during shutdown process by using SIGTERM and SIGKILL.
+  The service with shutdown_behavior of "critical" is not killed during shutdown
+  until shutdown times out. When shutdown times out, even services tagged with
+  "shutdown critical" will be killed. When the service tagged with "shutdown critical"
+  is not running when shut down starts, it will be started.
+
 
 Triggers
 --------
@@ -276,7 +318,8 @@
 
 `class_start <serviceclass>`
 > Start all services of the specified class if they are
-  not already running.
+  not already running.  See the start entry for more information on
+  starting services.
 
 `class_stop <serviceclass>`
 > Stop and disable all services of the specified class if they are
@@ -321,9 +364,9 @@
   Init halts executing commands until the forked process exits.
 
 `exec_start <service>`
-> Start service a given service and halt processing of additional init commands
-  until it returns.  It functions similarly to the `exec` command, but uses an
-  existing service definition instead of providing them as arguments.
+> Start a given service and halt the processing of additional init commands
+  until it returns.  The command functions similarly to the `exec` command,
+  but uses an existing service definition in place of the exec argument vector.
 
 `export <name> <value>`
 > Set the environment variable _name_ equal to _value_ in the
@@ -401,6 +444,16 @@
 
 `start <service>`
 > Start a service running if it is not already running.
+  Note that this is _not_ synchronous, and even if it were, there is
+  no guarantee that the operating system's scheduler will execute the
+  service sufficiently to guarantee anything about the service's status.
+
+> This creates an important consequence that if the service offers
+  functionality to other services, such as providing a
+  communication channel, simply starting this service before those
+  services is _not_ sufficient to guarantee that the channel has
+  been set up before those services ask for it.  There must be a
+  separate mechanism to make any such guarantees.
 
 `stop <service>`
 > Stop a service from running if it is currently running.
@@ -447,21 +500,54 @@
 
 Imports
 -------
-The import keyword is not a command, but rather its own section and is
-handled immediately after the .rc file that contains it has finished
-being parsed.  It takes the below form:
-
 `import <path>`
 > Parse an init config file, extending the current configuration.
   If _path_ is a directory, each file in the directory is parsed as
   a config file. It is not recursive, nested directories will
   not be parsed.
 
-There are only two times where the init executable imports .rc files:
+The import keyword is not a command, but rather its own section,
+meaning that it does not happen as part of an Action, but rather,
+imports are handled as a file is being parsed and follow the below logic.
 
-   1. When it imports /init.rc during initial boot
-   2. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
-      paths during mount_all
+There are only three times where the init executable imports .rc files:
+
+   1. When it imports /init.rc or the script indicated by the property
+      `ro.boot.init_rc` during initial boot.
+   2. When it imports /{system,vendor,odm}/etc/init/ for first stage mount
+      devices immediately after importing /init.rc.
+   3. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
+      paths during mount_all.
+
+The order that files are imported is a bit complex for legacy reasons
+and to keep backwards compatibility.  It is not strictly guaranteed.
+
+The only correct way to guarantee that a command has been run before a
+different command is to either 1) place it in an Action with an
+earlier executed trigger, or 2) place it in an Action with the same
+trigger within the same file at an earlier line.
+
+Nonetheless, the defacto order for first stage mount devices is:
+1. /init.rc is parsed then recursively each of its imports are
+   parsed.
+2. The contents of /system/etc/init/ are alphabetized and parsed
+   sequentially, with imports happening recursively after each file is
+   parsed.
+3. Step 2 is repeated for /vendor/etc/init then /odm/etc/init
+
+The below pseudocode may explain this more clearly:
+
+    fn Import(file)
+      Parse(file)
+      for (import : file.imports)
+        Import(import)
+
+    Import(/init.rc)
+    Directories = [/system/etc/init, /vendor/etc/init, /odm/etc/init]
+    for (directory : Directories)
+      files = <Alphabetical order of directory's contents>
+      for (file : files)
+        Import(file)
 
 
 Properties
diff --git a/init/action.cpp b/init/action.cpp
index 2ccf0bc..41bd061 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -16,25 +16,18 @@
 
 #include "action.h"
 
-#include <errno.h>
-
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
-#include "builtins.h"
-#include "error.h"
-#include "init_parser.h"
-#include "log.h"
 #include "util.h"
 
 using android::base::Join;
 using android::base::StringPrintf;
 
-Command::Command(BuiltinFunction f, const std::vector<std::string>& args,
-                 const std::string& filename, int line)
-    : func_(f), args_(args), filename_(filename), line_(line) {
-}
+Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
+    : func_(f), args_(args), line_(line) {}
 
 int Command::InvokeFunc() const {
     std::vector<std::string> expanded_args;
@@ -54,50 +47,28 @@
     return Join(args_, ' ');
 }
 
-std::string Command::BuildSourceString() const {
-    if (!filename_.empty()) {
-        return StringPrintf(" (%s:%d)", filename_.c_str(), line_);
-    } else {
-        return std::string();
-    }
-}
-
-Action::Action(bool oneshot) : oneshot_(oneshot) {
-}
+Action::Action(bool oneshot, const std::string& filename, int line)
+    : oneshot_(oneshot), filename_(filename), line_(line) {}
 
 const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
 
-bool Action::AddCommand(const std::vector<std::string>& args,
-                        const std::string& filename, int line, std::string* err) {
+bool Action::AddCommand(const std::vector<std::string>& args, int line, std::string* err) {
     if (!function_map_) {
         *err = "no function map available";
         return false;
     }
 
-    if (args.empty()) {
-        *err = "command needed, but not provided";
-        return false;
-    }
-
-    auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
+    auto function = function_map_->FindFunction(args, err);
     if (!function) {
         return false;
     }
 
-    AddCommand(function, args, filename, line);
+    AddCommand(function, args, line);
     return true;
 }
 
-void Action::AddCommand(BuiltinFunction f,
-                        const std::vector<std::string>& args,
-                        const std::string& filename, int line) {
-    commands_.emplace_back(f, args, filename, line);
-}
-
-void Action::CombineAction(const Action& action) {
-    for (const auto& c : action.commands_) {
-        commands_.emplace_back(c);
-    }
+void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
+    commands_.emplace_back(f, args, line);
 }
 
 std::size_t Action::NumCommands() const {
@@ -127,7 +98,7 @@
         android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
         std::string trigger_name = BuildTriggersString();
         std::string cmd_str = command.BuildCommandString();
-        std::string source = command.BuildSourceString();
+        std::string source = StringPrintf(" (%s:%d)", filename_.c_str(), command.line());
 
         LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << source
                   << " returned " << result << " took " << duration_ms << "ms.";
@@ -228,20 +199,17 @@
     return found;
 }
 
-bool Action::CheckEventTrigger(const std::string& trigger) const {
-    return !event_trigger_.empty() &&
-        trigger == event_trigger_ &&
-        CheckPropertyTriggers();
+bool Action::CheckEvent(const EventTrigger& event_trigger) const {
+    return event_trigger == event_trigger_ && CheckPropertyTriggers();
 }
 
-bool Action::CheckPropertyTrigger(const std::string& name,
-                                  const std::string& value) const {
+bool Action::CheckEvent(const PropertyChange& property_change) const {
+    const auto& [name, value] = property_change;
     return event_trigger_.empty() && CheckPropertyTriggers(name, value);
 }
 
-bool Action::TriggersEqual(const Action& other) const {
-    return property_triggers_ == other.property_triggers_ &&
-        event_trigger_ == other.event_trigger_;
+bool Action::CheckEvent(const BuiltinAction& builtin_action) const {
+    return this == builtin_action;
 }
 
 std::string Action::BuildTriggersString() const {
@@ -267,41 +235,6 @@
     }
 }
 
-class EventTrigger : public Trigger {
-public:
-    explicit EventTrigger(const std::string& trigger) : trigger_(trigger) {
-    }
-    bool CheckTriggers(const Action& action) const override {
-        return action.CheckEventTrigger(trigger_);
-    }
-private:
-    const std::string trigger_;
-};
-
-class PropertyTrigger : public Trigger {
-public:
-    PropertyTrigger(const std::string& name, const std::string& value)
-        : name_(name), value_(value) {
-    }
-    bool CheckTriggers(const Action& action) const override {
-        return action.CheckPropertyTrigger(name_, value_);
-    }
-private:
-    const std::string name_;
-    const std::string value_;
-};
-
-class BuiltinTrigger : public Trigger {
-public:
-    explicit BuiltinTrigger(Action* action) : action_(action) {
-    }
-    bool CheckTriggers(const Action& action) const override {
-        return action_ == &action;
-    }
-private:
-    const Action* action_;
-};
-
 ActionManager::ActionManager() : current_command_(0) {
 }
 
@@ -311,56 +244,45 @@
 }
 
 void ActionManager::AddAction(std::unique_ptr<Action> action) {
-    auto old_action_it =
-        std::find_if(actions_.begin(), actions_.end(),
-                     [&action] (std::unique_ptr<Action>& a) {
-                         return action->TriggersEqual(*a);
-                     });
-
-    if (old_action_it != actions_.end()) {
-        (*old_action_it)->CombineAction(*action);
-    } else {
-        actions_.emplace_back(std::move(action));
-    }
+    actions_.emplace_back(std::move(action));
 }
 
 void ActionManager::QueueEventTrigger(const std::string& trigger) {
-    trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
+    event_queue_.emplace(trigger);
 }
 
-void ActionManager::QueuePropertyTrigger(const std::string& name,
-                                         const std::string& value) {
-    trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
+void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
+    event_queue_.emplace(std::make_pair(name, value));
 }
 
-void ActionManager::QueueAllPropertyTriggers() {
-    QueuePropertyTrigger("", "");
+void ActionManager::QueueAllPropertyActions() {
+    QueuePropertyChange("", "");
 }
 
-void ActionManager::QueueBuiltinAction(BuiltinFunction func,
-                                       const std::string& name) {
-    auto action = std::make_unique<Action>(true);
+void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
+    auto action = std::make_unique<Action>(true, "<Builtin Action>", 0);
     std::vector<std::string> name_vector{name};
 
     if (!action->InitSingleTrigger(name)) {
         return;
     }
 
-    action->AddCommand(func, name_vector);
+    action->AddCommand(func, name_vector, 0);
 
-    trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
+    event_queue_.emplace(action.get());
     actions_.emplace_back(std::move(action));
 }
 
 void ActionManager::ExecuteOneCommand() {
-    // Loop through the trigger queue until we have an action to execute
-    while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
+    // Loop through the event queue until we have an action to execute
+    while (current_executing_actions_.empty() && !event_queue_.empty()) {
         for (const auto& action : actions_) {
-            if (trigger_queue_.front()->CheckTriggers(*action)) {
+            if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
+                           event_queue_.front())) {
                 current_executing_actions_.emplace(action.get());
             }
         }
-        trigger_queue_.pop();
+        event_queue_.pop();
     }
 
     if (current_executing_actions_.empty()) {
@@ -371,7 +293,8 @@
 
     if (current_command_ == 0) {
         std::string trigger_name = action->BuildTriggersString();
-        LOG(INFO) << "processing action (" << trigger_name << ")";
+        LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
+                  << ":" << action->line() << ")";
     }
 
     action->ExecuteOneCommand(current_command_);
@@ -393,7 +316,7 @@
 }
 
 bool ActionManager::HasMoreCommands() const {
-    return !current_executing_actions_.empty() || !trigger_queue_.empty();
+    return !current_executing_actions_.empty() || !event_queue_.empty();
 }
 
 void ActionManager::DumpState() const {
@@ -402,15 +325,22 @@
     }
 }
 
-bool ActionParser::ParseSection(const std::vector<std::string>& args,
-                                std::string* err) {
+void ActionManager::ClearQueue() {
+    // We are shutting down so don't claim the oneshot builtin actions back
+    current_executing_actions_ = {};
+    event_queue_ = {};
+    current_command_ = 0;
+}
+
+bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                int line, std::string* err) {
     std::vector<std::string> triggers(args.begin() + 1, args.end());
     if (triggers.size() < 1) {
         *err = "actions must have a trigger";
         return false;
     }
 
-    auto action = std::make_unique<Action>(false);
+    auto action = std::make_unique<Action>(false, filename, line);
     if (!action->InitTriggers(triggers, err)) {
         return false;
     }
@@ -419,14 +349,12 @@
     return true;
 }
 
-bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
-                                    const std::string& filename, int line,
-                                    std::string* err) const {
-    return action_ ? action_->AddCommand(args, filename, line, err) : false;
+bool ActionParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+    return action_ ? action_->AddCommand(std::move(args), line, err) : false;
 }
 
 void ActionParser::EndSection() {
     if (action_ && action_->NumCommands() > 0) {
-        ActionManager::GetInstance().AddAction(std::move(action_));
+        action_manager_->AddAction(std::move(action_));
     }
 }
diff --git a/init/action.h b/init/action.h
index 0bae9f0..c04076a 100644
--- a/init/action.h
+++ b/init/action.h
@@ -20,6 +20,7 @@
 #include <map>
 #include <queue>
 #include <string>
+#include <variant>
 #include <vector>
 
 #include "builtins.h"
@@ -27,44 +28,44 @@
 #include "keyword_map.h"
 
 class Command {
-public:
-    Command(BuiltinFunction f, const std::vector<std::string>& args,
-            const std::string& filename, int line);
+  public:
+    Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
 
     int InvokeFunc() const;
     std::string BuildCommandString() const;
-    std::string BuildSourceString() const;
 
-private:
+    int line() const { return line_; }
+
+  private:
     BuiltinFunction func_;
     std::vector<std::string> args_;
-    std::string filename_;
     int line_;
 };
 
-class Action {
-public:
-    explicit Action(bool oneshot = false);
+using EventTrigger = std::string;
+using PropertyChange = std::pair<std::string, std::string>;
+using BuiltinAction = class Action*;
 
-    bool AddCommand(const std::vector<std::string>& args,
-                    const std::string& filename, int line, std::string* err);
-    void AddCommand(BuiltinFunction f,
-                    const std::vector<std::string>& args,
-                    const std::string& filename = "", int line = 0);
-    void CombineAction(const Action& action);
+class Action {
+  public:
+    explicit Action(bool oneshot, const std::string& filename, int line);
+
+    bool AddCommand(const std::vector<std::string>& args, int line, std::string* err);
+    void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
     bool InitTriggers(const std::vector<std::string>& args, std::string* err);
     bool InitSingleTrigger(const std::string& trigger);
     std::size_t NumCommands() const;
     void ExecuteOneCommand(std::size_t command) const;
     void ExecuteAllCommands() const;
-    bool CheckEventTrigger(const std::string& trigger) const;
-    bool CheckPropertyTrigger(const std::string& name,
-                              const std::string& value) const;
-    bool TriggersEqual(const Action& other) const;
+    bool CheckEvent(const EventTrigger& event_trigger) const;
+    bool CheckEvent(const PropertyChange& property_change) const;
+    bool CheckEvent(const BuiltinAction& builtin_action) const;
     std::string BuildTriggersString() const;
     void DumpState() const;
 
     bool oneshot() const { return oneshot_; }
+    const std::string& filename() const { return filename_; }
+    int line() const { return line_; }
     static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
         function_map_ = function_map;
     }
@@ -80,53 +81,49 @@
     std::string event_trigger_;
     std::vector<Command> commands_;
     bool oneshot_;
+    std::string filename_;
+    int line_;
     static const KeywordMap<BuiltinFunction>* function_map_;
 };
 
-class Trigger {
-public:
-    virtual ~Trigger() { }
-    virtual bool CheckTriggers(const Action& action) const = 0;
-};
-
 class ActionManager {
-public:
+  public:
     static ActionManager& GetInstance();
 
+    // Exposed for testing
+    ActionManager();
+
     void AddAction(std::unique_ptr<Action> action);
     void QueueEventTrigger(const std::string& trigger);
-    void QueuePropertyTrigger(const std::string& name, const std::string& value);
-    void QueueAllPropertyTriggers();
+    void QueuePropertyChange(const std::string& name, const std::string& value);
+    void QueueAllPropertyActions();
     void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
     void ExecuteOneCommand();
     bool HasMoreCommands() const;
     void DumpState() const;
+    void ClearQueue();
 
-private:
-    ActionManager();
-
+  private:
     ActionManager(ActionManager const&) = delete;
     void operator=(ActionManager const&) = delete;
 
     std::vector<std::unique_ptr<Action>> actions_;
-    std::queue<std::unique_ptr<Trigger>> trigger_queue_;
+    std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
     std::queue<const Action*> current_executing_actions_;
     std::size_t current_command_;
 };
 
 class ActionParser : public SectionParser {
-public:
-    ActionParser() : action_(nullptr) {
-    }
-    bool ParseSection(const std::vector<std::string>& args,
+  public:
+    ActionParser(ActionManager* action_manager)
+        : action_manager_(action_manager), action_(nullptr) {}
+    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
                       std::string* err) override;
-    bool ParseLineSection(const std::vector<std::string>& args,
-                          const std::string& filename, int line,
-                          std::string* err) const override;
+    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
     void EndSection() override;
-    void EndFile(const std::string&) override {
-    }
-private:
+
+  private:
+    ActionManager* action_manager_;
     std::unique_ptr<Action> action_;
 };
 
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index beabea1..825603a 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -17,7 +17,6 @@
 #include "bootchart.h"
 
 #include <dirent.h>
-#include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -31,9 +30,7 @@
 #include <condition_variable>
 #include <memory>
 #include <mutex>
-#include <string>
 #include <thread>
-#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 7ee02d0..fcc6092 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -19,33 +19,28 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/loop.h>
+#include <linux/module.h>
 #include <mntent.h>
 #include <net/if.h>
-#include <signal.h>
 #include <sched.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/socket.h>
 #include <sys/mount.h>
 #include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
 #include <sys/syscall.h>
+#include <sys/system_properties.h>
 #include <sys/time.h>
 #include <sys/types.h>
-#include <sys/stat.h>
 #include <sys/wait.h>
 #include <unistd.h>
-#include <linux/loop.h>
-#include <linux/module.h>
-
-#include <string>
-#include <thread>
-
-#include <selinux/android.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -55,14 +50,14 @@
 #include <ext4_utils/ext4_crypt.h>
 #include <ext4_utils/ext4_crypt_init_extensions.h>
 #include <fs_mgr.h>
-#include <logwrap/logwrap.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
 
 #include "action.h"
 #include "bootchart.h"
-#include "devices.h"
 #include "init.h"
 #include "init_parser.h"
-#include "log.h"
 #include "property_service.h"
 #include "reboot.h"
 #include "service.h"
@@ -122,7 +117,7 @@
         LOG(ERROR) << "failed to set bootloader message: " << err;
         return -1;
     }
-    DoReboot(ANDROID_RB_RESTART2, "reboot", "recovery", false);
+    property_set("sys.powerctl", "reboot,recovery");
     return 0;
 }
 
@@ -155,7 +150,12 @@
 }
 
 static int do_domainname(const std::vector<std::string>& args) {
-    return write_file("/proc/sys/kernel/domainname", args[1]) ? 0 : 1;
+    std::string err;
+    if (!WriteFile("/proc/sys/kernel/domainname", args[1], &err)) {
+        LOG(ERROR) << err;
+        return -1;
+    }
+    return 0;
 }
 
 static int do_enable(const std::vector<std::string>& args) {
@@ -179,7 +179,12 @@
 }
 
 static int do_hostname(const std::vector<std::string>& args) {
-    return write_file("/proc/sys/kernel/hostname", args[1]) ? 0 : 1;
+    std::string err;
+    if (!WriteFile("/proc/sys/kernel/hostname", args[1], &err)) {
+        LOG(ERROR) << err;
+        return -1;
+    }
+    return 0;
 }
 
 static int do_ifup(const std::vector<std::string>& args) {
@@ -210,7 +215,7 @@
         mode = std::strtoul(args[2].c_str(), 0, 8);
     }
 
-    ret = make_dir(args[1].c_str(), mode);
+    ret = make_dir(args[1].c_str(), mode, sehandle);
     /* chmod in case the directory already exists */
     if (ret == -1 && errno == EEXIST) {
         ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
@@ -220,11 +225,19 @@
     }
 
     if (args.size() >= 4) {
-        uid_t uid = decode_uid(args[3].c_str());
+        uid_t uid;
+        std::string decode_uid_err;
+        if (!DecodeUid(args[3], &uid, &decode_uid_err)) {
+            LOG(ERROR) << "Unable to find UID for '" << args[3] << "': " << decode_uid_err;
+            return -1;
+        }
         gid_t gid = -1;
 
         if (args.size() == 5) {
-            gid = decode_uid(args[4].c_str());
+            if (!DecodeUid(args[4], &gid, &decode_uid_err)) {
+                LOG(ERROR) << "Unable to find GID for '" << args[3] << "': " << decode_uid_err;
+                return -1;
+            }
         }
 
         if (lchown(args[1].c_str(), uid, gid) == -1) {
@@ -395,7 +408,7 @@
 
     // Turning this on and letting the INFO logging be discarded adds 0.2s to
     // Nexus 9 boot time, so it's disabled by default.
-    if (false) parser.DumpState();
+    if (false) DumpState();
 }
 
 /* mount_fstab
@@ -486,6 +499,23 @@
         // Although encrypted, we have device key, so we do not need to
         // do anything different from the nonencrypted case.
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+    } else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
+        if (e4crypt_install_keyring()) {
+            return -1;
+        }
+        property_set("ro.crypto.state", "encrypted");
+        property_set("ro.crypto.type", "file");
+
+        // defaultcrypto detects file/block encryption. init flow is same for each.
+        ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
+    } else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
+        if (e4crypt_install_keyring()) {
+            return -1;
+        }
+        property_set("ro.crypto.type", "file");
+
+        // encrypt detects file/block encryption. init flow is same for each.
+        ActionManager::GetInstance().QueueEventTrigger("encrypt");
     } else if (code > 0) {
         PLOG(ERROR) << "fs_mgr_mount_all returned unexpected error " << code;
     }
@@ -648,29 +678,49 @@
 }
 
 static int do_write(const std::vector<std::string>& args) {
-    return write_file(args[1], args[2]) ? 0 : 1;
+    std::string err;
+    if (!WriteFile(args[1], args[2], &err)) {
+        LOG(ERROR) << err;
+        return -1;
+    }
+    return 0;
 }
 
 static int do_copy(const std::vector<std::string>& args) {
     std::string data;
-    if (read_file(args[1], &data)) {
-        return write_file(args[2], data) ? 0 : 1;
+    std::string err;
+    if (!ReadFile(args[1], &data, &err)) {
+        LOG(ERROR) << err;
+        return -1;
     }
-    return 1;
+    if (!WriteFile(args[2], data, &err)) {
+        LOG(ERROR) << err;
+        return -1;
+    }
+    return 0;
 }
 
 static int do_chown(const std::vector<std::string>& args) {
-    /* GID is optional. */
-    if (args.size() == 3) {
-        if (lchown(args[2].c_str(), decode_uid(args[1].c_str()), -1) == -1)
-            return -errno;
-    } else if (args.size() == 4) {
-        if (lchown(args[3].c_str(), decode_uid(args[1].c_str()),
-                   decode_uid(args[2].c_str())) == -1)
-            return -errno;
-    } else {
+    uid_t uid;
+    std::string decode_uid_err;
+    if (!DecodeUid(args[1], &uid, &decode_uid_err)) {
+        LOG(ERROR) << "Unable to find UID for '" << args[1] << "': " << decode_uid_err;
         return -1;
     }
+
+    // GID is optional and pushes the index of path out by one if specified.
+    const std::string& path = (args.size() == 4) ? args[3] : args[2];
+    gid_t gid = -1;
+
+    if (args.size() == 4) {
+        if (!DecodeUid(args[2], &gid, &decode_uid_err)) {
+            LOG(ERROR) << "Unable to find GID for '" << args[2] << "': " << decode_uid_err;
+            return -1;
+        }
+    }
+
+    if (lchown(path.c_str(), uid, gid) == -1) return -errno;
+
     return 0;
 }
 
@@ -729,7 +779,7 @@
             }
         } else {
             in_flags = false;
-            if (restorecon(args[i].c_str(), flag) < 0) {
+            if (selinux_android_restorecon(args[i].c_str(), flag) < 0) {
                 ret = -errno;
             }
         }
@@ -814,7 +864,7 @@
  * Callback to make a directory from the ext4 code
  */
 static int do_installkeys_ensure_dir_exists(const char* dir) {
-    if (make_dir(dir, 0700) && errno != EEXIST) {
+    if (make_dir(dir, 0700, sehandle) && errno != EEXIST) {
         return -1;
     }
 
@@ -840,10 +890,12 @@
 }
 
 static int do_init_user0(const std::vector<std::string>& args) {
-    return e4crypt_do_init_user0();
+    std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
+                                          "init_user0"};
+    return do_exec(exec_args);
 }
 
-BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
+const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     // clang-format off
     static const Map builtin_functions = {
diff --git a/init/builtins.h b/init/builtins.h
index 53f4a71..e1f0567 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -17,19 +17,20 @@
 #ifndef _INIT_BUILTINS_H
 #define _INIT_BUILTINS_H
 
+#include <functional>
 #include <map>
 #include <string>
 #include <vector>
 
 #include "keyword_map.h"
 
-using BuiltinFunction = int (*) (const std::vector<std::string>& args);
+using BuiltinFunction = std::function<int(const std::vector<std::string>&)>;
 class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
-public:
-    BuiltinFunctionMap() {
-    }
-private:
-    Map& map() const override;
+  public:
+    BuiltinFunctionMap() {}
+
+  private:
+    const Map& map() const override;
 };
 
 #endif
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
index 6e457cd..6f729a3 100644
--- a/init/descriptors.cpp
+++ b/init/descriptors.cpp
@@ -19,16 +19,16 @@
 #include <ctype.h>
 #include <fcntl.h>
 #include <sys/stat.h>
-#include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/android_get_control_file.h>
 #include <cutils/sockets.h>
 
 #include "init.h"
-#include "log.h"
 #include "util.h"
 
 DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
@@ -78,10 +78,12 @@
 }
 
 int SocketInfo::Create(const std::string& context) const {
-  int flags = ((type() == "stream" ? SOCK_STREAM :
-                (type() == "dgram" ? SOCK_DGRAM :
-                 SOCK_SEQPACKET)));
-  return create_socket(name().c_str(), flags, perm(), uid(), gid(), context.c_str());
+    auto types = android::base::Split(type(), "+");
+    int flags =
+        ((types[0] == "stream" ? SOCK_STREAM : (types[0] == "dgram" ? SOCK_DGRAM : SOCK_SEQPACKET)));
+    bool passcred = types.size() > 1 && types[1] == "passcred";
+    return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str(),
+                        sehandle);
 }
 
 const std::string SocketInfo::key() const {
diff --git a/init/devices.cpp b/init/devices.cpp
index 39571ac..2943fb7 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2014 The Android Open Source Project
+ * Copyright (C) 2007 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,238 +14,221 @@
  * limitations under the License.
  */
 
-#include <dirent.h>
+#include "devices.h"
+
 #include <errno.h>
-#include <fcntl.h>
 #include <fnmatch.h>
-#include <libgen.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/sendfile.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <sys/wait.h>
+#include <sys/sysmacros.h>
 #include <unistd.h>
 
-#include <linux/netlink.h>
-
 #include <memory>
-#include <thread>
 
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-#include <selinux/android.h>
-#include <selinux/avc.h>
-
-#include <private/android_filesystem_config.h>
-
-#include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <cutils/list.h>
-#include <cutils/uevent.h>
+#include <android-base/strings.h>
+#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+#include <selinux/selinux.h>
 
-#include "devices.h"
-#include "ueventd_parser.h"
+#include "ueventd.h"
 #include "util.h"
-#include "log.h"
 
-#define SYSFS_PREFIX    "/sys"
-static const char *firmware_dirs[] = { "/etc/firmware",
-                                       "/vendor/firmware",
-                                       "/firmware/image" };
+#ifdef _INIT_INIT_H
+#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#endif
 
-extern struct selabel_handle *sehandle;
+/* Given a path that may start with a PCI device, populate the supplied buffer
+ * with the PCI domain/bus number and the peripheral ID and return 0.
+ * If it doesn't start with a PCI device, or there is some error, return -1 */
+static bool FindPciDevicePrefix(const std::string& path, std::string* result) {
+    result->clear();
 
-static android::base::unique_fd device_fd;
+    if (!android::base::StartsWith(path, "/devices/pci")) return false;
 
-struct perms_ {
-    char *name;
-    char *attr;
-    mode_t perm;
-    unsigned int uid;
-    unsigned int gid;
-    unsigned short prefix;
-    unsigned short wildcard;
-};
+    /* Beginning of the prefix is the initial "pci" after "/devices/" */
+    std::string::size_type start = 9;
 
-struct perm_node {
-    struct perms_ dp;
-    struct listnode plist;
-};
+    /* End of the prefix is two path '/' later, capturing the domain/bus number
+     * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */
+    auto end = path.find('/', start);
+    if (end == std::string::npos) return false;
 
-struct platform_node {
-    char *name;
-    char *path;
-    int path_len;
-    struct listnode list;
-};
+    end = path.find('/', end + 1);
+    if (end == std::string::npos) return false;
 
-static list_declare(sys_perms);
-static list_declare(dev_perms);
-static list_declare(platform_names);
-
-int add_dev_perms(const char *name, const char *attr,
-                  mode_t perm, unsigned int uid, unsigned int gid,
-                  unsigned short prefix,
-                  unsigned short wildcard) {
-    struct perm_node *node = (perm_node*) calloc(1, sizeof(*node));
-    if (!node)
-        return -ENOMEM;
-
-    node->dp.name = strdup(name);
-    if (!node->dp.name) {
-        free(node);
-        return -ENOMEM;
+    auto length = end - start;
+    if (length <= 4) {
+        // The minimum string that will get to this check is 'pci/', which is malformed,
+        // so return false
+        return false;
     }
 
-    if (attr) {
-        node->dp.attr = strdup(attr);
-        if (!node->dp.attr) {
-            free(node->dp.name);
-            free(node);
-            return -ENOMEM;
-        }
-    }
-
-    node->dp.perm = perm;
-    node->dp.uid = uid;
-    node->dp.gid = gid;
-    node->dp.prefix = prefix;
-    node->dp.wildcard = wildcard;
-
-    if (attr)
-        list_add_tail(&sys_perms, &node->plist);
-    else
-        list_add_tail(&dev_perms, &node->plist);
-
-    return 0;
+    *result = path.substr(start, length);
+    return true;
 }
 
-static bool perm_path_matches(const char *path, struct perms_ *dp)
-{
-    if (dp->prefix) {
-        if (strncmp(path, dp->name, strlen(dp->name)) == 0)
+/* Given a path that may start with a virtual block device, populate
+ * the supplied buffer with the virtual block device ID and return 0.
+ * If it doesn't start with a virtual block device, or there is some
+ * error, return -1 */
+static bool FindVbdDevicePrefix(const std::string& path, std::string* result) {
+    result->clear();
+
+    if (!android::base::StartsWith(path, "/devices/vbd-")) return false;
+
+    /* Beginning of the prefix is the initial "vbd-" after "/devices/" */
+    std::string::size_type start = 13;
+
+    /* End of the prefix is one path '/' later, capturing the
+       virtual block device ID. Example: 768 */
+    auto end = path.find('/', start);
+    if (end == std::string::npos) return false;
+
+    auto length = end - start;
+    if (length == 0) return false;
+
+    *result = path.substr(start, length);
+    return true;
+}
+
+Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
+    : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
+    // Set 'prefix_' or 'wildcard_' based on the below cases:
+    //
+    // 1) No '*' in 'name' -> Neither are set and Match() checks a given path for strict
+    //    equality with 'name'
+    //
+    // 2) '*' only appears as the last character in 'name' -> 'prefix'_ is set to true and
+    //    Match() checks if 'name' is a prefix of a given path.
+    //
+    // 3) '*' appears elsewhere -> 'wildcard_' is set to true and Match() uses fnmatch()
+    //    with FNM_PATHNAME to compare 'name' to a given path.
+
+    auto wildcard_position = name_.find('*');
+    if (wildcard_position != std::string::npos) {
+        if (wildcard_position == name_.length() - 1) {
+            prefix_ = true;
+            name_.pop_back();
+        } else {
+            wildcard_ = true;
+        }
+    }
+}
+
+bool Permissions::Match(const std::string& path) const {
+    if (prefix_) return android::base::StartsWith(path, name_.c_str());
+    if (wildcard_) return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
+    return path == name_;
+}
+
+bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
+                                          const std::string& subsystem) const {
+    std::string path_basename = android::base::Basename(path);
+    if (name().find(subsystem) != std::string::npos) {
+        if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
+        if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
+    }
+    return Match(path);
+}
+
+void SysfsPermissions::SetPermissions(const std::string& path) const {
+    std::string attribute_file = path + "/" + attribute_;
+    LOG(VERBOSE) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
+                 << perm();
+
+    if (access(attribute_file.c_str(), F_OK) == 0) {
+        if (chown(attribute_file.c_str(), uid(), gid()) != 0) {
+            PLOG(ERROR) << "chown(" << attribute_file << ", " << uid() << ", " << gid()
+                        << ") failed";
+        }
+        if (chmod(attribute_file.c_str(), perm()) != 0) {
+            PLOG(ERROR) << "chmod(" << attribute_file << ", " << perm() << ") failed";
+        }
+    }
+}
+
+// Given a path that may start with a platform device, find the parent platform device by finding a
+// parent directory with a 'subsystem' symlink that points to the platform bus.
+// If it doesn't start with a platform device, return false
+bool DeviceHandler::FindPlatformDevice(std::string path, std::string* platform_device_path) const {
+    platform_device_path->clear();
+
+    // Uevents don't contain the mount point, so we need to add it here.
+    path.insert(0, sysfs_mount_point_);
+
+    std::string directory = android::base::Dirname(path);
+
+    while (directory != "/" && directory != ".") {
+        std::string subsystem_link_path;
+        if (android::base::Realpath(directory + "/subsystem", &subsystem_link_path) &&
+            subsystem_link_path == sysfs_mount_point_ + "/bus/platform") {
+            // We need to remove the mount point that we added above before returning.
+            directory.erase(0, sysfs_mount_point_.size());
+            *platform_device_path = directory;
             return true;
-    } else if (dp->wildcard) {
-        if (fnmatch(dp->name, path, FNM_PATHNAME) == 0)
-            return true;
-    } else {
-        if (strcmp(path, dp->name) == 0)
-            return true;
+        }
+
+        auto last_slash = path.rfind('/');
+        if (last_slash == std::string::npos) return false;
+
+        path.erase(last_slash);
+        directory = android::base::Dirname(path);
     }
 
     return false;
 }
 
-static bool match_subsystem(perms_* dp, const char* pattern,
-                            const char* path, const char* subsystem) {
-    if (!pattern || !subsystem || strstr(dp->name, subsystem) == NULL) {
-        return false;
-    }
-
-    std::string subsys_path = android::base::StringPrintf(pattern, subsystem, basename(path));
-    return perm_path_matches(subsys_path.c_str(), dp);
-}
-
-static void fixup_sys_perms(const char* upath, const char* subsystem) {
+void DeviceHandler::FixupSysPermissions(const std::string& upath,
+                                        const std::string& subsystem) const {
     // upaths omit the "/sys" that paths in this list
     // contain, so we prepend it...
-    std::string path = std::string(SYSFS_PREFIX) + upath;
+    std::string path = "/sys" + upath;
 
-    listnode* node;
-    list_for_each(node, &sys_perms) {
-        perms_* dp = &(node_to_item(node, perm_node, plist))->dp;
-        if (match_subsystem(dp, SYSFS_PREFIX "/class/%s/%s", path.c_str(), subsystem)) {
-            ; // matched
-        } else if (match_subsystem(dp, SYSFS_PREFIX "/bus/%s/devices/%s", path.c_str(), subsystem)) {
-            ; // matched
-        } else if (!perm_path_matches(path.c_str(), dp)) {
-            continue;
-        }
-
-        std::string attr_file = path + "/" + dp->attr;
-        LOG(INFO) << "fixup " << attr_file
-                  << " " << dp->uid << " " << dp->gid << " " << std::oct << dp->perm;
-        chown(attr_file.c_str(), dp->uid, dp->gid);
-        chmod(attr_file.c_str(), dp->perm);
+    for (const auto& s : sysfs_permissions_) {
+        if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
     }
 
-    if (access(path.c_str(), F_OK) == 0) {
+    if (!skip_restorecon_ && access(path.c_str(), F_OK) == 0) {
         LOG(VERBOSE) << "restorecon_recursive: " << path;
-        restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+        if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+            PLOG(ERROR) << "selinux_android_restorecon(" << path << ") failed";
+        }
     }
 }
 
-static mode_t get_device_perm(const char *path, const char **links,
-                unsigned *uid, unsigned *gid)
-{
-    struct listnode *node;
-    struct perm_node *perm_node;
-    struct perms_ *dp;
-
-    /* search the perms list in reverse so that ueventd.$hardware can
-     * override ueventd.rc
-     */
-    list_for_each_reverse(node, &dev_perms) {
-        bool match = false;
-
-        perm_node = node_to_item(node, struct perm_node, plist);
-        dp = &perm_node->dp;
-
-        if (perm_path_matches(path, dp)) {
-            match = true;
-        } else {
-            if (links) {
-                int i;
-                for (i = 0; links[i]; i++) {
-                    if (perm_path_matches(links[i], dp)) {
-                        match = true;
-                        break;
-                    }
-                }
-            }
-        }
-
-        if (match) {
-            *uid = dp->uid;
-            *gid = dp->gid;
-            return dp->perm;
+std::tuple<mode_t, uid_t, gid_t> DeviceHandler::GetDevicePermissions(
+    const std::string& path, const std::vector<std::string>& links) const {
+    // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
+    for (auto it = dev_permissions_.crbegin(); it != dev_permissions_.crend(); ++it) {
+        if (it->Match(path) || std::any_of(links.cbegin(), links.cend(),
+                                           [it](const auto& link) { return it->Match(link); })) {
+            return {it->perm(), it->uid(), it->gid()};
         }
     }
     /* Default if nothing found. */
-    *uid = 0;
-    *gid = 0;
-    return 0600;
+    return {0600, 0, 0};
 }
 
-static void make_device(const char *path,
-                        const char */*upath*/,
-                        int block, int major, int minor,
-                        const char **links)
-{
-    unsigned uid;
-    unsigned gid;
-    mode_t mode;
-    dev_t dev;
-    char *secontext = NULL;
+void DeviceHandler::MakeDevice(const std::string& path, int block, int major, int minor,
+                               const std::vector<std::string>& links) const {
+    auto[mode, uid, gid] = GetDevicePermissions(path, links);
+    mode |= (block ? S_IFBLK : S_IFCHR);
 
-    mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
-
-    if (sehandle) {
-        if (selabel_lookup_best_match(sehandle, &secontext, path, links, mode)) {
+    char* secontext = nullptr;
+    if (sehandle_) {
+        std::vector<const char*> c_links;
+        for (const auto& link : links) {
+            c_links.emplace_back(link.c_str());
+        }
+        c_links.emplace_back(nullptr);
+        if (selabel_lookup_best_match(sehandle_, &secontext, path.c_str(), &c_links[0], mode)) {
             PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
             return;
         }
         setfscreatecon(secontext);
     }
 
-    dev = makedev(major, minor);
+    dev_t dev = makedev(major, minor);
     /* Temporarily change egid to avoid race condition setting the gid of the
      * device node. Unforunately changing the euid would prevent creation of
      * some device nodes, so the uid has to be set with chown() and is still
@@ -257,10 +240,9 @@
     }
     /* If the node already exists update its SELinux label to handle cases when
      * it was created with the wrong context during coldboot procedure. */
-    if (mknod(path, mode, dev) && (errno == EEXIST) && secontext) {
-
+    if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
         char* fcon = nullptr;
-        int rc = lgetfilecon(path, &fcon);
+        int rc = lgetfilecon(path.c_str(), &fcon);
         if (rc < 0) {
             PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
             goto out;
@@ -269,820 +251,233 @@
         bool different = strcmp(fcon, secontext) != 0;
         freecon(fcon);
 
-        if (different && lsetfilecon(path, secontext)) {
-            PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path << "' device";
+        if (different && lsetfilecon(path.c_str(), secontext)) {
+            PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
+                        << "' device";
         }
     }
 
 out:
-    chown(path, uid, -1);
+    chown(path.c_str(), uid, -1);
     if (setegid(AID_ROOT)) {
         PLOG(FATAL) << "setegid(AID_ROOT) failed";
     }
 
     if (secontext) {
         freecon(secontext);
-        setfscreatecon(NULL);
+        setfscreatecon(nullptr);
     }
 }
 
-static void add_platform_device(const char *path)
-{
-    int path_len = strlen(path);
-    struct platform_node *bus;
-    const char *name = path;
+std::vector<std::string> DeviceHandler::GetCharacterDeviceSymlinks(const Uevent& uevent) const {
+    std::string parent_device;
+    if (!FindPlatformDevice(uevent.path, &parent_device)) return {};
 
-    if (!strncmp(path, "/devices/", 9)) {
-        name += 9;
-        if (!strncmp(name, "platform/", 9))
-            name += 9;
-    }
+    // skip path to the parent driver
+    std::string path = uevent.path.substr(parent_device.length());
 
-    LOG(VERBOSE) << "adding platform device " << name << " (" << path << ")";
+    if (!android::base::StartsWith(path, "/usb")) return {};
 
-    bus = (platform_node*) calloc(1, sizeof(struct platform_node));
-    bus->path = strdup(path);
-    bus->path_len = path_len;
-    bus->name = bus->path + (name - path);
-    list_add_tail(&platform_names, &bus->list);
-}
+    // skip root hub name and device. use device interface
+    // skip 3 slashes, including the first / by starting the search at the 1st character, not 0th.
+    // then extract what comes between the 3rd and 4th slash
+    // e.g. "/usb/usb_device/name/tty2-1:1.0" -> "name"
 
-/*
- * given a path that may start with a platform device, find the length of the
- * platform device prefix.  If it doesn't start with a platform device, return
- * 0.
- */
-static struct platform_node *find_platform_device(const char *path)
-{
-    int path_len = strlen(path);
-    struct listnode *node;
-    struct platform_node *bus;
+    std::string::size_type start = 0;
+    start = path.find('/', start + 1);
+    if (start == std::string::npos) return {};
 
-    list_for_each_reverse(node, &platform_names) {
-        bus = node_to_item(node, struct platform_node, list);
-        if ((bus->path_len < path_len) &&
-                (path[bus->path_len] == '/') &&
-                !strncmp(path, bus->path, bus->path_len))
-            return bus;
-    }
+    start = path.find('/', start + 1);
+    if (start == std::string::npos) return {};
 
-    return NULL;
-}
+    auto end = path.find('/', start + 1);
+    if (end == std::string::npos) return {};
 
-static void remove_platform_device(const char *path)
-{
-    struct listnode *node;
-    struct platform_node *bus;
+    start++;  // Skip the first '/'
 
-    list_for_each_reverse(node, &platform_names) {
-        bus = node_to_item(node, struct platform_node, list);
-        if (!strcmp(path, bus->path)) {
-            LOG(INFO) << "removing platform device " << bus->name;
-            free(bus->path);
-            list_remove(node);
-            free(bus);
-            return;
-        }
-    }
-}
+    auto length = end - start;
+    if (length == 0) return {};
 
-static void destroy_platform_devices() {
-    struct listnode* node;
-    struct listnode* n;
-    struct platform_node* bus;
+    auto name_string = path.substr(start, length);
 
-    list_for_each_safe(node, n, &platform_names) {
-        list_remove(node);
-        bus = node_to_item(node, struct platform_node, list);
-        free(bus->path);
-        free(bus);
-    }
-}
+    std::vector<std::string> links;
+    links.emplace_back("/dev/usb/" + uevent.subsystem + name_string);
 
-/* Given a path that may start with a PCI device, populate the supplied buffer
- * with the PCI domain/bus number and the peripheral ID and return 0.
- * If it doesn't start with a PCI device, or there is some error, return -1 */
-static int find_pci_device_prefix(const char *path, char *buf, ssize_t buf_sz)
-{
-    const char *start, *end;
-
-    if (strncmp(path, "/devices/pci", 12))
-        return -1;
-
-    /* Beginning of the prefix is the initial "pci" after "/devices/" */
-    start = path + 9;
-
-    /* End of the prefix is two path '/' later, capturing the domain/bus number
-     * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */
-    end = strchr(start, '/');
-    if (!end)
-        return -1;
-    end = strchr(end + 1, '/');
-    if (!end)
-        return -1;
-
-    /* Make sure we have enough room for the string plus null terminator */
-    if (end - start + 1 > buf_sz)
-        return -1;
-
-    strncpy(buf, start, end - start);
-    buf[end - start] = '\0';
-    return 0;
-}
-
-/* Given a path that may start with a virtual block device, populate
- * the supplied buffer with the virtual block device ID and return 0.
- * If it doesn't start with a virtual block device, or there is some
- * error, return -1 */
-static int find_vbd_device_prefix(const char *path, char *buf, ssize_t buf_sz)
-{
-    const char *start, *end;
-
-    /* Beginning of the prefix is the initial "vbd-" after "/devices/" */
-    if (strncmp(path, "/devices/vbd-", 13))
-        return -1;
-
-    /* End of the prefix is one path '/' later, capturing the
-       virtual block device ID. Example: 768 */
-    start = path + 13;
-    end = strchr(start, '/');
-    if (!end)
-        return -1;
-
-    /* Make sure we have enough room for the string plus null terminator */
-    if (end - start + 1 > buf_sz)
-        return -1;
-
-    strncpy(buf, start, end - start);
-    buf[end - start] = '\0';
-    return 0;
-}
-
-static void parse_event(const char *msg, struct uevent *uevent)
-{
-    uevent->action = "";
-    uevent->path = "";
-    uevent->subsystem = "";
-    uevent->firmware = "";
-    uevent->major = -1;
-    uevent->minor = -1;
-    uevent->partition_name = NULL;
-    uevent->partition_num = -1;
-    uevent->device_name = NULL;
-
-        /* currently ignoring SEQNUM */
-    while(*msg) {
-        if(!strncmp(msg, "ACTION=", 7)) {
-            msg += 7;
-            uevent->action = msg;
-        } else if(!strncmp(msg, "DEVPATH=", 8)) {
-            msg += 8;
-            uevent->path = msg;
-        } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
-            msg += 10;
-            uevent->subsystem = msg;
-        } else if(!strncmp(msg, "FIRMWARE=", 9)) {
-            msg += 9;
-            uevent->firmware = msg;
-        } else if(!strncmp(msg, "MAJOR=", 6)) {
-            msg += 6;
-            uevent->major = atoi(msg);
-        } else if(!strncmp(msg, "MINOR=", 6)) {
-            msg += 6;
-            uevent->minor = atoi(msg);
-        } else if(!strncmp(msg, "PARTN=", 6)) {
-            msg += 6;
-            uevent->partition_num = atoi(msg);
-        } else if(!strncmp(msg, "PARTNAME=", 9)) {
-            msg += 9;
-            uevent->partition_name = msg;
-        } else if(!strncmp(msg, "DEVNAME=", 8)) {
-            msg += 8;
-            uevent->device_name = msg;
-        }
-
-        /* advance to after the next \0 */
-        while(*msg++)
-            ;
-    }
-
-    if (LOG_UEVENTS) {
-        LOG(INFO) << android::base::StringPrintf("event { '%s', '%s', '%s', '%s', %d, %d }",
-                                                 uevent->action, uevent->path, uevent->subsystem,
-                                                 uevent->firmware, uevent->major, uevent->minor);
-    }
-}
-
-static char **get_character_device_symlinks(struct uevent *uevent)
-{
-    const char *parent;
-    const char *slash;
-    char **links;
-    int link_num = 0;
-    int width;
-    struct platform_node *pdev;
-
-    pdev = find_platform_device(uevent->path);
-    if (!pdev)
-        return NULL;
-
-    links = (char**) malloc(sizeof(char *) * 2);
-    if (!links)
-        return NULL;
-    memset(links, 0, sizeof(char *) * 2);
-
-    /* skip "/devices/platform/<driver>" */
-    parent = strchr(uevent->path + pdev->path_len, '/');
-    if (!parent)
-        goto err;
-
-    if (!strncmp(parent, "/usb", 4)) {
-        /* skip root hub name and device. use device interface */
-        while (*++parent && *parent != '/');
-        if (*parent)
-            while (*++parent && *parent != '/');
-        if (!*parent)
-            goto err;
-        slash = strchr(++parent, '/');
-        if (!slash)
-            goto err;
-        width = slash - parent;
-        if (width <= 0)
-            goto err;
-
-        if (asprintf(&links[link_num], "/dev/usb/%s%.*s", uevent->subsystem, width, parent) > 0)
-            link_num++;
-        else
-            links[link_num] = NULL;
-        mkdir("/dev/usb", 0755);
-    }
-    else {
-        goto err;
-    }
+    mkdir("/dev/usb", 0755);
 
     return links;
-err:
-    free(links);
-    return NULL;
 }
 
-char** get_block_device_symlinks(struct uevent* uevent) {
-    const char *device;
-    struct platform_node *pdev;
-    const char *slash;
-    const char *type;
-    char buf[256];
-    char link_path[256];
-    int link_num = 0;
-    char *p;
+// replaces any unacceptable characters with '_', the
+// length of the resulting string is equal to the input string
+void SanitizePartitionName(std::string* string) {
+    const char* accept =
+        "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        "0123456789"
+        "_-.";
 
-    pdev = find_platform_device(uevent->path);
-    if (pdev) {
-        device = pdev->name;
+    if (!string) return;
+
+    std::string::size_type pos = 0;
+    while ((pos = string->find_first_not_of(accept, pos)) != std::string::npos) {
+        (*string)[pos] = '_';
+    }
+}
+
+std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {
+    std::string device;
+    std::string type;
+
+    if (FindPlatformDevice(uevent.path, &device)) {
+        // Skip /devices/platform or /devices/ if present
+        static const std::string devices_platform_prefix = "/devices/platform/";
+        static const std::string devices_prefix = "/devices/";
+
+        if (android::base::StartsWith(device, devices_platform_prefix.c_str())) {
+            device = device.substr(devices_platform_prefix.length());
+        } else if (android::base::StartsWith(device, devices_prefix.c_str())) {
+            device = device.substr(devices_prefix.length());
+        }
+
         type = "platform";
-    } else if (!find_pci_device_prefix(uevent->path, buf, sizeof(buf))) {
-        device = buf;
+    } else if (FindPciDevicePrefix(uevent.path, &device)) {
         type = "pci";
-    } else if (!find_vbd_device_prefix(uevent->path, buf, sizeof(buf))) {
-        device = buf;
+    } else if (FindVbdDevicePrefix(uevent.path, &device)) {
         type = "vbd";
     } else {
-        return NULL;
+        return {};
     }
 
-    char **links = (char**) malloc(sizeof(char *) * 4);
-    if (!links)
-        return NULL;
-    memset(links, 0, sizeof(char *) * 4);
+    std::vector<std::string> links;
 
     LOG(VERBOSE) << "found " << type << " device " << device;
 
-    snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device);
+    auto link_path = "/dev/block/" + type + "/" + device;
 
-    if (uevent->partition_name) {
-        p = strdup(uevent->partition_name);
-        sanitize(p);
-        if (strcmp(uevent->partition_name, p)) {
-            LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '" << p << "'";
+    if (!uevent.partition_name.empty()) {
+        std::string partition_name_sanitized(uevent.partition_name);
+        SanitizePartitionName(&partition_name_sanitized);
+        if (partition_name_sanitized != uevent.partition_name) {
+            LOG(VERBOSE) << "Linking partition '" << uevent.partition_name << "' as '"
+                         << partition_name_sanitized << "'";
         }
-        if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0)
-            link_num++;
-        else
-            links[link_num] = NULL;
-        free(p);
+        links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
     }
 
-    if (uevent->partition_num >= 0) {
-        if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0)
-            link_num++;
-        else
-            links[link_num] = NULL;
+    if (uevent.partition_num >= 0) {
+        links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent.partition_num));
     }
 
-    slash = strrchr(uevent->path, '/');
-    if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0)
-        link_num++;
-    else
-        links[link_num] = NULL;
+    auto last_slash = uevent.path.rfind('/');
+    links.emplace_back(link_path + "/" + uevent.path.substr(last_slash + 1));
 
     return links;
 }
 
-static void make_link_init(const char* oldpath, const char* newpath) {
-  const char* slash = strrchr(newpath, '/');
-  if (!slash) return;
+void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, int block,
+                                 int major, int minor, const std::vector<std::string>& links) const {
+    if (action == "add") {
+        MakeDevice(devpath, block, major, minor, links);
+        for (const auto& link : links) {
+            if (mkdir_recursive(android::base::Dirname(link), 0755, sehandle_)) {
+                PLOG(ERROR) << "Failed to create directory " << android::base::Dirname(link);
+            }
 
-  if (mkdir_recursive(dirname(newpath), 0755)) {
-    PLOG(ERROR) << "Failed to create directory " << dirname(newpath);
-  }
-
-  if (symlink(oldpath, newpath) && errno != EEXIST) {
-    PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
-  }
-}
-
-static void remove_link(const char* oldpath, const char* newpath) {
-  std::string path;
-  if (android::base::Readlink(newpath, &path) && path == oldpath) unlink(newpath);
-}
-
-static void handle_device(const char *action, const char *devpath,
-        const char *path, int block, int major, int minor, char **links)
-{
-    if(!strcmp(action, "add")) {
-        make_device(devpath, path, block, major, minor, (const char **)links);
-        if (links) {
-            for (int i = 0; links[i]; i++) {
-                make_link_init(devpath, links[i]);
+            if (symlink(devpath.c_str(), link.c_str()) && errno != EEXIST) {
+                PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
             }
         }
     }
 
-    if(!strcmp(action, "remove")) {
-        if (links) {
-            for (int i = 0; links[i]; i++) {
-                remove_link(devpath, links[i]);
+    if (action == "remove") {
+        for (const auto& link : links) {
+            std::string link_path;
+            if (android::base::Readlink(link, &link_path) && link_path == devpath) {
+                unlink(link.c_str());
             }
         }
-        unlink(devpath);
-    }
-
-    if (links) {
-        for (int i = 0; links[i]; i++) {
-            free(links[i]);
-        }
-        free(links);
+        unlink(devpath.c_str());
     }
 }
 
-static void handle_platform_device_event(struct uevent *uevent)
-{
-    const char *path = uevent->path;
+void DeviceHandler::HandleBlockDeviceEvent(const Uevent& uevent) const {
+    // if it's not a /dev device, nothing to do
+    if (uevent.major < 0 || uevent.minor < 0) return;
 
-    if (!strcmp(uevent->action, "add"))
-        add_platform_device(path);
-    else if (!strcmp(uevent->action, "remove"))
-        remove_platform_device(path);
-}
+    const char* base = "/dev/block/";
+    make_dir(base, 0755, sehandle_);
 
-static const char *parse_device_name(struct uevent *uevent, unsigned int len)
-{
-    const char *name;
+    std::string name = android::base::Basename(uevent.path);
+    std::string devpath = base + name;
 
-    /* if it's not a /dev device, nothing else to do */
-    if((uevent->major < 0) || (uevent->minor < 0))
-        return NULL;
-
-    /* do we have a name? */
-    name = strrchr(uevent->path, '/');
-    if(!name)
-        return NULL;
-    name++;
-
-    /* too-long names would overrun our buffer */
-    if(strlen(name) > len) {
-        LOG(ERROR) << "DEVPATH=" << name << " exceeds " << len << "-character limit on filename; ignoring event";
-        return NULL;
+    std::vector<std::string> links;
+    if (android::base::StartsWith(uevent.path, "/devices")) {
+        links = GetBlockDeviceSymlinks(uevent);
     }
 
-    return name;
+    HandleDevice(uevent.action, devpath, 1, uevent.major, uevent.minor, links);
 }
 
-#define DEVPATH_LEN 96
-#define MAX_DEV_NAME 64
+void DeviceHandler::HandleGenericDeviceEvent(const Uevent& uevent) const {
+    // if it's not a /dev device, nothing to do
+    if (uevent.major < 0 || uevent.minor < 0) return;
 
-static void handle_block_device_event(struct uevent *uevent)
-{
-    const char *base = "/dev/block/";
-    const char *name;
-    char devpath[DEVPATH_LEN];
-    char **links = NULL;
+    std::string devpath;
 
-    name = parse_device_name(uevent, MAX_DEV_NAME);
-    if (!name)
-        return;
-
-    snprintf(devpath, sizeof(devpath), "%s%s", base, name);
-    make_dir(base, 0755);
-
-    if (!strncmp(uevent->path, "/devices/", 9))
-        links = get_block_device_symlinks(uevent);
-
-    handle_device(uevent->action, devpath, uevent->path, 1,
-            uevent->major, uevent->minor, links);
-}
-
-static bool assemble_devpath(char *devpath, const char *dirname,
-        const char *devname)
-{
-    int s = snprintf(devpath, DEVPATH_LEN, "%s/%s", dirname, devname);
-    if (s < 0) {
-        PLOG(ERROR) << "failed to assemble device path; ignoring event";
-        return false;
-    } else if (s >= DEVPATH_LEN) {
-        LOG(ERROR) << dirname << "/" << devname
-                   << " exceeds " << DEVPATH_LEN << "-character limit on path; ignoring event";
-        return false;
-    }
-    return true;
-}
-
-static void mkdir_recursive_for_devpath(const char *devpath)
-{
-    char dir[DEVPATH_LEN];
-    char *slash;
-
-    strcpy(dir, devpath);
-    slash = strrchr(dir, '/');
-    *slash = '\0';
-    mkdir_recursive(dir, 0755);
-}
-
-static void handle_generic_device_event(struct uevent *uevent)
-{
-    const char *base;
-    const char *name;
-    char devpath[DEVPATH_LEN] = {0};
-    char **links = NULL;
-
-    name = parse_device_name(uevent, MAX_DEV_NAME);
-    if (!name)
-        return;
-
-    struct ueventd_subsystem *subsystem =
-            ueventd_subsystem_find_by_name(uevent->subsystem);
-
-    if (subsystem) {
-        const char *devname;
-
-        switch (subsystem->devname_src) {
-        case DEVNAME_UEVENT_DEVNAME:
-            devname = uevent->device_name;
-            break;
-
-        case DEVNAME_UEVENT_DEVPATH:
-            devname = name;
-            break;
-
-        default:
-            LOG(ERROR) << uevent->subsystem << " subsystem's devpath option is not set; ignoring event";
+    if (android::base::StartsWith(uevent.subsystem, "usb")) {
+        if (uevent.subsystem == "usb") {
+            if (!uevent.device_name.empty()) {
+                devpath = "/dev/" + uevent.device_name;
+            } else {
+                // This imitates the file system that would be created
+                // if we were using devfs instead.
+                // Minors are broken up into groups of 128, starting at "001"
+                int bus_id = uevent.minor / 128 + 1;
+                int device_id = uevent.minor % 128 + 1;
+                devpath = android::base::StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
+            }
+        } else {
+            // ignore other USB events
             return;
         }
-
-        if (!assemble_devpath(devpath, subsystem->dirname, devname))
-            return;
-        mkdir_recursive_for_devpath(devpath);
-    } else if (!strncmp(uevent->subsystem, "usb", 3)) {
-         if (!strcmp(uevent->subsystem, "usb")) {
-            if (uevent->device_name) {
-                if (!assemble_devpath(devpath, "/dev", uevent->device_name))
-                    return;
-                mkdir_recursive_for_devpath(devpath);
-             }
-             else {
-                 /* This imitates the file system that would be created
-                  * if we were using devfs instead.
-                  * Minors are broken up into groups of 128, starting at "001"
-                  */
-                 int bus_id = uevent->minor / 128 + 1;
-                 int device_id = uevent->minor % 128 + 1;
-                 /* build directories */
-                 make_dir("/dev/bus", 0755);
-                 make_dir("/dev/bus/usb", 0755);
-                 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id);
-                 make_dir(devpath, 0755);
-                 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id);
-             }
-         } else {
-             /* ignore other USB events */
-             return;
-         }
-     } else if (!strncmp(uevent->subsystem, "graphics", 8)) {
-         base = "/dev/graphics/";
-         make_dir(base, 0755);
-     } else if (!strncmp(uevent->subsystem, "drm", 3)) {
-         base = "/dev/dri/";
-         make_dir(base, 0755);
-     } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
-         base = "/dev/oncrpc/";
-         make_dir(base, 0755);
-     } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
-         base = "/dev/adsp/";
-         make_dir(base, 0755);
-     } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
-         base = "/dev/msm_camera/";
-         make_dir(base, 0755);
-     } else if(!strncmp(uevent->subsystem, "input", 5)) {
-         base = "/dev/input/";
-         make_dir(base, 0755);
-     } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
-         base = "/dev/mtd/";
-         make_dir(base, 0755);
-     } else if(!strncmp(uevent->subsystem, "sound", 5)) {
-         base = "/dev/snd/";
-         make_dir(base, 0755);
-     } else if(!strncmp(uevent->subsystem, "misc", 4) && !strncmp(name, "log_", 4)) {
-         LOG(INFO) << "kernel logger is deprecated";
-         base = "/dev/log/";
-         make_dir(base, 0755);
-         name += 4;
-     } else
-         base = "/dev/";
-     links = get_character_device_symlinks(uevent);
-
-     if (!devpath[0])
-         snprintf(devpath, sizeof(devpath), "%s%s", base, name);
-
-     handle_device(uevent->action, devpath, uevent->path, 0,
-             uevent->major, uevent->minor, links);
-}
-
-static void handle_device_event(struct uevent *uevent)
-{
-    if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") || !strcmp(uevent->action, "online"))
-        fixup_sys_perms(uevent->path, uevent->subsystem);
-
-    if (!strncmp(uevent->subsystem, "block", 5)) {
-        handle_block_device_event(uevent);
-    } else if (!strncmp(uevent->subsystem, "platform", 8)) {
-        handle_platform_device_event(uevent);
+    } else if (const auto subsystem =
+                   std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
+               subsystem != subsystems_.cend()) {
+        devpath = subsystem->ParseDevPath(uevent);
     } else {
-        handle_generic_device_event(uevent);
+        devpath = "/dev/" + android::base::Basename(uevent.path);
     }
+
+    mkdir_recursive(android::base::Dirname(devpath), 0755, sehandle_);
+
+    auto links = GetCharacterDeviceSymlinks(uevent);
+
+    HandleDevice(uevent.action, devpath, 0, uevent.major, uevent.minor, links);
 }
 
-static void load_firmware(uevent* uevent, const std::string& root,
-                          int fw_fd, size_t fw_size,
-                          int loading_fd, int data_fd) {
-    // Start transfer.
-    android::base::WriteFully(loading_fd, "1", 1);
-
-    // Copy the firmware.
-    int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
-    if (rc == -1) {
-        PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent->firmware << "' }";
+void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
+    if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
+        FixupSysPermissions(uevent.path, uevent.subsystem);
     }
 
-    // Tell the firmware whether to abort or commit.
-    const char* response = (rc != -1) ? "0" : "-1";
-    android::base::WriteFully(loading_fd, response, strlen(response));
-}
-
-static int is_booting() {
-    return access("/dev/.booting", F_OK) == 0;
-}
-
-static void process_firmware_event(uevent* uevent) {
-    int booting = is_booting();
-
-    LOG(INFO) << "firmware: loading '" << uevent->firmware << "' for '" << uevent->path << "'";
-
-    std::string root = android::base::StringPrintf("/sys%s", uevent->path);
-    std::string loading = root + "/loading";
-    std::string data = root + "/data";
-
-    android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY|O_CLOEXEC));
-    if (loading_fd == -1) {
-        PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent->firmware;
-        return;
-    }
-
-    android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY|O_CLOEXEC));
-    if (data_fd == -1) {
-        PLOG(ERROR) << "couldn't open firmware data fd for " << uevent->firmware;
-        return;
-    }
-
-try_loading_again:
-    for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
-        std::string file = android::base::StringPrintf("%s/%s", firmware_dirs[i], uevent->firmware);
-        android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY|O_CLOEXEC));
-        struct stat sb;
-        if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
-            load_firmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
-            return;
-        }
-    }
-
-    if (booting) {
-        // If we're not fully booted, we may be missing
-        // filesystems needed for firmware, wait and retry.
-        std::this_thread::sleep_for(100ms);
-        booting = is_booting();
-        goto try_loading_again;
-    }
-
-    LOG(ERROR) << "firmware: could not find firmware for " << uevent->firmware;
-
-    // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
-    write(loading_fd, "-1", 2);
-}
-
-static void handle_firmware_event(uevent* uevent) {
-    if (strcmp(uevent->subsystem, "firmware")) return;
-    if (strcmp(uevent->action, "add")) return;
-
-    // Loading the firmware in a child means we can do that in parallel...
-    // (We ignore SIGCHLD rather than wait for our children.)
-    pid_t pid = fork();
-    if (pid == 0) {
-        Timer t;
-        process_firmware_event(uevent);
-        LOG(INFO) << "loading " << uevent->path << " took " << t;
-        _exit(EXIT_SUCCESS);
-    } else if (pid == -1) {
-        PLOG(ERROR) << "could not fork to process firmware event for " << uevent->firmware;
-    }
-}
-
-static bool inline should_stop_coldboot(coldboot_action_t act)
-{
-    return (act == COLDBOOT_STOP || act == COLDBOOT_FINISH);
-}
-
-#define UEVENT_MSG_LEN  2048
-
-static inline coldboot_action_t handle_device_fd_with(
-        std::function<coldboot_action_t(uevent* uevent)> handle_uevent)
-{
-    char msg[UEVENT_MSG_LEN+2];
-    int n;
-    while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
-        if(n >= UEVENT_MSG_LEN)   /* overflow -- discard */
-            continue;
-
-        msg[n] = '\0';
-        msg[n+1] = '\0';
-
-        struct uevent uevent;
-        parse_event(msg, &uevent);
-        coldboot_action_t act = handle_uevent(&uevent);
-        if (should_stop_coldboot(act))
-            return act;
-    }
-
-    return COLDBOOT_CONTINUE;
-}
-
-coldboot_action_t handle_device_fd(coldboot_callback fn)
-{
-    coldboot_action_t ret = handle_device_fd_with(
-        [&](uevent* uevent) -> coldboot_action_t {
-            if (selinux_status_updated() > 0) {
-                struct selabel_handle *sehandle2;
-                sehandle2 = selinux_android_file_context_handle();
-                if (sehandle2) {
-                    selabel_close(sehandle);
-                    sehandle = sehandle2;
-                }
-            }
-
-            // default is to always create the devices
-            coldboot_action_t act = COLDBOOT_CREATE;
-            if (fn) {
-                act = fn(uevent);
-            }
-
-            if (act == COLDBOOT_CREATE || act == COLDBOOT_STOP) {
-                handle_device_event(uevent);
-                handle_firmware_event(uevent);
-            }
-
-            return act;
-        });
-
-    return ret;
-}
-
-/* Coldboot walks parts of the /sys tree and pokes the uevent files
-** to cause the kernel to regenerate device add events that happened
-** before init's device manager was started
-**
-** We drain any pending events from the netlink socket every time
-** we poke another uevent file to make sure we don't overrun the
-** socket's buffer.
-*/
-
-static coldboot_action_t do_coldboot(DIR *d, coldboot_callback fn)
-{
-    struct dirent *de;
-    int dfd, fd;
-    coldboot_action_t act = COLDBOOT_CONTINUE;
-
-    dfd = dirfd(d);
-
-    fd = openat(dfd, "uevent", O_WRONLY);
-    if (fd >= 0) {
-        write(fd, "add\n", 4);
-        close(fd);
-        act = handle_device_fd(fn);
-        if (should_stop_coldboot(act))
-            return act;
-    }
-
-    while (!should_stop_coldboot(act) && (de = readdir(d))) {
-        DIR *d2;
-
-        if(de->d_type != DT_DIR || de->d_name[0] == '.')
-            continue;
-
-        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
-        if(fd < 0)
-            continue;
-
-        d2 = fdopendir(fd);
-        if(d2 == 0)
-            close(fd);
-        else {
-            act = do_coldboot(d2, fn);
-            closedir(d2);
-        }
-    }
-
-    // default is always to continue looking for uevents
-    return act;
-}
-
-static coldboot_action_t coldboot(const char *path, coldboot_callback fn)
-{
-    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path), closedir);
-    if (d) {
-        return do_coldboot(d.get(), fn);
-    }
-
-    return COLDBOOT_CONTINUE;
-}
-
-void device_init(const char* path, coldboot_callback fn) {
-    if (!sehandle) {
-        sehandle = selinux_android_file_context_handle();
-    }
-    // open uevent socket and selinux status only if it hasn't been
-    // done before
-    if (device_fd == -1) {
-        /* is 256K enough? udev uses 16MB! */
-        device_fd.reset(uevent_open_socket(256 * 1024, true));
-        if (device_fd == -1) {
-            return;
-        }
-        fcntl(device_fd, F_SETFL, O_NONBLOCK);
-        selinux_status_open(true);
-    }
-
-    if (access(COLDBOOT_DONE, F_OK) == 0) {
-        LOG(VERBOSE) << "Skipping coldboot, already done!";
-        return;
-    }
-
-    Timer t;
-    coldboot_action_t act;
-    if (!path) {
-        act = coldboot("/sys/class", fn);
-        if (!should_stop_coldboot(act)) {
-            act = coldboot("/sys/block", fn);
-            if (!should_stop_coldboot(act)) {
-                act = coldboot("/sys/devices", fn);
-            }
-        }
+    if (uevent.subsystem == "block") {
+        HandleBlockDeviceEvent(uevent);
     } else {
-        act = coldboot(path, fn);
+        HandleGenericDeviceEvent(uevent);
     }
-
-    // If we have a callback, then do as it says. If no, then the default is
-    // to always create COLDBOOT_DONE file.
-    if (!fn || (act == COLDBOOT_FINISH)) {
-        close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
-    }
-
-    LOG(INFO) << "Coldboot took " << t;
 }
 
-void device_close() {
-    destroy_platform_devices();
-    device_fd.reset();
-    selinux_status_close();
-}
+DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
+                             std::vector<SysfsPermissions> sysfs_permissions,
+                             std::vector<Subsystem> subsystems, bool skip_restorecon)
+    : dev_permissions_(std::move(dev_permissions)),
+      sysfs_permissions_(std::move(sysfs_permissions)),
+      subsystems_(std::move(subsystems)),
+      sehandle_(selinux_android_file_context_handle()),
+      skip_restorecon_(skip_restorecon),
+      sysfs_mount_point_("/sys") {}
 
-int get_device_fd() {
-    return device_fd;
-}
+DeviceHandler::DeviceHandler()
+    : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
+                    std::vector<Subsystem>{}, false) {}
diff --git a/init/devices.h b/init/devices.h
index 3f2cde4..362c38c 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -17,44 +17,120 @@
 #ifndef _INIT_DEVICES_H
 #define _INIT_DEVICES_H
 
-#include <functional>
 #include <sys/stat.h>
+#include <sys/types.h>
 
-enum coldboot_action_t {
-    // coldboot continues without creating the device for the uevent
-    COLDBOOT_CONTINUE = 0,
-    // coldboot continues after creating the device for the uevent
-    COLDBOOT_CREATE,
-    // coldboot stops after creating the device for uevent but doesn't
-    // create the COLDBOOT_DONE file
-    COLDBOOT_STOP,
-    // same as COLDBOOT_STOP, but creates the COLDBOOT_DONE file
-    COLDBOOT_FINISH
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <selinux/label.h>
+
+#include "uevent.h"
+
+class Permissions {
+  public:
+    Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid);
+
+    bool Match(const std::string& path) const;
+
+    mode_t perm() const { return perm_; }
+    uid_t uid() const { return uid_; }
+    gid_t gid() const { return gid_; }
+
+  protected:
+    const std::string& name() const { return name_; }
+
+  private:
+    std::string name_;
+    mode_t perm_;
+    uid_t uid_;
+    gid_t gid_;
+    bool prefix_;
+    bool wildcard_;
 };
 
-struct uevent {
-    const char* action;
-    const char* path;
-    const char* subsystem;
-    const char* firmware;
-    const char* partition_name;
-    const char* device_name;
-    int partition_num;
-    int major;
-    int minor;
+class SysfsPermissions : public Permissions {
+  public:
+    SysfsPermissions(const std::string& name, const std::string& attribute, mode_t perm, uid_t uid,
+                     gid_t gid)
+        : Permissions(name, perm, uid, gid), attribute_(attribute) {}
+
+    bool MatchWithSubsystem(const std::string& path, const std::string& subsystem) const;
+    void SetPermissions(const std::string& path) const;
+
+  private:
+    const std::string attribute_;
 };
 
-typedef std::function<coldboot_action_t(struct uevent* uevent)> coldboot_callback;
-extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr);
-extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr);
-extern void device_close();
+class Subsystem {
+  public:
+    friend class SubsystemParser;
 
-extern int add_dev_perms(const char *name, const char *attr,
-                         mode_t perm, unsigned int uid,
-                         unsigned int gid, unsigned short prefix,
-                         unsigned short wildcard);
-int get_device_fd();
+    Subsystem() {}
 
-char** get_block_device_symlinks(struct uevent* uevent);
+    // Returns the full path for a uevent of a device that is a member of this subsystem,
+    // according to the rules parsed from ueventd.rc
+    std::string ParseDevPath(const Uevent& uevent) const {
+        std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
+                                  ? uevent.device_name
+                                  : android::base::Basename(uevent.path);
 
-#endif	/* _INIT_DEVICES_H */
+        return dir_name_ + "/" + devname;
+    }
+
+    bool operator==(const std::string& string_name) const { return name_ == string_name; }
+
+  private:
+    enum class DevnameSource {
+        DEVNAME_UEVENT_DEVNAME,
+        DEVNAME_UEVENT_DEVPATH,
+    };
+
+    std::string name_;
+    std::string dir_name_ = "/dev";
+    DevnameSource devname_source_;
+};
+
+class DeviceHandler {
+  public:
+    friend class DeviceHandlerTester;
+
+    DeviceHandler();
+    DeviceHandler(std::vector<Permissions> dev_permissions,
+                  std::vector<SysfsPermissions> sysfs_permissions,
+                  std::vector<Subsystem> subsystems, bool skip_restorecon);
+    ~DeviceHandler(){};
+
+    void HandleDeviceEvent(const Uevent& uevent);
+
+    std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
+    void set_skip_restorecon(bool value) { skip_restorecon_ = value; }
+
+  private:
+    bool FindPlatformDevice(std::string path, std::string* platform_device_path) const;
+    std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
+        const std::string& path, const std::vector<std::string>& links) const;
+    void MakeDevice(const std::string& path, int block, int major, int minor,
+                    const std::vector<std::string>& links) const;
+    std::vector<std::string> GetCharacterDeviceSymlinks(const Uevent& uevent) const;
+    void HandleDevice(const std::string& action, const std::string& devpath, int block, int major,
+                      int minor, const std::vector<std::string>& links) const;
+    void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
+
+    void HandleBlockDeviceEvent(const Uevent& uevent) const;
+    void HandleGenericDeviceEvent(const Uevent& uevent) const;
+
+    std::vector<Permissions> dev_permissions_;
+    std::vector<SysfsPermissions> sysfs_permissions_;
+    std::vector<Subsystem> subsystems_;
+    selabel_handle* sehandle_;
+    bool skip_restorecon_;
+    std::string sysfs_mount_point_;
+};
+
+// Exposed for testing
+void SanitizePartitionName(std::string* string);
+
+#endif
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
new file mode 100644
index 0000000..e1e4e49
--- /dev/null
+++ b/init/devices_test.cpp
@@ -0,0 +1,402 @@
+/*
+ * 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 "devices.h"
+
+#include <android-base/scopeguard.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "util.h"
+
+using namespace std::string_literals;
+
+class DeviceHandlerTester {
+  public:
+    void TestGetSymlinks(const std::string& platform_device, const Uevent& uevent,
+                         const std::vector<std::string> expected_links, bool block) {
+        TemporaryDir fake_sys_root;
+        device_handler_.sysfs_mount_point_ = fake_sys_root.path;
+
+        std::string platform_device_dir = fake_sys_root.path + platform_device;
+        mkdir_recursive(platform_device_dir, 0777, nullptr);
+
+        std::string platform_bus = fake_sys_root.path + "/bus/platform"s;
+        mkdir_recursive(platform_bus, 0777, nullptr);
+        symlink(platform_bus.c_str(), (platform_device_dir + "/subsystem").c_str());
+
+        mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777, nullptr);
+
+        std::vector<std::string> result;
+        if (block) {
+            result = device_handler_.GetBlockDeviceSymlinks(uevent);
+        } else {
+            result = device_handler_.GetCharacterDeviceSymlinks(uevent);
+        }
+
+        auto expected_size = expected_links.size();
+        ASSERT_EQ(expected_size, result.size());
+        if (expected_size == 0) return;
+
+        // Explicitly iterate so the results are visible if a failure occurs
+        for (unsigned int i = 0; i < expected_size; ++i) {
+            EXPECT_EQ(expected_links[i], result[i]);
+        }
+    }
+
+  private:
+    DeviceHandler device_handler_;
+};
+
+TEST(device_handler, get_character_device_symlinks_success) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    Uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device/name/tty2-1:1.0",
+        .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result{"/dev/usb/ttyname"};
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_character_device_symlinks_no_pdev_match) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    Uevent uevent = {
+        .path = "/device/name/tty2-1:1.0", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_character_device_symlinks_nothing_after_platform_device) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    Uevent uevent = {
+        .path = "/devices/platform/some_device_name", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_character_device_symlinks_no_usb_found) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    Uevent uevent = {
+        .path = "/devices/platform/some_device_name/bad/bad/", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_character_device_symlinks_no_roothub) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    Uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_character_device_symlinks_no_usb_device) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    Uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device/", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_character_device_symlinks_no_final_slash) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    Uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device/name", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_character_device_symlinks_no_final_name) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    Uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device//", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_platform) {
+    // These are actual paths from bullhead
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    Uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0",
+        .partition_name = "",
+        .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0"};
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition) {
+    // These are actual paths from bullhead
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    Uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = "modem",
+        .partition_num = 1,
+    };
+    std::vector<std::string> expected_result{
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+        "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+    };
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_num) {
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    Uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = "",
+        .partition_num = 1,
+    };
+    std::vector<std::string> expected_result{
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+        "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+    };
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_name) {
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    Uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = "modem",
+        .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+        "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+    };
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_pci) {
+    const char* platform_device = "/devices/do/not/match";
+    Uevent uevent = {
+        .path = "/devices/pci0000:00/0000:00:1f.2/mmcblk0", .partition_name = "", .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0"};
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_pci_bad_format) {
+    const char* platform_device = "/devices/do/not/match";
+    Uevent uevent = {
+        .path = "/devices/pci//mmcblk0", .partition_name = "", .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{};
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_vbd) {
+    const char* platform_device = "/devices/do/not/match";
+    Uevent uevent = {
+        .path = "/devices/vbd-1234/mmcblk0", .partition_name = "", .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{"/dev/block/vbd/1234/mmcblk0"};
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_vbd_bad_format) {
+    const char* platform_device = "/devices/do/not/match";
+    Uevent uevent = {
+        .path = "/devices/vbd-/mmcblk0", .partition_name = "", .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{};
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_no_matches) {
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    Uevent uevent = {
+        .path = "/devices/soc.0/not_the_device/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = "",
+        .partition_num = -1,
+    };
+    std::vector<std::string> expected_result;
+
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, sanitize_null) {
+    SanitizePartitionName(nullptr);
+}
+
+TEST(device_handler, sanitize_empty) {
+    std::string empty;
+    SanitizePartitionName(&empty);
+    EXPECT_EQ(0u, empty.size());
+}
+
+TEST(device_handler, sanitize_allgood) {
+    std::string good =
+        "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        "0123456789"
+        "_-.";
+    std::string good_copy = good;
+    SanitizePartitionName(&good);
+    EXPECT_EQ(good_copy, good);
+}
+
+TEST(device_handler, sanitize_somebad) {
+    std::string string = "abc!@#$%^&*()";
+    SanitizePartitionName(&string);
+    EXPECT_EQ("abc__________", string);
+}
+
+TEST(device_handler, sanitize_allbad) {
+    std::string string = "!@#$%^&*()";
+    SanitizePartitionName(&string);
+    EXPECT_EQ("__________", string);
+}
+
+TEST(device_handler, sanitize_onebad) {
+    std::string string = ")";
+    SanitizePartitionName(&string);
+    EXPECT_EQ("_", string);
+}
+
+TEST(device_handler, DevPermissionsMatchNormal) {
+    // Basic from ueventd.rc
+    // /dev/null                 0666   root       root
+    Permissions permissions("/dev/null", 0666, 0, 0);
+    EXPECT_TRUE(permissions.Match("/dev/null"));
+    EXPECT_FALSE(permissions.Match("/dev/nullsuffix"));
+    EXPECT_FALSE(permissions.Match("/dev/nul"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(0U, permissions.gid());
+}
+
+TEST(device_handler, DevPermissionsMatchPrefix) {
+    // Prefix from ueventd.rc
+    // /dev/dri/*                0666   root       graphics
+    Permissions permissions("/dev/dri/*", 0666, 0, 1000);
+    EXPECT_TRUE(permissions.Match("/dev/dri/some_dri_device"));
+    EXPECT_TRUE(permissions.Match("/dev/dri/some_other_dri_device"));
+    EXPECT_TRUE(permissions.Match("/dev/dri/"));
+    EXPECT_FALSE(permissions.Match("/dev/dr/non_match"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(device_handler, DevPermissionsMatchWildcard) {
+    // Wildcard example
+    // /dev/device*name                0666   root       graphics
+    Permissions permissions("/dev/device*name", 0666, 0, 1000);
+    EXPECT_TRUE(permissions.Match("/dev/devicename"));
+    EXPECT_TRUE(permissions.Match("/dev/device123name"));
+    EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
+    EXPECT_FALSE(permissions.Match("/dev/device123name/subdevice"));
+    EXPECT_FALSE(permissions.Match("/dev/deviceame"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(device_handler, DevPermissionsMatchWildcardPrefix) {
+    // Wildcard+Prefix example
+    // /dev/device*name*                0666   root       graphics
+    Permissions permissions("/dev/device*name*", 0666, 0, 1000);
+    EXPECT_TRUE(permissions.Match("/dev/devicename"));
+    EXPECT_TRUE(permissions.Match("/dev/device123name"));
+    EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
+    EXPECT_TRUE(permissions.Match("/dev/device123namesomething"));
+    // FNM_PATHNAME doesn't match '/' with *
+    EXPECT_FALSE(permissions.Match("/dev/device123name/something"));
+    EXPECT_FALSE(permissions.Match("/dev/deviceame"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemNormal) {
+    // /sys/devices/virtual/input/input*   enable      0660  root   input
+    SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001);
+    EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/input0", "input"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/not_input0", "input"));
+    EXPECT_EQ(0660U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1001U, permissions.gid());
+}
+
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemClass) {
+    // /sys/class/input/event*   enable      0660  root   input
+    SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001);
+    EXPECT_TRUE(permissions.MatchWithSubsystem(
+        "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0", "input"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem(
+        "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/not_event0", "input"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem(
+        "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0", "not_input"));
+    EXPECT_EQ(0660U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1001U, permissions.gid());
+}
+
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemBus) {
+    // /sys/bus/i2c/devices/i2c-*   enable      0660  root   input
+    SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001);
+    EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "i2c"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/not-i2c", "i2c"));
+    EXPECT_FALSE(
+        permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "not_i2c"));
+    EXPECT_EQ(0660U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1001U, permissions.gid());
+}
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
new file mode 100644
index 0000000..844c605
--- /dev/null
+++ b/init/firmware_handler.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "firmware_handler.h"
+
+#include <fcntl.h>
+#include <sys/sendfile.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include "util.h"
+
+static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
+                         int loading_fd, int data_fd) {
+    // Start transfer.
+    android::base::WriteFully(loading_fd, "1", 1);
+
+    // Copy the firmware.
+    int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
+    if (rc == -1) {
+        PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent.firmware
+                    << "' }";
+    }
+
+    // Tell the firmware whether to abort or commit.
+    const char* response = (rc != -1) ? "0" : "-1";
+    android::base::WriteFully(loading_fd, response, strlen(response));
+}
+
+static bool IsBooting() {
+    return access("/dev/.booting", F_OK) == 0;
+}
+
+static void ProcessFirmwareEvent(const Uevent& uevent) {
+    int booting = IsBooting();
+
+    LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
+
+    std::string root = "/sys" + uevent.path;
+    std::string loading = root + "/loading";
+    std::string data = root + "/data";
+
+    android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));
+    if (loading_fd == -1) {
+        PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent.firmware;
+        return;
+    }
+
+    android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));
+    if (data_fd == -1) {
+        PLOG(ERROR) << "couldn't open firmware data fd for " << uevent.firmware;
+        return;
+    }
+
+    static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/",
+                                          "/firmware/image/"};
+
+try_loading_again:
+    for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
+        std::string file = firmware_dirs[i] + uevent.firmware;
+        android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
+        struct stat sb;
+        if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
+            LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
+            return;
+        }
+    }
+
+    if (booting) {
+        // If we're not fully booted, we may be missing
+        // filesystems needed for firmware, wait and retry.
+        std::this_thread::sleep_for(100ms);
+        booting = IsBooting();
+        goto try_loading_again;
+    }
+
+    LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware;
+
+    // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
+    write(loading_fd, "-1", 2);
+}
+
+void HandleFirmwareEvent(const Uevent& uevent) {
+    if (uevent.subsystem != "firmware" || uevent.action != "add") return;
+
+    // Loading the firmware in a child means we can do that in parallel...
+    // We double fork instead of waiting for these processes.
+    pid_t pid = fork();
+    if (pid == -1) {
+        PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware;
+        return;
+    }
+
+    if (pid == 0) {
+        pid = fork();
+        if (pid == -1) {
+            PLOG(ERROR) << "could not fork a sceond time to process firmware event for "
+                        << uevent.firmware;
+            _exit(EXIT_FAILURE);
+        }
+        if (pid == 0) {
+            Timer t;
+            ProcessFirmwareEvent(uevent);
+            LOG(INFO) << "loading " << uevent.path << " took " << t;
+            _exit(EXIT_SUCCESS);
+        }
+
+        _exit(EXIT_SUCCESS);
+    }
+
+    waitpid(pid, nullptr, 0);
+}
diff --git a/libsystem/include/system/window.h b/init/firmware_handler.h
similarity index 70%
rename from libsystem/include/system/window.h
rename to init/firmware_handler.h
index efa10d6..be9daae 100644
--- a/libsystem/include/system/window.h
+++ b/init/firmware_handler.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2007 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,9 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
-#define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
+#ifndef _INIT_FIRMWARE_HANDLER_H
+#define _INIT_FIRMWARE_HANDLER_H
 
-#include <system/window-deprecated.h>
+#include "uevent.h"
 
-#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
+void HandleFirmwareEvent(const Uevent& uevent);
+
+#endif
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index d52247b..99275e5 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -16,16 +16,12 @@
 
 #include "import_parser.h"
 
-#include "errno.h"
+#include <android-base/logging.h>
 
-#include <string>
-#include <vector>
-
-#include "log.h"
 #include "util.h"
 
-bool ImportParser::ParseSection(const std::vector<std::string>& args,
-                                std::string* err) {
+bool ImportParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                int line, std::string* err) {
     if (args.size() != 2) {
         *err = "single argument needed for import\n";
         return false;
@@ -39,16 +35,18 @@
     }
 
     LOG(INFO) << "Added '" << conf_file << "' to import list";
-    imports_.emplace_back(std::move(conf_file));
+    if (filename_.empty()) filename_ = filename;
+    imports_.emplace_back(std::move(conf_file), line);
     return true;
 }
 
-void ImportParser::EndFile(const std::string& filename) {
+void ImportParser::EndFile() {
     auto current_imports = std::move(imports_);
     imports_.clear();
-    for (const auto& s : current_imports) {
-        if (!Parser::GetInstance().ParseConfig(s)) {
-            PLOG(ERROR) << "could not import file '" << s << "' from '" << filename << "'";
+    for (const auto& [import, line_num] : current_imports) {
+        if (!parser_->ParseConfig(import)) {
+            PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
+                        << "'";
         }
     }
 }
diff --git a/init/import_parser.h b/init/import_parser.h
index 0e91025..45cbfad 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -23,21 +23,18 @@
 #include <vector>
 
 class ImportParser : public SectionParser {
-public:
-    ImportParser()  {
-    }
-    bool ParseSection(const std::vector<std::string>& args,
+  public:
+    ImportParser(Parser* parser) : parser_(parser) {}
+    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
                       std::string* err) override;
-    bool ParseLineSection(const std::vector<std::string>& args,
-                          const std::string& filename, int line,
-                          std::string* err) const override {
-        return true;
-    }
-    void EndSection() override {
-    }
-    void EndFile(const std::string& filename) override;
-private:
-    std::vector<std::string> imports_;
+    void EndFile() override;
+
+  private:
+    Parser* parser_;
+    // Store filename for later error reporting.
+    std::string filename_;
+    // Vector of imports and their line numbers for later error reporting.
+    std::vector<std::pair<std::string, int>> imports_;
 };
 
 #endif
diff --git a/init/init.cpp b/init/init.cpp
index bb6355a..9652efa 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
+#include "init.h"
+
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
-#include <keyutils.h>
 #include <libgen.h>
 #include <paths.h>
 #include <signal.h>
@@ -37,17 +38,19 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-#include <selinux/android.h>
-
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <keyutils.h>
 #include <libavb/libavb.h>
 #include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
 
 #include <fstream>
 #include <memory>
@@ -55,9 +58,7 @@
 
 #include "action.h"
 #include "bootchart.h"
-#include "devices.h"
 #include "import_parser.h"
-#include "init.h"
 #include "init_first_stage.h"
 #include "init_parser.h"
 #include "keychords.h"
@@ -70,6 +71,7 @@
 #include "util.h"
 #include "watchdogd.h"
 
+using android::base::boot_clock;
 using android::base::GetProperty;
 using android::base::StringPrintf;
 
@@ -90,6 +92,12 @@
 static std::unique_ptr<Timer> waiting_for_prop(nullptr);
 static std::string wait_prop_name;
 static std::string wait_prop_value;
+static bool shutting_down;
+
+void DumpState() {
+    ServiceManager::GetInstance().DumpState();
+    ActionManager::GetInstance().DumpState();
+}
 
 void register_epoll_handler(int fd, void (*fn)()) {
     epoll_event ev;
@@ -149,21 +157,31 @@
     return true;
 }
 
+void ResetWaitForProp() {
+    wait_prop_name.clear();
+    wait_prop_value.clear();
+    waiting_for_prop.reset();
+}
+
 void property_changed(const std::string& name, const std::string& value) {
     // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
     // This is to ensure that init will always and immediately shutdown/reboot, regardless of
     // if there are other pending events to process or if init is waiting on an exec service or
     // waiting on a property.
-    if (name == "sys.powerctl") HandlePowerctlMessage(value);
+    // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
+    // commands to be executed.
+    if (name == "sys.powerctl") {
+        if (HandlePowerctlMessage(value)) {
+            shutting_down = true;
+        }
+    }
 
-    if (property_triggers_enabled)
-        ActionManager::GetInstance().QueuePropertyTrigger(name, value);
+    if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
+
     if (waiting_for_prop) {
         if (wait_prop_name == name && wait_prop_value == value) {
-            wait_prop_name.clear();
-            wait_prop_value.clear();
             LOG(INFO) << "Wait for property took " << *waiting_for_prop;
-            waiting_for_prop.reset();
+            ResetWaitForProp();
         }
     }
 }
@@ -527,7 +545,7 @@
 static int queue_property_triggers_action(const std::vector<std::string>& args)
 {
     ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
-    ActionManager::GetInstance().QueueAllPropertyTriggers();
+    ActionManager::GetInstance().QueueAllPropertyActions();
     return 0;
 }
 
@@ -862,7 +880,9 @@
             }
         }
 
-        if (!write_file("/sys/fs/selinux/checkreqprot", "0")) {
+        std::string err;
+        if (!WriteFile("/sys/fs/selinux/checkreqprot", "0", &err)) {
+            LOG(ERROR) << err;
             security_failure();
         }
 
@@ -873,35 +893,38 @@
     }
 }
 
-// The files and directories that were created before initial sepolicy load
-// need to have their security context restored to the proper value.
-// This must happen before /dev is populated by ueventd.
+// The files and directories that were created before initial sepolicy load or
+// files on ramdisk need to have their security context restored to the proper
+// value. This must happen before /dev is populated by ueventd.
 static void selinux_restore_context() {
     LOG(INFO) << "Running restorecon...";
-    restorecon("/dev");
-    restorecon("/dev/kmsg");
-    restorecon("/dev/socket");
-    restorecon("/dev/random");
-    restorecon("/dev/urandom");
-    restorecon("/dev/__properties__");
+    selinux_android_restorecon("/dev", 0);
+    selinux_android_restorecon("/dev/kmsg", 0);
+    selinux_android_restorecon("/dev/socket", 0);
+    selinux_android_restorecon("/dev/random", 0);
+    selinux_android_restorecon("/dev/urandom", 0);
+    selinux_android_restorecon("/dev/__properties__", 0);
 
-    restorecon("/file_contexts.bin");
-    restorecon("/plat_file_contexts");
-    restorecon("/nonplat_file_contexts");
-    restorecon("/plat_property_contexts");
-    restorecon("/nonplat_property_contexts");
-    restorecon("/plat_seapp_contexts");
-    restorecon("/nonplat_seapp_contexts");
-    restorecon("/plat_service_contexts");
-    restorecon("/nonplat_service_contexts");
-    restorecon("/plat_hwservice_contexts");
-    restorecon("/nonplat_hwservice_contexts");
-    restorecon("/sepolicy");
-    restorecon("/vndservice_contexts");
+    selinux_android_restorecon("/file_contexts.bin", 0);
+    selinux_android_restorecon("/plat_file_contexts", 0);
+    selinux_android_restorecon("/nonplat_file_contexts", 0);
+    selinux_android_restorecon("/plat_property_contexts", 0);
+    selinux_android_restorecon("/nonplat_property_contexts", 0);
+    selinux_android_restorecon("/plat_seapp_contexts", 0);
+    selinux_android_restorecon("/nonplat_seapp_contexts", 0);
+    selinux_android_restorecon("/plat_service_contexts", 0);
+    selinux_android_restorecon("/nonplat_service_contexts", 0);
+    selinux_android_restorecon("/plat_hwservice_contexts", 0);
+    selinux_android_restorecon("/nonplat_hwservice_contexts", 0);
+    selinux_android_restorecon("/sepolicy", 0);
+    selinux_android_restorecon("/vndservice_contexts", 0);
 
-    restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
-    restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
-    restorecon("/dev/device-mapper");
+    selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
+    selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
+    selinux_android_restorecon("/dev/device-mapper", 0);
+
+    selinux_android_restorecon("/sbin/mke2fs_static", 0);
+    selinux_android_restorecon("/sbin/e2fsdroid_static", 0);
 }
 
 // Set the UDC controller for the ConfigFS USB Gadgets.
@@ -1004,7 +1027,7 @@
 
         // We're in the kernel domain, so re-exec init to transition to the init domain now
         // that the SELinux policy has been loaded.
-        if (restorecon("/init") == -1) {
+        if (selinux_android_restorecon("/init", 0) == -1) {
             PLOG(ERROR) << "restorecon failed";
             security_failure();
         }
@@ -1032,7 +1055,7 @@
     // Set up a session keyring that all processes will have access to. It
     // will hold things like FBE encryption keys. No process should override
     // its session keyring.
-    keyctl(KEYCTL_GET_KEYRING_ID, KEY_SPEC_SESSION_KEYRING, 1);
+    keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
 
     // Indicate that booting is in progress to background fw loaders, etc.
     close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
@@ -1082,10 +1105,13 @@
     const BuiltinFunctionMap function_map;
     Action::set_function_map(&function_map);
 
+    ActionManager& am = ActionManager::GetInstance();
+    ServiceManager& sm = ServiceManager::GetInstance();
     Parser& parser = Parser::GetInstance();
-    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
-    parser.AddSectionParser("on", std::make_unique<ActionParser>());
-    parser.AddSectionParser("import", std::make_unique<ImportParser>());
+
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
     std::string bootscript = GetProperty("ro.boot.init_rc", "");
     if (bootscript.empty()) {
         parser.ParseConfig("/init.rc");
@@ -1103,9 +1129,7 @@
 
     // Turning this on and letting the INFO logging be discarded adds 0.2s to
     // Nexus 9 boot time, so it's disabled by default.
-    if (false) parser.DumpState();
-
-    ActionManager& am = ActionManager::GetInstance();
+    if (false) DumpState();
 
     am.QueueEventTrigger("early-init");
 
@@ -1140,11 +1164,11 @@
         // By default, sleep until something happens.
         int epoll_timeout_ms = -1;
 
-        if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+        if (!(waiting_for_prop || sm.IsWaitingForExec())) {
             am.ExecuteOneCommand();
         }
-        if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
-            restart_processes();
+        if (!(waiting_for_prop || sm.IsWaitingForExec())) {
+            if (!shutting_down) restart_processes();
 
             // If there's a process that needs restarting, wake up in time for that.
             if (process_needs_restart_at != 0) {
diff --git a/init/init.h b/init/init.h
index 1da3350..4024cfe 100644
--- a/init/init.h
+++ b/init/init.h
@@ -19,6 +19,9 @@
 
 #include <string>
 
+// Note: These globals are *only* valid in init, so they should not be used in ueventd,
+// watchdogd, or any files that may be included in those, such as devices.cpp and util.cpp.
+// TODO: Have an Init class and remove all globals.
 extern const char *ENV[32];
 extern std::string default_console;
 extern struct selabel_handle *sehandle;
@@ -34,4 +37,8 @@
 
 bool start_waiting_for_property(const char *name, const char *value);
 
+void DumpState();
+
+void ResetWaitForProp();
+
 #endif  /* _INIT_INIT_H */
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index bcc8d1b..d72c3ac 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <memory>
 #include <set>
 #include <string>
@@ -31,8 +32,12 @@
 #include "devices.h"
 #include "fs_mgr.h"
 #include "fs_mgr_avb.h"
+#include "uevent.h"
+#include "uevent_listener.h"
 #include "util.h"
 
+using namespace std::chrono_literals;
+
 // Class Declarations
 // ------------------
 class FirstStageMount {
@@ -47,22 +52,23 @@
     bool InitDevices();
 
   protected:
-    void InitRequiredDevices();
-    void InitVerityDevice(const std::string& verity_device);
+    bool InitRequiredDevices();
+    bool InitVerityDevice(const std::string& verity_device);
     bool MountPartitions();
 
-    virtual coldboot_action_t ColdbootCallback(uevent* uevent);
+    virtual ListenerAction UeventCallback(const Uevent& uevent);
 
     // Pure virtual functions.
     virtual bool GetRequiredDevices() = 0;
     virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
 
     bool need_dm_verity_;
-    // Device tree fstab entries.
+
     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
-    // Eligible first stage mount candidates, only allow /system, /vendor and/or /odm.
     std::vector<fstab_rec*> mount_fstab_recs_;
     std::set<std::string> required_devices_partition_names_;
+    DeviceHandler device_handler_;
+    UeventListener uevent_listener_;
 };
 
 class FirstStageMountVBootV1 : public FirstStageMount {
@@ -83,7 +89,7 @@
     ~FirstStageMountVBootV2() override = default;
 
   protected:
-    coldboot_action_t ColdbootCallback(uevent* uevent) override;
+    ListenerAction UeventCallback(const Uevent& uevent) override;
     bool GetRequiredDevices() override;
     bool SetUpDmVerity(fstab_rec* fstab_rec) override;
     bool InitAvbHandle();
@@ -111,12 +117,10 @@
         LOG(ERROR) << "Failed to read fstab from device tree";
         return;
     }
-    for (auto mount_point : {"/system", "/vendor", "/odm"}) {
-        fstab_rec* fstab_rec =
-            fs_mgr_get_entry_for_mount_point(device_tree_fstab_.get(), mount_point);
-        if (fstab_rec != nullptr) {
-            mount_fstab_recs_.push_back(fstab_rec);
-        }
+    // Stores device_tree_fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>)
+    // for easier manipulation later, e.g., range-base for loop.
+    for (int i = 0; i < device_tree_fstab_->num_entries; i++) {
+        mount_fstab_recs_.push_back(&device_tree_fstab_->recs[i]);
     }
 }
 
@@ -140,86 +144,119 @@
 }
 
 bool FirstStageMount::InitDevices() {
-    if (!GetRequiredDevices()) return false;
-
-    InitRequiredDevices();
-
-    // InitRequiredDevices() will remove found partitions from required_devices_partition_names_.
-    // So if it isn't empty here, it means some partitions are not found.
-    if (!required_devices_partition_names_.empty()) {
-        LOG(ERROR) << __FUNCTION__ << "(): partition(s) not found: "
-                   << android::base::Join(required_devices_partition_names_, ", ");
-        return false;
-    } else {
-        return true;
-    }
+    return GetRequiredDevices() && InitRequiredDevices();
 }
 
 // Creates devices with uevent->partition_name matching one in the member variable
 // required_devices_partition_names_. Found partitions will then be removed from it
 // for the subsequent member function to check which devices are NOT created.
-void FirstStageMount::InitRequiredDevices() {
+bool FirstStageMount::InitRequiredDevices() {
     if (required_devices_partition_names_.empty()) {
-        return;
+        return true;
     }
 
     if (need_dm_verity_) {
         const std::string dm_path = "/devices/virtual/misc/device-mapper";
-        device_init(("/sys" + dm_path).c_str(), [&dm_path](uevent* uevent) -> coldboot_action_t {
-            if (uevent->path && uevent->path == dm_path) return COLDBOOT_STOP;
-            return COLDBOOT_CONTINUE;  // dm_path not found, continue to find it.
-        });
+        bool found = false;
+        auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
+            if (uevent.path == dm_path) {
+                device_handler_.HandleDeviceEvent(uevent);
+                found = true;
+                return ListenerAction::kStop;
+            }
+            return ListenerAction::kContinue;
+        };
+        uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
+        if (!found) {
+            LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
+            Timer t;
+            uevent_listener_.Poll(dm_callback, 10s);
+            LOG(INFO) << "Wait for device-mapper returned after " << t;
+        }
+        if (!found) {
+            LOG(ERROR) << "device-mapper device not found after polling timeout";
+            return false;
+        }
     }
 
-    device_init(nullptr,
-                [this](uevent* uevent) -> coldboot_action_t { return ColdbootCallback(uevent); });
+    auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
+    uevent_listener_.RegenerateUevents(uevent_callback);
 
-    device_close();
+    // UeventCallback() will remove found partitions from required_devices_partition_names_.
+    // So if it isn't empty here, it means some partitions are not found.
+    if (!required_devices_partition_names_.empty()) {
+        LOG(INFO) << __PRETTY_FUNCTION__
+                  << ": partition(s) not found in /sys, waiting for their uevent(s): "
+                  << android::base::Join(required_devices_partition_names_, ", ");
+        Timer t;
+        uevent_listener_.Poll(uevent_callback, 10s);
+        LOG(INFO) << "Wait for partitions returned after " << t;
+    }
+
+    if (!required_devices_partition_names_.empty()) {
+        LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
+                   << android::base::Join(required_devices_partition_names_, ", ");
+        return false;
+    }
+
+    return true;
 }
 
-coldboot_action_t FirstStageMount::ColdbootCallback(uevent* uevent) {
-    // We need platform devices to create symlinks.
-    if (!strncmp(uevent->subsystem, "platform", 8)) {
-        return COLDBOOT_CREATE;
-    }
-
+ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent) {
     // Ignores everything that is not a block device.
-    if (strncmp(uevent->subsystem, "block", 5)) {
-        return COLDBOOT_CONTINUE;
+    if (uevent.subsystem != "block") {
+        return ListenerAction::kContinue;
     }
 
-    if (uevent->partition_name) {
+    if (!uevent.partition_name.empty()) {
         // Matches partition name to create device nodes.
         // Both required_devices_partition_names_ and uevent->partition_name have A/B
         // suffix when A/B is used.
-        auto iter = required_devices_partition_names_.find(uevent->partition_name);
+        auto iter = required_devices_partition_names_.find(uevent.partition_name);
         if (iter != required_devices_partition_names_.end()) {
-            LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter;
+            LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
             required_devices_partition_names_.erase(iter);
+            device_handler_.HandleDeviceEvent(uevent);
             if (required_devices_partition_names_.empty()) {
-                return COLDBOOT_STOP;  // Found all partitions, stop coldboot.
+                return ListenerAction::kStop;
             } else {
-                return COLDBOOT_CREATE;  // Creates this device and continue to find others.
+                return ListenerAction::kContinue;
             }
         }
     }
     // Not found a partition or find an unneeded partition, continue to find others.
-    return COLDBOOT_CONTINUE;
+    return ListenerAction::kContinue;
 }
 
 // Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
-void FirstStageMount::InitVerityDevice(const std::string& verity_device) {
+bool FirstStageMount::InitVerityDevice(const std::string& verity_device) {
     const std::string device_name(basename(verity_device.c_str()));
     const std::string syspath = "/sys/block/" + device_name;
+    bool found = false;
 
-    device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
-        if (uevent->device_name && uevent->device_name == device_name) {
+    auto verity_callback = [&device_name, &verity_device, this, &found](const Uevent& uevent) {
+        if (uevent.device_name == device_name) {
             LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
-            return COLDBOOT_STOP;
+            device_handler_.HandleDeviceEvent(uevent);
+            found = true;
+            return ListenerAction::kStop;
         }
-        return COLDBOOT_CONTINUE;
-    });
-    device_close();
+        return ListenerAction::kContinue;
+    };
+
+    uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback);
+    if (!found) {
+        LOG(INFO) << "dm-verity device not found in /sys, waiting for its uevent";
+        Timer t;
+        uevent_listener_.Poll(verity_callback, 10s);
+        LOG(INFO) << "wait for dm-verity device returned after " << t;
+    }
+    if (!found) {
+        LOG(ERROR) << "dm-verity device not found after polling timeout";
+        return false;
+    }
+
+    return true;
 }
 
 bool FirstStageMount::MountPartitions() {
@@ -280,14 +317,18 @@
 bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
     if (fs_mgr_is_verified(fstab_rec)) {
         int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */);
-        if (ret == FS_MGR_SETUP_VERITY_DISABLED) {
-            LOG(INFO) << "Verity disabled for '" << fstab_rec->mount_point << "'";
-        } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
-            // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
-            // Needs to create it because ueventd isn't started in init first stage.
-            InitVerityDevice(fstab_rec->blk_device);
-        } else {
-            return false;
+        switch (ret) {
+            case FS_MGR_SETUP_VERITY_SKIPPED:
+            case FS_MGR_SETUP_VERITY_DISABLED:
+                LOG(INFO) << "Verity disabled/skipped for '" << fstab_rec->mount_point << "'";
+                return true;
+            case FS_MGR_SETUP_VERITY_SUCCESS:
+                // The exact block device name (fstab_rec->blk_device) is changed to
+                // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
+                // first stage.
+                return InitVerityDevice(fstab_rec->blk_device);
+            default:
+                return false;
         }
     }
     return true;  // Returns true to mount the partition.
@@ -345,46 +386,49 @@
     return true;
 }
 
-coldboot_action_t FirstStageMountVBootV2::ColdbootCallback(uevent* uevent) {
-    // Invokes the parent function to see if any desired partition has been found.
-    // If yes, record the by-name symlink for creating FsManagerAvbHandle later.
-    coldboot_action_t parent_callback_ret = FirstStageMount::ColdbootCallback(uevent);
-
-    // Skips the uevent if the parent function returns COLDBOOT_CONTINUE (meaning
-    // that the uevent was skipped) or there is no uevent->partition_name to
-    // create the by-name symlink.
-    if (parent_callback_ret != COLDBOOT_CONTINUE && uevent->partition_name) {
-        // get_block_device_symlinks() will return three symlinks at most, depending on
+ListenerAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) {
+    // Check if this uevent corresponds to one of the required partitions and store its symlinks if
+    // so, in order to create FsManagerAvbHandle later.
+    // Note that the parent callback removes partitions from the list of required partitions
+    // as it finds them, so this must happen first.
+    if (!uevent.partition_name.empty() &&
+        required_devices_partition_names_.find(uevent.partition_name) !=
+            required_devices_partition_names_.end()) {
+        // GetBlockDeviceSymlinks() will return three symlinks at most, depending on
         // the content of uevent. by-name symlink will be at [0] if uevent->partition_name
         // is not empty. e.g.,
         //   - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem
         //   - /dev/block/platform/soc.0/f9824900.sdhci/by-num/p1
         //   - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
-        char** links = get_block_device_symlinks(uevent);
-        if (links && links[0]) {
-            auto[it, inserted] = by_name_symlink_map_.emplace(uevent->partition_name, links[0]);
+        std::vector<std::string> links = device_handler_.GetBlockDeviceSymlinks(uevent);
+        if (!links.empty()) {
+            auto[it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]);
             if (!inserted) {
-                LOG(ERROR) << "Partition '" << uevent->partition_name
+                LOG(ERROR) << "Partition '" << uevent.partition_name
                            << "' already existed in the by-name symlink map with a value of '"
                            << it->second << "', new value '" << links[0] << "' will be ignored.";
             }
         }
     }
 
-    return parent_callback_ret;
+    return FirstStageMount::UeventCallback(uevent);
 }
 
 bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
     if (fs_mgr_is_avb(fstab_rec)) {
         if (!InitAvbHandle()) return false;
-        if (avb_handle_->hashtree_disabled()) {
-            LOG(INFO) << "avb hashtree disabled for '" << fstab_rec->mount_point << "'";
-        } else if (avb_handle_->SetUpAvb(fstab_rec, false /* wait_for_verity_dev */)) {
-            // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
-            // Needs to create it because ueventd isn't started in init first stage.
-            InitVerityDevice(fstab_rec->blk_device);
-        } else {
-            return false;
+        SetUpAvbHashtreeResult hashtree_result =
+            avb_handle_->SetUpAvbHashtree(fstab_rec, false /* wait_for_verity_dev */);
+        switch (hashtree_result) {
+            case SetUpAvbHashtreeResult::kDisabled:
+                return true;  // Returns true to mount the partition.
+            case SetUpAvbHashtreeResult::kSuccess:
+                // The exact block device name (fstab_rec->blk_device) is changed to
+                // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
+                // first stage.
+                return InitVerityDevice(fstab_rec->blk_device);
+            default:
+                return false;
         }
     }
     return true;  // Returns true to mount the partition.
@@ -412,7 +456,7 @@
 
 // Public functions
 // ----------------
-// Mounts /system, /vendor, and/or /odm if they are present in the fstab provided by device tree.
+// Mounts partitions specified by fstab in device tree.
 bool DoFirstStageMount() {
     // Skips first stage mount if we're in recovery mode.
     if (IsRecoveryMode()) {
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index a192862..1b31cf2 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -14,18 +14,16 @@
  * limitations under the License.
  */
 
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-
-#include "action.h"
 #include "init_parser.h"
-#include "log.h"
-#include "parser.h"
-#include "service.h"
-#include "util.h"
 
+#include <dirent.h>
+
+#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "parser.h"
+#include "util.h"
 
 Parser::Parser() {
 }
@@ -40,13 +38,16 @@
     section_parsers_[name] = std::move(parser);
 }
 
+void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
+    line_callbacks_.emplace_back(prefix, callback);
+}
+
 void Parser::ParseData(const std::string& filename, const std::string& data) {
     //TODO: Use a parser with const input and remove this copy
     std::vector<char> data_copy(data.begin(), data.end());
     data_copy.push_back('\0');
 
     parse_state state;
-    state.filename = filename.c_str();
     state.line = 0;
     state.ptr = &data_copy[0];
     state.nexttoken = 0;
@@ -66,21 +67,34 @@
             if (args.empty()) {
                 break;
             }
+            // If we have a line matching a prefix we recognize, call its callback and unset any
+            // current section parsers.  This is meant for /sys/ and /dev/ line entries for uevent.
+            for (const auto& [prefix, callback] : line_callbacks_) {
+                if (android::base::StartsWith(args[0], prefix.c_str())) {
+                    if (section_parser) section_parser->EndSection();
+
+                    std::string ret_err;
+                    if (!callback(std::move(args), &ret_err)) {
+                        LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
+                    }
+                    section_parser = nullptr;
+                    break;
+                }
+            }
             if (section_parsers_.count(args[0])) {
                 if (section_parser) {
                     section_parser->EndSection();
                 }
                 section_parser = section_parsers_[args[0]].get();
                 std::string ret_err;
-                if (!section_parser->ParseSection(args, &ret_err)) {
-                    parse_error(&state, "%s\n", ret_err.c_str());
+                if (!section_parser->ParseSection(std::move(args), filename, state.line, &ret_err)) {
+                    LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
                     section_parser = nullptr;
                 }
             } else if (section_parser) {
                 std::string ret_err;
-                if (!section_parser->ParseLineSection(args, state.filename,
-                                                      state.line, &ret_err)) {
-                    parse_error(&state, "%s\n", ret_err.c_str());
+                if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) {
+                    LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
                 }
             }
             args.clear();
@@ -96,14 +110,16 @@
     LOG(INFO) << "Parsing file " << path << "...";
     Timer t;
     std::string data;
-    if (!read_file(path, &data)) {
+    std::string err;
+    if (!ReadFile(path, &data, &err)) {
+        LOG(ERROR) << err;
         return false;
     }
 
     data.push_back('\n'); // TODO: fix parse_config.
     ParseData(path, data);
-    for (const auto& sp : section_parsers_) {
-        sp.second->EndFile(path);
+    for (const auto& [section_name, section_parser] : section_parsers_) {
+        section_parser->EndFile();
     }
 
     LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
@@ -143,8 +159,3 @@
     }
     return ParseConfigFile(path);
 }
-
-void Parser::DumpState() const {
-    ServiceManager::GetInstance().DumpState();
-    ActionManager::GetInstance().DumpState();
-}
diff --git a/init/init_parser.h b/init/init_parser.h
index f66ba52..722ebb2 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -18,50 +18,76 @@
 #define _INIT_INIT_PARSER_H_
 
 #include <map>
+#include <memory>
 #include <string>
 #include <vector>
 
+//  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.
+//
+//  1) bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
+//                       int line, std::string* err)
+//    This function is called when a section is first encountered.
+//
+//  2) bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err)
+//    This function is called on each subsequent line until the next section is encountered.
+//
+//  3) bool 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()
+//    This function is called at the end of the file.
+//    It indicates that the parsing has completed and any relevant objects should be committed.
+
 class SectionParser {
-public:
-    virtual ~SectionParser() {
-    }
-    virtual bool ParseSection(const std::vector<std::string>& args,
-                              std::string* err) = 0;
-    virtual bool ParseLineSection(const std::vector<std::string>& args,
-                                  const std::string& filename, int line,
-                                  std::string* err) const = 0;
-    virtual void EndSection() = 0;
-    virtual void EndFile(const std::string& filename) = 0;
+  public:
+    virtual ~SectionParser() {}
+    virtual bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                              int line, std::string* err) = 0;
+    virtual bool ParseLineSection(std::vector<std::string>&&, int, std::string*) { return true; };
+    virtual void EndSection(){};
+    virtual void EndFile(){};
 };
 
 class Parser {
-public:
+  public:
+    //  LineCallback is the type for callbacks that can parse a line starting with a given prefix.
+    //
+    //  They take the form of bool Callback(std::vector<std::string>&& args, std::string* err)
+    //
+    //  Similar to ParseSection() and ParseLineSection(), this function returns bool with false
+    //  indicating a failure and has an std::string* err parameter into which an error string can
+    //  be written.
+    using LineCallback = std::function<bool(std::vector<std::string>&&, std::string*)>;
+
+    // TODO: init is the only user of this as a singleton; remove it.
     static Parser& GetInstance();
-    void DumpState() const;
+
+    Parser();
+
     bool ParseConfig(const std::string& path);
-    void AddSectionParser(const std::string& name,
-                          std::unique_ptr<SectionParser> parser);
-    void set_is_system_etc_init_loaded(bool loaded) {
-        is_system_etc_init_loaded_ = loaded;
-    }
-    void set_is_vendor_etc_init_loaded(bool loaded) {
-        is_vendor_etc_init_loaded_ = loaded;
-    }
-    void set_is_odm_etc_init_loaded(bool loaded) {
-        is_odm_etc_init_loaded_ = loaded;
-    }
+    void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
+    void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+    void set_is_system_etc_init_loaded(bool loaded) { is_system_etc_init_loaded_ = loaded; }
+    void set_is_vendor_etc_init_loaded(bool loaded) { is_vendor_etc_init_loaded_ = loaded; }
+    void set_is_odm_etc_init_loaded(bool loaded) { is_odm_etc_init_loaded_ = loaded; }
     bool is_system_etc_init_loaded() { return is_system_etc_init_loaded_; }
     bool is_vendor_etc_init_loaded() { return is_vendor_etc_init_loaded_; }
     bool is_odm_etc_init_loaded() { return is_odm_etc_init_loaded_; }
 
-private:
-    Parser();
-
+  private:
     void ParseData(const std::string& filename, const std::string& data);
     bool ParseConfigFile(const std::string& path);
     bool ParseConfigDir(const std::string& path);
 
     std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
+    std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
     bool is_system_etc_init_loaded_ = false;
     bool is_vendor_etc_init_loaded_ = false;
     bool is_odm_etc_init_loaded_ = false;
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
index 52aaa37..86d60d0 100644
--- a/init/init_parser_test.cpp
+++ b/init/init_parser_test.cpp
@@ -18,9 +18,7 @@
 
 #include "init.h"
 #include "service.h"
-#include "util.h"
 
-#include <errno.h>
 #include <gtest/gtest.h>
 
 #include <string>
@@ -88,19 +86,29 @@
         ASSERT_EQ("", svc->seclabel());
     }
     if (uid) {
-        ASSERT_EQ(decode_uid("log"), svc->uid());
+        uid_t decoded_uid;
+        std::string err;
+        ASSERT_TRUE(DecodeUid("log", &decoded_uid, &err));
+        ASSERT_EQ(decoded_uid, svc->uid());
     } else {
         ASSERT_EQ(0U, svc->uid());
     }
     if (gid) {
-        ASSERT_EQ(decode_uid("shell"), svc->gid());
+        uid_t decoded_uid;
+        std::string err;
+        ASSERT_TRUE(DecodeUid("shell", &decoded_uid, &err));
+        ASSERT_EQ(decoded_uid, svc->gid());
     } else {
         ASSERT_EQ(0U, svc->gid());
     }
     if (supplementary_gids) {
         ASSERT_EQ(2U, svc->supp_gids().size());
-        ASSERT_EQ(decode_uid("system"), svc->supp_gids()[0]);
-        ASSERT_EQ(decode_uid("adb"), svc->supp_gids()[1]);
+        uid_t decoded_uid;
+        std::string err;
+        ASSERT_TRUE(DecodeUid("system", &decoded_uid, &err));
+        ASSERT_EQ(decoded_uid, svc->supp_gids()[0]);
+        ASSERT_TRUE(DecodeUid("adb", &decoded_uid, &err));
+        ASSERT_EQ(decoded_uid, svc->supp_gids()[1]);
     } else {
         ASSERT_EQ(0U, svc->supp_gids().size());
     }
diff --git a/init/init_test.cpp b/init/init_test.cpp
new file mode 100644
index 0000000..7093ba9
--- /dev/null
+++ b/init/init_test.cpp
@@ -0,0 +1,187 @@
+/*
+ * 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 <functional>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "action.h"
+#include "builtins.h"
+#include "import_parser.h"
+#include "init_parser.h"
+#include "keyword_map.h"
+#include "util.h"
+
+class TestFunctionMap : public KeywordMap<BuiltinFunction> {
+  public:
+    // Helper for argument-less functions
+    using BuiltinFunctionNoArgs = std::function<void(void)>;
+    void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
+        Add(name, 0, 0, [function](const std::vector<std::string>&) {
+            function();
+            return 0;
+        });
+    }
+
+    void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
+             const BuiltinFunction function) {
+        builtin_functions_[name] = make_tuple(min_parameters, max_parameters, function);
+    }
+
+  private:
+    Map builtin_functions_ = {};
+
+    const Map& map() const override { return builtin_functions_; }
+};
+
+using ActionManagerCommand = std::function<void(ActionManager&)>;
+
+void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
+              const std::vector<ActionManagerCommand>& commands) {
+    ActionManager am;
+
+    Action::set_function_map(&test_function_map);
+
+    Parser parser;
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
+
+    ASSERT_TRUE(parser.ParseConfig(init_script_file));
+
+    for (const auto& command : commands) {
+        command(am);
+    }
+
+    while (am.HasMoreCommands()) {
+        am.ExecuteOneCommand();
+    }
+}
+
+void TestInitText(const std::string& init_script, const TestFunctionMap& test_function_map,
+                  const std::vector<ActionManagerCommand>& commands) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+    TestInit(tf.path, test_function_map, commands);
+}
+
+TEST(init, SimpleEventTrigger) {
+    bool expect_true = false;
+    std::string init_script =
+        R"init(
+on boot
+pass_test
+)init";
+
+    TestFunctionMap test_function_map;
+    test_function_map.Add("pass_test", [&expect_true]() { expect_true = true; });
+
+    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+    std::vector<ActionManagerCommand> commands{trigger_boot};
+
+    TestInitText(init_script, test_function_map, commands);
+
+    EXPECT_TRUE(expect_true);
+}
+
+TEST(init, EventTriggerOrder) {
+    std::string init_script =
+        R"init(
+on boot
+execute_first
+
+on boot && property:ro.hardware=*
+execute_second
+
+on boot
+execute_third
+
+)init";
+
+    int num_executed = 0;
+    TestFunctionMap test_function_map;
+    test_function_map.Add("execute_first", [&num_executed]() { EXPECT_EQ(0, num_executed++); });
+    test_function_map.Add("execute_second", [&num_executed]() { EXPECT_EQ(1, num_executed++); });
+    test_function_map.Add("execute_third", [&num_executed]() { EXPECT_EQ(2, num_executed++); });
+
+    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+    std::vector<ActionManagerCommand> commands{trigger_boot};
+
+    TestInitText(init_script, test_function_map, commands);
+}
+
+TEST(init, EventTriggerOrderMultipleFiles) {
+    // 6 total files, which should have their triggers executed in the following order:
+    // 1: start - original script parsed
+    // 2: first_import - immediately imported by first_script
+    // 3: dir_a - file named 'a.rc' in dir; dir is imported after first_import
+    // 4: a_import - file imported by dir_a
+    // 5: dir_b - file named 'b.rc' in dir
+    // 6: last_import - imported after dir is imported
+
+    TemporaryFile first_import;
+    ASSERT_TRUE(first_import.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 2", first_import.fd));
+
+    TemporaryFile dir_a_import;
+    ASSERT_TRUE(dir_a_import.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 4", dir_a_import.fd));
+
+    TemporaryFile last_import;
+    ASSERT_TRUE(last_import.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 6", last_import.fd));
+
+    TemporaryDir dir;
+    // clang-format off
+    std::string dir_a_script = "import " + std::string(dir_a_import.path) + "\n"
+                               "on boot\n"
+                               "execute 3";
+    // clang-format on
+    // WriteFile() ensures the right mode is set
+    std::string err;
+    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/a.rc", dir_a_script, &err));
+
+    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/b.rc", "on boot\nexecute 5", &err));
+
+    // clang-format off
+    std::string start_script = "import " + std::string(first_import.path) + "\n"
+                               "import " + std::string(dir.path) + "\n"
+                               "import " + std::string(last_import.path) + "\n"
+                               "on boot\n"
+                               "execute 1";
+    // clang-format on
+    TemporaryFile start;
+    ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
+
+    int num_executed = 0;
+    auto execute_command = [&num_executed](const std::vector<std::string>& args) {
+        EXPECT_EQ(2U, args.size());
+        EXPECT_EQ(++num_executed, std::stoi(args[1]));
+        return 0;
+    };
+
+    TestFunctionMap test_function_map;
+    test_function_map.Add("execute", 1, 1, execute_command);
+
+    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+    std::vector<ActionManagerCommand> commands{trigger_boot};
+
+    TestInit(start.path, test_function_map, commands);
+
+    EXPECT_EQ(6, num_executed);
+}
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 5801ea8..c572cee 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -14,19 +14,17 @@
  * limitations under the License.
  */
 
-#include <errno.h>
 #include <fcntl.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <linux/keychord.h>
 #include <unistd.h>
 
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 
 #include "init.h"
-#include "log.h"
 #include "service.h"
 
 static struct input_keychord *keychords = 0;
diff --git a/init/keyutils.h b/init/keyutils.h
deleted file mode 100644
index de01beb..0000000
--- a/init/keyutils.h
+++ /dev/null
@@ -1,44 +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.
- */
-
-/* Miniature version of a header-only keyutils.h (no library required) */
-
-#ifndef _INIT_KEYUTILS_H_
-#define _INIT_KEYUTILS_H_
-
-#ifndef KEYUTILS_H /* walk away if the _real_ one exists */
-
-#include <linux/keyctl.h>
-#include <stdarg.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-
-static inline long keyctl(int cmd, ...) {
-    va_list va;
-    unsigned long arg2, arg3, arg4, arg5;
-
-    va_start(va, cmd);
-    arg2 = va_arg(va, unsigned long);
-    arg3 = va_arg(va, unsigned long);
-    arg4 = va_arg(va, unsigned long);
-    arg5 = va_arg(va, unsigned long);
-    va_end(va);
-    return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
-}
-
-#endif
-
-#endif
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 693d82a..88bad01 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -24,18 +24,23 @@
 
 template <typename Function>
 class KeywordMap {
-public:
+  public:
     using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
-    using Map = const std::map<std::string, FunctionInfo>;
+    using Map = std::map<std::string, FunctionInfo>;
 
     virtual ~KeywordMap() {
     }
 
-    const Function FindFunction(const std::string& keyword,
-                                size_t num_args,
-                                std::string* err) const {
+    const Function FindFunction(const std::vector<std::string>& args, std::string* err) const {
         using android::base::StringPrintf;
 
+        if (args.empty()) {
+            *err = "keyword needed, but not provided";
+            return nullptr;
+        }
+        auto& keyword = args[0];
+        auto num_args = args.size() - 1;
+
         auto function_info_it = map().find(keyword);
         if (function_info_it == map().end()) {
             *err = StringPrintf("invalid keyword '%s'", keyword.c_str());
@@ -68,10 +73,10 @@
         return std::get<Function>(function_info);
     }
 
-private:
-//Map of keyword ->
-//(minimum number of arguments, maximum number of arguments, function pointer)
-    virtual Map& map() const = 0;
+  private:
+    // Map of keyword ->
+    // (minimum number of arguments, maximum number of arguments, function pointer)
+    virtual const Map& map() const = 0;
 };
 
 #endif
diff --git a/init/log.h b/init/log.h
index 8fa6d74..29a27af 100644
--- a/init/log.h
+++ b/init/log.h
@@ -17,7 +17,7 @@
 #ifndef _INIT_LOG_H_
 #define _INIT_LOG_H_
 
-#include <android-base/logging.h>
+#include <sys/cdefs.h>
 
 void InitKernelLogging(char* argv[]);
 
diff --git a/init/parser.cpp b/init/parser.cpp
index 45862b7..0d13cfe 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -1,28 +1,5 @@
 #include "parser.h"
 
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "log.h"
-
-void parse_error(struct parse_state *state, const char *fmt, ...)
-{
-    va_list ap;
-    char buf[128];
-    int off;
-
-    snprintf(buf, sizeof(buf), "%s: %d: ", state->filename, state->line);
-    buf[127] = 0;
-    off = strlen(buf);
-
-    va_start(ap, fmt);
-    vsnprintf(buf + off, 128 - off, fmt, ap);
-    va_end(ap);
-    buf[127] = 0;
-    LOG(ERROR) << buf;
-}
-
 int next_token(struct parse_state *state)
 {
     char *x = state->ptr;
diff --git a/init/parser.h b/init/parser.h
index 95e1164..3dcc566 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -27,14 +27,8 @@
     char *text;
     int line;
     int nexttoken;
-    void *context;
-    void (*parse_line)(struct parse_state *state, int nargs, char **args);
-    const char *filename;
-    void *priv;
 };
 
-void dump_parser_state(void);
 int next_token(struct parse_state *state);
-void parse_error(struct parse_state *state, const char *fmt, ...);
 
 #endif /* PARSER_H_ */
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 73954fe..20f21a9 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -14,47 +14,46 @@
  * limitations under the License.
  */
 
+#include "property_service.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <inttypes.h>
+#include <limits.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <string.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <dirent.h>
-#include <limits.h>
-#include <errno.h>
+#include <sys/mman.h>
 #include <sys/poll.h>
-
-#include <memory>
-#include <vector>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
 
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <sys/mman.h>
-
-#include <selinux/android.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
+#include <memory>
+#include <vector>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <bootimg.h>
 #include <fs_mgr.h>
-#include "bootimg.h"
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
 
-#include "property_service.h"
 #include "init.h"
 #include "util.h"
-#include "log.h"
 
 using android::base::StringPrintf;
 
@@ -178,7 +177,7 @@
     }
 
     if (name == "selinux.restorecon_recursive" && valuelen > 0) {
-        if (restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+        if (selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
             LOG(ERROR) << "Failed to restorecon_recursive " << value;
         }
     }
@@ -511,8 +510,9 @@
 static bool load_properties_from_file(const char* filename, const char* filter) {
     Timer t;
     std::string data;
-    if (!read_file(filename, &data)) {
-        PLOG(WARNING) << "Couldn't load properties from " << filename;
+    std::string err;
+    if (!ReadFile(filename, &data, &err)) {
+        PLOG(WARNING) << "Couldn't load property file: " << err;
         return false;
     }
     data.push_back('\n');
@@ -666,8 +666,8 @@
 void start_property_service() {
     property_set("ro.property_service.version", "2");
 
-    property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                                    0666, 0, 0, NULL);
+    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+                                   false, 0666, 0, 0, nullptr, sehandle);
     if (property_set_fd == -1) {
         PLOG(ERROR) << "start_property_service socket creation failed";
         exit(1);
diff --git a/init/property_service.h b/init/property_service.h
index 994da63..9a5b6f6 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -17,9 +17,8 @@
 #ifndef _INIT_PROPERTY_H
 #define _INIT_PROPERTY_H
 
-#include <stddef.h>
 #include <sys/socket.h>
-#include <sys/system_properties.h>
+
 #include <string>
 
 struct property_audit_data {
diff --git a/init/reboot.cpp b/init/reboot.cpp
index aac1b5c..cee5a72 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -13,6 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+#include "reboot.h"
+
 #include <dirent.h>
 #include <fcntl.h>
 #include <linux/fs.h>
@@ -29,11 +32,11 @@
 
 #include <memory>
 #include <set>
-#include <string>
 #include <thread>
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -45,11 +48,9 @@
 #include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
 
-#include "log.h"
+#include "init.h"
 #include "property_service.h"
-#include "reboot.h"
 #include "service.h"
-#include "util.h"
 
 using android::base::StringPrintf;
 
@@ -197,7 +198,13 @@
             LOG(INFO) << "mount entry " << mentry->mnt_fsname << ":" << mentry->mnt_dir << " opts "
                       << mentry->mnt_opts << " type " << mentry->mnt_type;
         } else if (MountEntry::IsBlockDevice(*mentry) && hasmntopt(mentry, "rw")) {
-            blockDevPartitions->emplace(blockDevPartitions->begin(), *mentry);
+            std::string mount_dir(mentry->mnt_dir);
+            // These are R/O partitions changed to R/W after adb remount.
+            // Do not umount them as shutdown critical services may rely on them.
+            if (mount_dir != "/" && mount_dir != "/system" && mount_dir != "/vendor" &&
+                mount_dir != "/oem") {
+                blockDevPartitions->emplace(blockDevPartitions->begin(), *mentry);
+            }
         } else if (MountEntry::IsEmulatedDevice(*mentry)) {
             emulatedPartitions->emplace(emulatedPartitions->begin(), *mentry);
         }
@@ -281,11 +288,11 @@
     UmountStat stat = UmountPartitions(timeoutMs - t.duration_ms());
     if (stat != UMOUNT_STAT_SUCCESS) {
         LOG(INFO) << "umount timeout, last resort, kill all and try";
-        if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
+        if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
         KillAllProcesses();
         // even if it succeeds, still it is timeout and do not run fsck with all processes killed
-        UmountPartitions(0);
-        if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
+        UmountStat st = UmountPartitions(0);
+        if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
     }
 
     if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
@@ -298,13 +305,6 @@
     return stat;
 }
 
-static void __attribute__((noreturn)) DoThermalOff() {
-    LOG(WARNING) << "Thermal system shutdown";
-    sync();
-    RebootSystem(ANDROID_RB_THERMOFF, "");
-    abort();
-}
-
 void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
               bool runFsck) {
     Timer t;
@@ -313,31 +313,38 @@
     android::base::WriteStringToFile(StringPrintf("%s\n", reason.c_str()), LAST_REBOOT_REASON_FILE,
                                      S_IRUSR | S_IWUSR, AID_SYSTEM, AID_SYSTEM);
 
-    if (cmd == ANDROID_RB_THERMOFF) {  // do not wait if it is thermal
-        DoThermalOff();
-        abort();
+    bool is_thermal_shutdown = false;
+    if (cmd == ANDROID_RB_THERMOFF) {
+        is_thermal_shutdown = true;
+        runFsck = false;
     }
 
-    constexpr unsigned int shutdownTimeoutDefault = 6;
-    unsigned int shutdownTimeout = shutdownTimeoutDefault;
-    if (SHUTDOWN_ZERO_TIMEOUT) {  // eng build
-        shutdownTimeout = 0;
-    } else {
-        shutdownTimeout =
-            android::base::GetUintProperty("ro.build.shutdown_timeout", shutdownTimeoutDefault);
+    unsigned int shutdown_timeout = 0;  // ms
+    if (!SHUTDOWN_ZERO_TIMEOUT) {
+        if (is_thermal_shutdown) {
+            constexpr unsigned int thermal_shutdown_timeout = 1;  // sec
+            shutdown_timeout = thermal_shutdown_timeout * 1000;
+        } else {
+            constexpr unsigned int shutdown_timeout_default = 6;  // sec
+            auto shutdown_timeout_property = android::base::GetUintProperty(
+                "ro.build.shutdown_timeout", shutdown_timeout_default);
+            shutdown_timeout = shutdown_timeout_property * 1000;
+        }
     }
-    LOG(INFO) << "Shutdown timeout: " << shutdownTimeout;
+    LOG(INFO) << "Shutdown timeout: " << shutdown_timeout << " ms";
 
     // keep debugging tools until non critical ones are all gone.
     const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
     // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
-    const std::set<std::string> to_starts{"watchdogd", "vold", "ueventd"};
+    const std::set<std::string> to_starts{"watchdogd"};
     ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
         if (kill_after_apps.count(s->name())) {
             s->SetShutdownCritical();
         } else if (to_starts.count(s->name())) {
             s->Start();
             s->SetShutdownCritical();
+        } else if (s->IsShutdownCritical()) {
+            s->Start();  // start shutdown critical service if not started
         }
     });
 
@@ -351,7 +358,7 @@
 
     // optional shutdown step
     // 1. terminate all services except shutdown critical ones. wait for delay to finish
-    if (shutdownTimeout > 0) {
+    if (shutdown_timeout > 0) {
         LOG(INFO) << "terminating init services";
 
         // Ask all services to terminate except shutdown critical ones.
@@ -360,9 +367,9 @@
         });
 
         int service_count = 0;
-        // Up to half as long as shutdownTimeout or 3 seconds, whichever is lower.
-        unsigned int terminationWaitTimeout = std::min<unsigned int>((shutdownTimeout + 1) / 2, 3);
-        while (t.duration_s() < terminationWaitTimeout) {
+        // Only wait up to half of timeout here
+        auto termination_wait_timeout = shutdown_timeout / 2;
+        while (t.duration_ms() < termination_wait_timeout) {
             ServiceManager::GetInstance().ReapAnyOutstandingChildren();
 
             service_count = 0;
@@ -410,10 +417,10 @@
     });
     // 4. sync, try umount, and optionally run fsck for user shutdown
     sync();
-    UmountStat stat = TryUmountAndFsck(runFsck, shutdownTimeout * 1000 - t.duration_ms());
+    UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration_ms());
     // Follow what linux shutdown is doing: one more sync with little bit delay
     sync();
-    std::this_thread::sleep_for(100ms);
+    if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
     LogShutdownTime(stat, &t);
     // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
     RebootSystem(cmd, rebootTarget);
@@ -456,6 +463,7 @@
             }
         }
     } else if (command == "thermal-shutdown") {  // no additional parameter allowed
+        // run_fsck is false to avoid delay
         cmd = ANDROID_RB_THERMOFF;
     } else {
         command_invalid = true;
@@ -465,6 +473,25 @@
         return false;
     }
 
-    DoReboot(cmd, command, reboot_target, run_fsck);
+    LOG(INFO) << "Clear action queue and start shutdown trigger";
+    ActionManager::GetInstance().ClearQueue();
+    // Queue shutdown trigger first
+    ActionManager::GetInstance().QueueEventTrigger("shutdown");
+    // Queue built-in shutdown_done
+    auto shutdown_handler = [cmd, command, reboot_target,
+                             run_fsck](const std::vector<std::string>&) {
+        DoReboot(cmd, command, reboot_target, run_fsck);
+        return 0;
+    };
+    ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
+
+    // Skip wait for prop if it is in progress
+    ResetWaitForProp();
+
+    // Skip wait for exec if it is in progress
+    if (ServiceManager::GetInstance().IsWaitingForExec()) {
+        ServiceManager::GetInstance().ClearExecWait();
+    }
+
     return true;
 }
diff --git a/init/reboot.h b/init/reboot.h
index cd6be39..b304b3c 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -17,6 +17,8 @@
 #ifndef _INIT_REBOOT_H
 #define _INIT_REBOOT_H
 
+#include <string>
+
 /* Reboot / shutdown the system.
  * cmd ANDROID_RB_* as defined in android_reboot.h
  * reason Reason string like "reboot", "userrequested"
diff --git a/init/service.cpp b/init/service.cpp
index 9ba0272..09ebc31 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -24,30 +24,27 @@
 #include <sys/prctl.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
+#include <sys/system_properties.h>
 #include <sys/time.h>
-#include <sys/types.h>
 #include <sys/wait.h>
 #include <termios.h>
 #include <unistd.h>
 
-#include <selinux/selinux.h>
-
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <processgroup/processgroup.h>
+#include <selinux/selinux.h>
 #include <system/thread_defs.h>
 
-#include <processgroup/processgroup.h>
-
-#include "action.h"
 #include "init.h"
-#include "init_parser.h"
-#include "log.h"
 #include "property_service.h"
 #include "util.h"
 
+using android::base::boot_clock;
 using android::base::ParseInt;
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
@@ -160,6 +157,7 @@
       gid_(0),
       namespace_flags_(0),
       seclabel_(""),
+      onrestart_(false, "<Service '" + name + "' onrestart>", 0),
       keychord_id_(0),
       ioprio_class_(IoSchedClass_NONE),
       ioprio_pri_(0),
@@ -184,6 +182,7 @@
       capabilities_(capabilities),
       namespace_flags_(namespace_flags),
       seclabel_(seclabel),
+      onrestart_(false, "<Service '" + name + "' onrestart>", 0),
       keychord_id_(0),
       ioprio_class_(IoSchedClass_NONE),
       ioprio_pri_(0),
@@ -210,20 +209,21 @@
 }
 
 void Service::KillProcessGroup(int signal) {
-    LOG(INFO) << "Sending signal " << signal
-              << " to service '" << name_
-              << "' (pid " << pid_ << ") process group...";
-    int r;
-    if (signal == SIGTERM) {
-        r = killProcessGroupOnce(uid_, pid_, signal);
-    } else {
-        r = killProcessGroup(uid_, pid_, signal);
-    }
-    if (r == -1) {
-        PLOG(ERROR) << "killProcessGroup(" << uid_ << ", " << pid_ << ", " << signal << ") failed";
-    }
-    if (kill(-pid_, signal) == -1) {
-        PLOG(ERROR) << "kill(" << pid_ << ", " << signal << ") failed";
+    // If we've already seen a successful result from killProcessGroup*(), then we have removed
+    // the cgroup already and calling these functions a second time will simply result in an error.
+    // This is true regardless of which signal was sent.
+    // These functions handle their own logging, so no additional logging is needed.
+    if (!process_cgroup_empty_) {
+        LOG(INFO) << "Sending signal " << signal << " to service '" << name_ << "' (pid " << pid_
+                  << ") process group...";
+        int r;
+        if (signal == SIGTERM) {
+            r = killProcessGroupOnce(uid_, pid_, signal);
+        } else {
+            r = killProcessGroup(uid_, pid_, signal);
+        }
+
+        if (r == 0) process_cgroup_empty_ = true;
     }
 }
 
@@ -381,9 +381,18 @@
 }
 
 bool Service::ParseGroup(const std::vector<std::string>& args, std::string* err) {
-    gid_ = decode_uid(args[1].c_str());
+    std::string decode_uid_err;
+    if (!DecodeUid(args[1], &gid_, &decode_uid_err)) {
+        *err = "Unable to find GID for '" + args[1] + "': " + decode_uid_err;
+        return false;
+    }
     for (std::size_t n = 2; n < args.size(); n++) {
-        supp_gids_.emplace_back(decode_uid(args[n].c_str()));
+        gid_t gid;
+        if (!DecodeUid(args[n], &gid, &decode_uid_err)) {
+            *err = "Unable to find GID for '" + args[n] + "': " + decode_uid_err;
+            return false;
+        }
+        supp_gids_.emplace_back(gid);
     }
     return true;
 }
@@ -439,7 +448,8 @@
 
 bool Service::ParseOnrestart(const std::vector<std::string>& args, std::string* err) {
     std::vector<std::string> str_args(args.begin() + 1, args.end());
-    onrestart_.AddCommand(str_args, "", 0, err);
+    int line = onrestart_.NumCommands() + 1;
+    onrestart_.AddCommand(str_args, line, err);
     return true;
 }
 
@@ -477,13 +487,36 @@
     return true;
 }
 
+bool Service::ParseShutdown(const std::vector<std::string>& args, std::string* err) {
+    if (args[1] == "critical") {
+        flags_ |= SVC_SHUTDOWN_CRITICAL;
+        return true;
+    }
+    return false;
+}
+
 template <typename T>
 bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
     int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
-    uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
-    gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
+    uid_t uid = 0;
+    gid_t gid = 0;
     std::string context = args.size() > 6 ? args[6] : "";
 
+    std::string decode_uid_err;
+    if (args.size() > 4) {
+        if (!DecodeUid(args[4], &uid, &decode_uid_err)) {
+            *err = "Unable to find UID for '" + args[4] + "': " + decode_uid_err;
+            return false;
+        }
+    }
+
+    if (args.size() > 5) {
+        if (!DecodeUid(args[5], &gid, &decode_uid_err)) {
+            *err = "Unable to find GID for '" + args[5] + "': " + decode_uid_err;
+            return false;
+        }
+    }
+
     auto descriptor = std::make_unique<T>(args[1], args[2], uid, gid, perm, context);
 
     auto old =
@@ -501,7 +534,9 @@
 
 // name type perm [ uid gid context ]
 bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) {
-    if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") {
+    if (!android::base::StartsWith(args[2], "dgram") &&
+        !android::base::StartsWith(args[2], "stream") &&
+        !android::base::StartsWith(args[2], "seqpacket")) {
         *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
         return false;
     }
@@ -522,7 +557,11 @@
 }
 
 bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) {
-    uid_ = decode_uid(args[1].c_str());
+    std::string decode_uid_err;
+    if (!DecodeUid(args[1], &uid_, &decode_uid_err)) {
+        *err = "Unable to find UID for '" + args[1] + "': " + decode_uid_err;
+        return false;
+    }
     return true;
 }
 
@@ -532,14 +571,14 @@
 }
 
 class Service::OptionParserMap : public KeywordMap<OptionParser> {
-public:
-    OptionParserMap() {
-    }
-private:
-    Map& map() const override;
+  public:
+    OptionParserMap() {}
+
+  private:
+    const Map& map() const override;
 };
 
-Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
+const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     // clang-format off
     static const Map option_parsers = {
@@ -560,6 +599,7 @@
         {"namespace",   {1,     2,    &Service::ParseNamespace}},
         {"seclabel",    {1,     1,    &Service::ParseSeclabel}},
         {"setenv",      {2,     2,    &Service::ParseSetenv}},
+        {"shutdown",    {1,     1,    &Service::ParseShutdown}},
         {"socket",      {3,     6,    &Service::ParseSocket}},
         {"file",        {2,     2,    &Service::ParseFile}},
         {"user",        {1,     1,    &Service::ParseUser}},
@@ -570,13 +610,8 @@
 }
 
 bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
-    if (args.empty()) {
-        *err = "option needed, but not provided";
-        return false;
-    }
-
     static const OptionParserMap parser_map;
-    auto parser = parser_map.FindFunction(args[0], args.size() - 1, err);
+    auto parser = parser_map.FindFunction(args, err);
 
     if (!parser) {
         return false;
@@ -637,7 +672,6 @@
     if (!seclabel_.empty()) {
         scon = seclabel_;
     } else {
-        LOG(INFO) << "computing context for service '" << name_ << "'";
         scon = ComputeContextFromExecutable(name_, args_[0]);
         if (scon == "") {
             return false;
@@ -742,6 +776,7 @@
     time_started_ = boot_clock::now();
     pid_ = pid;
     flags_ |= SVC_RUNNING;
+    process_cgroup_empty_ = false;
 
     errno = -createProcessGroup(uid_, pid_);
     if (errno != 0) {
@@ -875,11 +910,6 @@
 }
 
 void ServiceManager::AddService(std::unique_ptr<Service> service) {
-    Service* old_service = FindServiceByName(service->name());
-    if (old_service) {
-        LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
-        return;
-    }
     services_.emplace_back(std::move(service));
 }
 
@@ -934,7 +964,9 @@
     std::vector<std::string> str_args(args.begin() + command_arg, args.end());
 
     exec_count_++;
-    std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
+    std::string name =
+        "exec " + std::to_string(exec_count_) + " (" + android::base::Join(str_args, " ") + ")";
+
     unsigned flags = SVC_EXEC | SVC_ONESHOT | SVC_TEMPORARY;
     CapSet no_capabilities;
     unsigned namespace_flags = 0;
@@ -945,15 +977,28 @@
     }
     uid_t uid = 0;
     if (command_arg > 3) {
-        uid = decode_uid(args[2].c_str());
+        std::string decode_uid_err;
+        if (!DecodeUid(args[2], &uid, &decode_uid_err)) {
+            LOG(ERROR) << "Unable to find UID for '" << args[2] << "': " << decode_uid_err;
+            return nullptr;
+        }
     }
     gid_t gid = 0;
     std::vector<gid_t> supp_gids;
     if (command_arg > 4) {
-        gid = decode_uid(args[3].c_str());
+        std::string decode_uid_err;
+        if (!DecodeUid(args[3], &gid, &decode_uid_err)) {
+            LOG(ERROR) << "Unable to find GID for '" << args[3] << "': " << decode_uid_err;
+            return nullptr;
+        }
         std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
         for (size_t i = 0; i < nr_supp_gids; ++i) {
-            supp_gids.push_back(decode_uid(args[4 + i].c_str()));
+            gid_t supp_gid;
+            if (!DecodeUid(args[4 + i], &supp_gid, &decode_uid_err)) {
+                LOG(ERROR) << "Unable to find UID for '" << args[4 + i] << "': " << decode_uid_err;
+                return nullptr;
+            }
+            supp_gids.push_back(supp_gid);
         }
     }
 
@@ -1097,8 +1142,17 @@
     }
 }
 
-bool ServiceParser::ParseSection(const std::vector<std::string>& args,
-                                 std::string* err) {
+void ServiceManager::ClearExecWait() {
+    // Clear EXEC flag if there is one pending
+    // And clear the wait flag
+    for (const auto& s : services_) {
+        s->UnSetExec();
+    }
+    exec_waiter_.reset();
+}
+
+bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line, std::string* err) {
     if (args.size() < 3) {
         *err = "services must have a name and a program";
         return false;
@@ -1110,20 +1164,24 @@
         return false;
     }
 
+    Service* old_service = service_manager_->FindServiceByName(name);
+    if (old_service) {
+        *err = "ignored duplicate definition of service '" + name + "'";
+        return false;
+    }
+
     std::vector<std::string> str_args(args.begin() + 2, args.end());
     service_ = std::make_unique<Service>(name, str_args);
     return true;
 }
 
-bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
-                                     const std::string& filename, int line,
-                                     std::string* err) const {
-    return service_ ? service_->ParseLine(args, err) : false;
+bool ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+    return service_ ? service_->ParseLine(std::move(args), err) : false;
 }
 
 void ServiceParser::EndSection() {
     if (service_) {
-        ServiceManager::GetInstance().AddService(std::move(service_));
+        service_manager_->AddService(std::move(service_));
     }
 }
 
diff --git a/init/service.h b/init/service.h
index f93ac38..25d1975 100644
--- a/init/service.h
+++ b/init/service.h
@@ -19,13 +19,14 @@
 
 #include <sys/types.h>
 
-#include <cutils/iosched_policy.h>
-
 #include <memory>
 #include <set>
 #include <string>
 #include <vector>
 
+#include <android-base/chrono_utils.h>
+#include <cutils/iosched_policy.h>
+
 #include "action.h"
 #include "capabilities.h"
 #include "descriptors.h"
@@ -88,6 +89,7 @@
     void DumpState() const;
     void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
     bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
+    void UnSetExec() { flags_ &= ~SVC_EXEC; }
 
     const std::string& name() const { return name_; }
     const std::set<std::string>& classnames() const { return classnames_; }
@@ -106,6 +108,7 @@
     int ioprio_pri() const { return ioprio_pri_; }
     int priority() const { return priority_; }
     int oom_score_adjust() const { return oom_score_adjust_; }
+    bool process_cgroup_empty() const { return process_cgroup_empty_; }
     const std::vector<std::string>& args() const { return args_; }
 
   private:
@@ -135,6 +138,7 @@
     bool ParseNamespace(const std::vector<std::string>& args, std::string* err);
     bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
     bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
+    bool ParseShutdown(const std::vector<std::string>& args, std::string* err);
     bool ParseSocket(const std::vector<std::string>& args, std::string* err);
     bool ParseFile(const std::vector<std::string>& args, std::string* err);
     bool ParseUser(const std::vector<std::string>& args, std::string* err);
@@ -149,8 +153,8 @@
 
     unsigned flags_;
     pid_t pid_;
-    boot_clock::time_point time_started_; // time of last start
-    boot_clock::time_point time_crashed_; // first crash within inspection window
+    android::base::boot_clock::time_point time_started_;  // time of last start
+    android::base::boot_clock::time_point time_crashed_;  // first crash within inspection window
     int crash_count_;                     // number of times crashed within window
 
     uid_t uid_;
@@ -178,11 +182,13 @@
 
     int oom_score_adjust_;
 
+    bool process_cgroup_empty_ = false;
+
     std::vector<std::string> args_;
 };
 
 class ServiceManager {
-public:
+  public:
     static ServiceManager& GetInstance();
 
     // Exposed for testing
@@ -204,8 +210,9 @@
     void ReapAnyOutstandingChildren();
     void RemoveService(const Service& svc);
     void DumpState() const;
+    void ClearExecWait();
 
-private:
+  private:
     // Cleans up a child process that exited.
     // Returns true iff a children was cleaned up.
     bool ReapOneProcess();
@@ -217,20 +224,18 @@
 };
 
 class ServiceParser : public SectionParser {
-public:
-    ServiceParser() : service_(nullptr) {
-    }
-    bool ParseSection(const std::vector<std::string>& args,
+  public:
+    ServiceParser(ServiceManager* service_manager)
+        : service_manager_(service_manager), service_(nullptr) {}
+    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
                       std::string* err) override;
-    bool ParseLineSection(const std::vector<std::string>& args,
-                          const std::string& filename, int line,
-                          std::string* err) const override;
+    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
     void EndSection() override;
-    void EndFile(const std::string&) override {
-    }
-private:
+
+  private:
     bool IsValidName(const std::string& name) const;
 
+    ServiceManager* service_manager_;
     std::unique_ptr<Service> service_;
 };
 
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 4493f25..b9c4627 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -45,6 +45,7 @@
     EXPECT_EQ(0, service_in_old_memory->ioprio_pri());
     EXPECT_EQ(0, service_in_old_memory->priority());
     EXPECT_EQ(-1000, service_in_old_memory->oom_score_adjust());
+    EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
 
     for (std::size_t i = 0; i < memory_size; ++i) {
         old_memory[i] = 0xFF;
@@ -64,4 +65,5 @@
     EXPECT_EQ(0, service_in_old_memory2->ioprio_pri());
     EXPECT_EQ(0, service_in_old_memory2->priority());
     EXPECT_EQ(-1000, service_in_old_memory2->oom_score_adjust());
+    EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
 }
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index 5e3acac..4d56d84 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -14,22 +14,17 @@
  * limitations under the License.
  */
 
-#include <errno.h>
-#include <fcntl.h>
 #include <signal.h>
-#include <stdio.h>
+#include <string.h>
 #include <sys/socket.h>
 #include <sys/types.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
+#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
-#include "action.h"
 #include "init.h"
-#include "log.h"
 #include "service.h"
-#include "util.h"
 
 static int signal_write_fd = -1;
 static int signal_read_fd = -1;
diff --git a/init/test_service/Android.bp b/init/test_service/Android.bp
new file mode 100644
index 0000000..9bd6f27
--- /dev/null
+++ b/init/test_service/Android.bp
@@ -0,0 +1,22 @@
+//
+// 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_binary {
+    name: "test_service",
+    srcs: ["test_service.cpp"],
+    shared_libs: ["libbase"],
+    init_rc: ["test_service.rc"],
+}
diff --git a/init/test_service/Android.mk b/init/test_service/Android.mk
deleted file mode 100644
index 30c9e9d..0000000
--- a/init/test_service/Android.mk
+++ /dev/null
@@ -1,27 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-# Sample service for testing.
-# =========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := test_service
-LOCAL_SRC_FILES := test_service.cpp
-
-LOCAL_SHARED_LIBRARIES += libbase
-
-LOCAL_INIT_RC := test_service.rc
-
-include $(BUILD_EXECUTABLE)
diff --git a/libsystem/include/system/window.h b/init/uevent.h
similarity index 60%
copy from libsystem/include/system/window.h
copy to init/uevent.h
index efa10d6..1095665 100644
--- a/libsystem/include/system/window.h
+++ b/init/uevent.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.
@@ -14,9 +14,21 @@
  * limitations under the License.
  */
 
-#ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
-#define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
+#ifndef _INIT_UEVENT_H
+#define _INIT_UEVENT_H
 
-#include <system/window-deprecated.h>
+#include <string>
 
-#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
+struct Uevent {
+    std::string action;
+    std::string path;
+    std::string subsystem;
+    std::string firmware;
+    std::string partition_name;
+    std::string device_name;
+    int partition_num;
+    int major;
+    int minor;
+};
+
+#endif
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
new file mode 100644
index 0000000..923fa8e
--- /dev/null
+++ b/init/uevent_listener.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uevent_listener.h"
+
+#include <fcntl.h>
+#include <poll.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+#include <cutils/uevent.h>
+
+static void ParseEvent(const char* msg, Uevent* uevent) {
+    uevent->partition_num = -1;
+    uevent->major = -1;
+    uevent->minor = -1;
+    uevent->action.clear();
+    uevent->path.clear();
+    uevent->subsystem.clear();
+    uevent->firmware.clear();
+    uevent->partition_name.clear();
+    uevent->device_name.clear();
+    // currently ignoring SEQNUM
+    while (*msg) {
+        if (!strncmp(msg, "ACTION=", 7)) {
+            msg += 7;
+            uevent->action = msg;
+        } else if (!strncmp(msg, "DEVPATH=", 8)) {
+            msg += 8;
+            uevent->path = msg;
+        } else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
+            msg += 10;
+            uevent->subsystem = msg;
+        } else if (!strncmp(msg, "FIRMWARE=", 9)) {
+            msg += 9;
+            uevent->firmware = msg;
+        } else if (!strncmp(msg, "MAJOR=", 6)) {
+            msg += 6;
+            uevent->major = atoi(msg);
+        } else if (!strncmp(msg, "MINOR=", 6)) {
+            msg += 6;
+            uevent->minor = atoi(msg);
+        } else if (!strncmp(msg, "PARTN=", 6)) {
+            msg += 6;
+            uevent->partition_num = atoi(msg);
+        } else if (!strncmp(msg, "PARTNAME=", 9)) {
+            msg += 9;
+            uevent->partition_name = msg;
+        } else if (!strncmp(msg, "DEVNAME=", 8)) {
+            msg += 8;
+            uevent->device_name = msg;
+        }
+
+        // advance to after the next \0
+        while (*msg++)
+            ;
+    }
+
+    if (LOG_UEVENTS) {
+        LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
+                  << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
+                  << ", " << uevent->minor << " }";
+    }
+}
+
+UeventListener::UeventListener() {
+    // is 256K enough? udev uses 16MB!
+    device_fd_.reset(uevent_open_socket(256 * 1024, true));
+    if (device_fd_ == -1) {
+        LOG(FATAL) << "Could not open uevent socket";
+    }
+
+    fcntl(device_fd_, F_SETFL, O_NONBLOCK);
+}
+
+bool UeventListener::ReadUevent(Uevent* uevent) const {
+    char msg[UEVENT_MSG_LEN + 2];
+    int n = uevent_kernel_multicast_recv(device_fd_, msg, UEVENT_MSG_LEN);
+    if (n <= 0) {
+        if (errno != EAGAIN && errno != EWOULDBLOCK) {
+            LOG(ERROR) << "Error reading from Uevent Fd";
+        }
+        return false;
+    }
+    if (n >= UEVENT_MSG_LEN) {
+        LOG(ERROR) << "Uevent overflowed buffer, discarding";
+        // Return true here even if we discard as we may have more uevents pending and we
+        // want to keep processing them.
+        return true;
+    }
+
+    msg[n] = '\0';
+    msg[n + 1] = '\0';
+
+    ParseEvent(msg, uevent);
+
+    return true;
+}
+
+// RegenerateUevents*() walks parts of the /sys tree and pokes the uevent files to cause the kernel
+// to regenerate device add uevents that have already happened.  This is particularly useful when
+// starting ueventd, to regenerate all of the uevents that it had previously missed.
+//
+// We drain any pending events from the netlink socket every time we poke another uevent file to
+// make sure we don't overrun the socket's buffer.
+//
+
+ListenerAction UeventListener::RegenerateUeventsForDir(DIR* d,
+                                                       const ListenerCallback& callback) const {
+    int dfd = dirfd(d);
+
+    int fd = openat(dfd, "uevent", O_WRONLY);
+    if (fd >= 0) {
+        write(fd, "add\n", 4);
+        close(fd);
+
+        Uevent uevent;
+        while (ReadUevent(&uevent)) {
+            if (callback(uevent) == ListenerAction::kStop) return ListenerAction::kStop;
+        }
+    }
+
+    dirent* de;
+    while ((de = readdir(d)) != nullptr) {
+        if (de->d_type != DT_DIR || de->d_name[0] == '.') continue;
+
+        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
+        if (fd < 0) continue;
+
+        std::unique_ptr<DIR, decltype(&closedir)> d2(fdopendir(fd), closedir);
+        if (d2 == 0) {
+            close(fd);
+        } else {
+            if (RegenerateUeventsForDir(d2.get(), callback) == ListenerAction::kStop) {
+                return ListenerAction::kStop;
+            }
+        }
+    }
+
+    // default is always to continue looking for uevents
+    return ListenerAction::kContinue;
+}
+
+ListenerAction UeventListener::RegenerateUeventsForPath(const std::string& path,
+                                                        const ListenerCallback& callback) const {
+    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
+    if (!d) return ListenerAction::kContinue;
+
+    return RegenerateUeventsForDir(d.get(), callback);
+}
+
+const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
+
+void UeventListener::RegenerateUevents(const ListenerCallback& callback) const {
+    for (const auto path : kRegenerationPaths) {
+        if (RegenerateUeventsForPath(path, callback) == ListenerAction::kStop) return;
+    }
+}
+
+void UeventListener::Poll(const ListenerCallback& callback,
+                          const std::optional<std::chrono::milliseconds> relative_timeout) const {
+    using namespace std::chrono;
+
+    pollfd ufd;
+    ufd.events = POLLIN;
+    ufd.fd = device_fd_;
+
+    auto start_time = steady_clock::now();
+
+    while (true) {
+        ufd.revents = 0;
+
+        int timeout_ms = -1;
+        if (relative_timeout) {
+            auto now = steady_clock::now();
+            auto time_elapsed = duration_cast<milliseconds>(now - start_time);
+            if (time_elapsed > *relative_timeout) return;
+
+            auto remaining_timeout = *relative_timeout - time_elapsed;
+            timeout_ms = remaining_timeout.count();
+        }
+
+        int nr = poll(&ufd, 1, timeout_ms);
+        if (nr == 0) return;
+        if (nr < 0) {
+            PLOG(ERROR) << "poll() of uevent socket failed, continuing";
+            continue;
+        }
+        if (ufd.revents & POLLIN) {
+            // We're non-blocking, so if we receive a poll event keep processing until
+            // we have exhausted all uevent messages.
+            Uevent uevent;
+            while (ReadUevent(&uevent)) {
+                if (callback(uevent) == ListenerAction::kStop) return;
+            }
+        }
+    }
+}
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
new file mode 100644
index 0000000..1964688
--- /dev/null
+++ b/init/uevent_listener.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UEVENT_LISTENER_H
+#define _INIT_UEVENT_LISTENER_H
+
+#include <dirent.h>
+
+#include <chrono>
+#include <functional>
+#include <optional>
+
+#include <android-base/unique_fd.h>
+
+#include "uevent.h"
+
+#define UEVENT_MSG_LEN 2048
+
+enum class ListenerAction {
+    kStop = 0,  // Stop regenerating uevents as we've handled the one(s) we're interested in.
+    kContinue,  // Continue regenerating uevents as we haven't seen the one(s) we're interested in.
+};
+
+using ListenerCallback = std::function<ListenerAction(const Uevent&)>;
+
+extern const char* kRegenerationPaths[3];
+
+class UeventListener {
+  public:
+    UeventListener();
+
+    void RegenerateUevents(const ListenerCallback& callback) const;
+    ListenerAction RegenerateUeventsForPath(const std::string& path,
+                                            const ListenerCallback& callback) const;
+    void Poll(const ListenerCallback& callback,
+              const std::optional<std::chrono::milliseconds> relative_timeout = {}) const;
+
+  private:
+    bool ReadUevent(Uevent* uevent) const;
+    ListenerAction RegenerateUeventsForDir(DIR* d, const ListenerCallback& callback) const;
+
+    android::base::unique_fd device_fd_;
+};
+
+#endif
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index f27be64..ff64e8e 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -14,55 +14,221 @@
  * limitations under the License.
  */
 
+#include "ueventd.h"
+
 #include <ctype.h>
 #include <fcntl.h>
-#include <grp.h>
-#include <poll.h>
-#include <pwd.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/wait.h>
 
-#include <sys/types.h>
+#include <set>
+#include <thread>
 
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <selinux/android.h>
 #include <selinux/selinux.h>
 
-#include "ueventd.h"
-#include "log.h"
-#include "util.h"
 #include "devices.h"
+#include "firmware_handler.h"
+#include "log.h"
+#include "uevent_listener.h"
 #include "ueventd_parser.h"
+#include "util.h"
 
-int ueventd_main(int argc, char **argv)
-{
-    /*
-     * init sets the umask to 077 for forked processes. We need to
-     * create files with exact permissions, without modification by
-     * the umask.
-     */
-    umask(000);
+// At a high level, ueventd listens for uevent messages generated by the kernel through a netlink
+// socket.  When ueventd receives such a message it handles it by taking appropriate actions,
+// which can typically be creating a device node in /dev, setting file permissions, setting selinux
+// labels, etc.
+// Ueventd also handles loading of firmware that the kernel requests, and creates symlinks for block
+// and character devices.
 
-    /* Prevent fire-and-forget children from becoming zombies.
-     * If we should need to wait() for some children in the future
-     * (as opposed to none right now), double-forking here instead
-     * of ignoring SIGCHLD may be the better solution.
-     */
-    signal(SIGCHLD, SIG_IGN);
+// When ueventd starts, it regenerates uevents for all currently registered devices by traversing
+// /sys and writing 'add' to each 'uevent' file that it finds.  This causes the kernel to generate
+// and resend uevent messages for all of the currently registered devices.  This is done, because
+// ueventd would not have been running when these devices were registered and therefore was unable
+// to receive their uevent messages and handle them appropriately.  This process is known as
+// 'cold boot'.
 
-    InitKernelLogging(argv);
+// 'init' currently waits synchronously on the cold boot process of ueventd before it continues
+// its boot process.  For this reason, cold boot should be as quick as possible.  One way to achieve
+// a speed up here is to parallelize the handling of ueventd messages, which consume the bulk of the
+// time during cold boot.
 
-    LOG(INFO) << "ueventd started!";
+// Handling of uevent messages has two unique properties:
+// 1) It can be done in isolation; it doesn't need to read or write any status once it is started.
+// 2) It uses setegid() and setfscreatecon() so either care (aka locking) must be taken to ensure
+//    that no file system operations are done while the uevent process has an abnormal egid or
+//    fscreatecon or this handling must happen in a separate process.
+// Given the above two properties, it is best to fork() subprocesses to handle the uevents.  This
+// reduces the overhead and complexity that would be required in a solution with threads and locks.
+// In testing, a racy multithreaded solution has the same performance as the fork() solution, so
+// there is no reason to deal with the complexity of the former.
 
-    selinux_callback cb;
-    cb.func_log = selinux_klog_callback;
-    selinux_set_callback(SELINUX_CB_LOG, cb);
+// One other important caveat during the boot process is the handling of SELinux restorecon.
+// Since many devices have child devices, calling selinux_android_restorecon() recursively for each
+// device when its uevent is handled, results in multiple restorecon operations being done on a
+// given file.  It is more efficient to simply do restorecon recursively on /sys during cold boot,
+// than to do restorecon on each device as its uevent is handled.  This only applies to cold boot;
+// once that has completed, restorecon is done for each device as its uevent is handled.
 
-    ueventd_parse_config_file("/ueventd.rc");
-    ueventd_parse_config_file("/vendor/ueventd.rc");
-    ueventd_parse_config_file("/odm/ueventd.rc");
+// With all of the above considered, the cold boot process has the below steps:
+// 1) ueventd regenerates uevents by doing the /sys traversal and listens to the netlink socket for
+//    the generated uevents.  It writes these uevents into a queue represented by a vector.
+//
+// 2) ueventd forks 'n' separate uevent handler subprocesses and has each of them to handle the
+//    uevents in the queue based on a starting offset (their process number) and a stride (the total
+//    number of processes).  Note that no IPC happens at this point and only const functions from
+//    DeviceHandler should be called from this context.
+//
+// 3) In parallel to the subprocesses handling the uevents, the main thread of ueventd calls
+//    selinux_android_restorecon() recursively on /sys/class, /sys/block, and /sys/devices.
+//
+// 4) Once the restorecon operation finishes, the main thread calls waitpid() to wait for all
+//    subprocess handlers to complete and exit.  Once this happens, it marks coldboot as having
+//    completed.
+//
+// At this point, ueventd is single threaded, poll()'s and then handles any future uevents.
+
+// Lastly, it should be noted that uevents that occur during the coldboot process are handled
+// without issue after the coldboot process completes.  This is because the uevent listener is
+// paused while the uevent handler and restorecon actions take place.  Once coldboot completes,
+// the uevent listener resumes in polling mode and will handle the uevents that occurred during
+// coldboot.
+
+class ColdBoot {
+  public:
+    ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler)
+        : uevent_listener_(uevent_listener),
+          device_handler_(device_handler),
+          num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}
+
+    void Run();
+
+  private:
+    void UeventHandlerMain(unsigned int process_num, unsigned int total_processes);
+    void RegenerateUevents();
+    void ForkSubProcesses();
+    void DoRestoreCon();
+    void WaitForSubProcesses();
+
+    UeventListener& uevent_listener_;
+    DeviceHandler& device_handler_;
+
+    unsigned int num_handler_subprocesses_;
+    std::vector<Uevent> uevent_queue_;
+
+    std::set<pid_t> subprocess_pids_;
+};
+
+void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
+    for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) {
+        auto& uevent = uevent_queue_[i];
+        device_handler_.HandleDeviceEvent(uevent);
+    }
+    _exit(EXIT_SUCCESS);
+}
+
+void ColdBoot::RegenerateUevents() {
+    uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
+        HandleFirmwareEvent(uevent);
+
+        uevent_queue_.emplace_back(std::move(uevent));
+        return ListenerAction::kContinue;
+    });
+}
+
+void ColdBoot::ForkSubProcesses() {
+    for (unsigned int i = 0; i < num_handler_subprocesses_; ++i) {
+        auto pid = fork();
+        if (pid < 0) {
+            PLOG(FATAL) << "fork() failed!";
+        }
+
+        if (pid == 0) {
+            UeventHandlerMain(i, num_handler_subprocesses_);
+        }
+
+        subprocess_pids_.emplace(pid);
+    }
+}
+
+void ColdBoot::DoRestoreCon() {
+    for (const char* path : kRegenerationPaths) {
+        selinux_android_restorecon(path, SELINUX_ANDROID_RESTORECON_RECURSE);
+    }
+    device_handler_.set_skip_restorecon(false);
+}
+
+void ColdBoot::WaitForSubProcesses() {
+    // Treat subprocesses that crash or get stuck the same as if ueventd itself has crashed or gets
+    // stuck.
+    //
+    // When a subprocess crashes, we fatally abort from ueventd.  init will restart ueventd when
+    // init reaps it, and the cold boot process will start again.  If this continues to fail, then
+    // since ueventd is marked as a critical service, init will reboot to recovery.
+    //
+    // When a subprocess gets stuck, keep ueventd spinning waiting for it.  init has a timeout for
+    // cold boot and will reboot to the bootloader if ueventd does not complete in time.
+    while (!subprocess_pids_.empty()) {
+        int status;
+        pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, 0));
+        if (pid == -1) {
+            PLOG(ERROR) << "waitpid() failed";
+            continue;
+        }
+
+        auto it = std::find(subprocess_pids_.begin(), subprocess_pids_.end(), pid);
+        if (it == subprocess_pids_.end()) continue;
+
+        if (WIFEXITED(status)) {
+            if (WEXITSTATUS(status) == EXIT_SUCCESS) {
+                subprocess_pids_.erase(it);
+            } else {
+                LOG(FATAL) << "subprocess exited with status " << WEXITSTATUS(status);
+            }
+        } else if (WIFSIGNALED(status)) {
+            LOG(FATAL) << "subprocess killed by signal " << WTERMSIG(status);
+        }
+    }
+}
+
+void ColdBoot::Run() {
+    Timer cold_boot_timer;
+
+    RegenerateUevents();
+
+    ForkSubProcesses();
+
+    DoRestoreCon();
+
+    WaitForSubProcesses();
+
+    close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+    LOG(INFO) << "Coldboot took " << cold_boot_timer;
+}
+
+DeviceHandler CreateDeviceHandler() {
+    Parser parser;
+
+    std::vector<Subsystem> subsystems;
+    parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>(&subsystems));
+
+    using namespace std::placeholders;
+    std::vector<SysfsPermissions> sysfs_permissions;
+    std::vector<Permissions> dev_permissions;
+    parser.AddSingleLineParser(
+        "/sys/", std::bind(ParsePermissionsLine, _1, _2, &sysfs_permissions, nullptr));
+    parser.AddSingleLineParser("/dev/",
+                               std::bind(ParsePermissionsLine, _1, _2, nullptr, &dev_permissions));
+
+    parser.ParseConfig("/ueventd.rc");
+    parser.ParseConfig("/vendor/ueventd.rc");
+    parser.ParseConfig("/odm/ueventd.rc");
 
     /*
      * keep the current product name base configuration so
@@ -72,95 +238,41 @@
      * device node entries (b/34968103)
      */
     std::string hardware = android::base::GetProperty("ro.hardware", "");
-    ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str());
+    parser.ParseConfig("/ueventd." + hardware + ".rc");
 
-    device_init();
-
-    pollfd ufd;
-    ufd.events = POLLIN;
-    ufd.fd = get_device_fd();
-
-    while (true) {
-        ufd.revents = 0;
-        int nr = poll(&ufd, 1, -1);
-        if (nr <= 0) {
-            continue;
-        }
-        if (ufd.revents & POLLIN) {
-            handle_device_fd();
-        }
-    }
-
-    return 0;
+    return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
+                         std::move(subsystems), true);
 }
 
-void set_device_permission(int nargs, char **args)
-{
-    char *name;
-    char *attr = 0;
-    mode_t perm;
-    uid_t uid;
-    gid_t gid;
-    int prefix = 0;
-    int wildcard = 0;
-    char *endptr;
+int ueventd_main(int argc, char** argv) {
+    /*
+     * init sets the umask to 077 for forked processes. We need to
+     * create files with exact permissions, without modification by
+     * the umask.
+     */
+    umask(000);
 
-    if (nargs == 0)
-        return;
+    InitKernelLogging(argv);
 
-    if (args[0][0] == '#')
-        return;
+    LOG(INFO) << "ueventd started!";
 
-    name = args[0];
+    selinux_callback cb;
+    cb.func_log = selinux_klog_callback;
+    selinux_set_callback(SELINUX_CB_LOG, cb);
 
-    if (!strncmp(name,"/sys/", 5) && (nargs == 5)) {
-        LOG(INFO) << "/sys/ rule " << args[0] << " " << args[1];
-        attr = args[1];
-        args++;
-        nargs--;
+    DeviceHandler device_handler = CreateDeviceHandler();
+    UeventListener uevent_listener;
+
+    if (access(COLDBOOT_DONE, F_OK) != 0) {
+        ColdBoot cold_boot(uevent_listener, device_handler);
+        cold_boot.Run();
     }
 
-    if (nargs != 4) {
-        LOG(ERROR) << "invalid line ueventd.rc line for '" << args[0] << "'";
-        return;
-    }
+    uevent_listener.Poll([&device_handler](const Uevent& uevent) {
+        HandleFirmwareEvent(uevent);
+        device_handler.HandleDeviceEvent(uevent);
+        return ListenerAction::kContinue;
+    });
 
-    int len = strlen(name);
-    char *wildcard_chr = strchr(name, '*');
-    if ((name[len - 1] == '*') && (wildcard_chr == (name + len - 1))) {
-        prefix = 1;
-        name[len - 1] = '\0';
-    } else if (wildcard_chr) {
-        wildcard = 1;
-    }
-
-    perm = strtol(args[1], &endptr, 8);
-    if (!endptr || *endptr != '\0') {
-        LOG(ERROR) << "invalid mode '" << args[1] << "'";
-        return;
-    }
-
-    struct passwd* pwd = getpwnam(args[2]);
-    if (!pwd) {
-        LOG(ERROR) << "invalid uid '" << args[2] << "'";
-        return;
-    }
-    uid = pwd->pw_uid;
-
-    struct group* grp = getgrnam(args[3]);
-    if (!grp) {
-        LOG(ERROR) << "invalid gid '" << args[3] << "'";
-        return;
-    }
-    gid = grp->gr_gid;
-
-    if (add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard) != 0) {
-        PLOG(ERROR) << "add_dev_perms(name=" << name <<
-                       ", attr=" << attr <<
-                       ", perm=" << std::oct << perm << std::dec <<
-                       ", uid=" << uid << ", gid=" << gid <<
-                       ", prefix=" << prefix << ", wildcard=" << wildcard <<
-                       ")";
-        return;
-    }
+    return 0;
 }
diff --git a/init/ueventd.h b/init/ueventd.h
index d12d7fe..1f424d3 100644
--- a/init/ueventd.h
+++ b/init/ueventd.h
@@ -17,23 +17,6 @@
 #ifndef _INIT_UEVENTD_H_
 #define _INIT_UEVENTD_H_
 
-#include <cutils/list.h>
-#include <sys/types.h>
-
-enum devname_src_t {
-    DEVNAME_UNKNOWN = 0,
-    DEVNAME_UEVENT_DEVNAME,
-    DEVNAME_UEVENT_DEVPATH,
-};
-
-struct ueventd_subsystem {
-    struct listnode slist;
-
-    const char *name;
-    const char *dirname;
-    devname_src_t devname_src;
-};
-
-int ueventd_main(int argc, char **argv);
+int ueventd_main(int argc, char** argv);
 
 #endif
diff --git a/init/ueventd_keywords.h b/init/ueventd_keywords.h
deleted file mode 100644
index 88e8f01..0000000
--- a/init/ueventd_keywords.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef KEYWORD
-#define __MAKE_KEYWORD_ENUM__
-#define KEYWORD(symbol, flags, nargs) K_##symbol,
-enum {
-    K_UNKNOWN,
-#endif
-    KEYWORD(subsystem,      SECTION,    1)
-    KEYWORD(devname,        OPTION,     1)
-    KEYWORD(dirname,        OPTION,     1)
-#ifdef __MAKE_KEYWORD_ENUM__
-    KEYWORD_COUNT,
-};
-#undef __MAKE_KEYWORD_ENUM__
-#undef KEYWORD
-#endif
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index baff58c..7156e76 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,228 +14,132 @@
  * limitations under the License.
  */
 
-#include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "ueventd.h"
 #include "ueventd_parser.h"
-#include "parser.h"
-#include "log.h"
-#include "util.h"
 
-static list_declare(subsystem_list);
+#include <grp.h>
+#include <pwd.h>
 
-static void parse_line_device(struct parse_state *state, int nargs, char **args);
+#include "keyword_map.h"
 
-#define SECTION 0x01
-#define OPTION  0x02
-
-#include "ueventd_keywords.h"
-
-#define KEYWORD(symbol, flags, nargs) \
-    [ K_##symbol ] = { #symbol, (nargs) + 1, flags, },
-
-static struct {
-    const char *name;
-    unsigned char nargs;
-    unsigned char flags;
-} keyword_info[KEYWORD_COUNT] = {
-    [ K_UNKNOWN ] = { "unknown", 0, 0 },
-#include "ueventd_keywords.h"
-};
-#undef KEYWORD
-
-#define kw_is(kw, type) (keyword_info[kw].flags & (type))
-#define kw_nargs(kw) (keyword_info[kw].nargs)
-
-static int lookup_keyword(const char *s)
-{
-    switch (*s++) {
-    case 'd':
-        if (!strcmp(s, "evname")) return K_devname;
-        if (!strcmp(s, "irname")) return K_dirname;
-        break;
-    case 's':
-        if (!strcmp(s, "ubsystem")) return K_subsystem;
-        break;
-    }
-    return K_UNKNOWN;
-}
-
-static void parse_line_no_op(struct parse_state*, int, char**) {
-}
-
-static int valid_name(const char *name)
-{
-    while (*name) {
-        if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
-            return 0;
-        }
-        name++;
-    }
-    return 1;
-}
-
-struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name)
-{
-    struct listnode *node;
-    struct ueventd_subsystem *s;
-
-    list_for_each(node, &subsystem_list) {
-        s = node_to_item(node, struct ueventd_subsystem, slist);
-        if (!strcmp(s->name, name)) {
-            return s;
-        }
-    }
-    return 0;
-}
-
-static void *parse_subsystem(parse_state* state, int /*nargs*/, char** args) {
-    if (!valid_name(args[1])) {
-        parse_error(state, "invalid subsystem name '%s'\n", args[1]);
-        return 0;
+bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
+                          std::vector<SysfsPermissions>* out_sysfs_permissions,
+                          std::vector<Permissions>* out_dev_permissions) {
+    bool is_sysfs = out_sysfs_permissions != nullptr;
+    if (is_sysfs && args.size() != 5) {
+        *err = "/sys/ lines must have 5 entries";
+        return false;
     }
 
-    ueventd_subsystem* s = ueventd_subsystem_find_by_name(args[1]);
-    if (s) {
-        parse_error(state, "ignored duplicate definition of subsystem '%s'\n",
-                args[1]);
-        return 0;
+    if (!is_sysfs && args.size() != 4) {
+        *err = "/dev/ lines must have 4 entries";
+        return false;
     }
 
-    s = (ueventd_subsystem*) calloc(1, sizeof(*s));
-    if (!s) {
-        parse_error(state, "out of memory\n");
-        return 0;
-    }
-    s->name = args[1];
-    s->dirname = "/dev";
-    list_add_tail(&subsystem_list, &s->slist);
-    return s;
-}
+    auto it = args.begin();
+    const std::string& name = *it++;
 
-static void parse_line_subsystem(struct parse_state *state, int nargs,
-        char **args)
-{
-    struct ueventd_subsystem *s = (ueventd_subsystem*) state->context;
-    int kw;
+    std::string sysfs_attribute;
+    if (is_sysfs) sysfs_attribute = *it++;
 
-    if (nargs == 0) {
-        return;
+    // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>
+    std::string& perm_string = *it++;
+    char* end_pointer = 0;
+    mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
+    if (end_pointer == nullptr || *end_pointer != '\0') {
+        *err = "invalid mode '" + perm_string + "'";
+        return false;
     }
 
-    kw = lookup_keyword(args[0]);
-    switch (kw) {
-    case K_devname:
-        if (!strcmp(args[1], "uevent_devname"))
-            s->devname_src = DEVNAME_UEVENT_DEVNAME;
-        else if (!strcmp(args[1], "uevent_devpath"))
-            s->devname_src = DEVNAME_UEVENT_DEVPATH;
-        else
-            parse_error(state, "invalid devname '%s'\n", args[1]);
-        break;
-
-    case K_dirname:
-        if (args[1][0] == '/')
-            s->dirname = args[1];
-        else
-            parse_error(state, "dirname '%s' does not start with '/'\n",
-                    args[1]);
-        break;
-
-    default:
-        parse_error(state, "invalid option '%s'\n", args[0]);
+    std::string& uid_string = *it++;
+    passwd* pwd = getpwnam(uid_string.c_str());
+    if (!pwd) {
+        *err = "invalid uid '" + uid_string + "'";
+        return false;
     }
-}
+    uid_t uid = pwd->pw_uid;
 
-static void parse_new_section(struct parse_state *state, int kw,
-                       int nargs, char **args)
-{
-    printf("[ %s %s ]\n", args[0],
-           nargs > 1 ? args[1] : "");
-
-    switch(kw) {
-    case K_subsystem:
-        state->context = parse_subsystem(state, nargs, args);
-        if (state->context) {
-            state->parse_line = parse_line_subsystem;
-            return;
-        }
-        break;
+    std::string& gid_string = *it++;
+    struct group* grp = getgrnam(gid_string.c_str());
+    if (!grp) {
+        *err = "invalid gid '" + gid_string + "'";
+        return false;
     }
-    state->parse_line = parse_line_no_op;
-}
+    gid_t gid = grp->gr_gid;
 
-static void parse_line(struct parse_state *state, char **args, int nargs)
-{
-    int kw = lookup_keyword(args[0]);
-    int kw_nargs = kw_nargs(kw);
-
-    if (nargs < kw_nargs) {
-        parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
-            kw_nargs > 2 ? "arguments" : "argument");
-        return;
-    }
-
-    if (kw_is(kw, SECTION)) {
-        parse_new_section(state, kw, nargs, args);
-    } else if (kw_is(kw, OPTION)) {
-        state->parse_line(state, nargs, args);
+    if (is_sysfs) {
+        out_sysfs_permissions->emplace_back(name, sysfs_attribute, perm, uid, gid);
     } else {
-        parse_line_device(state, nargs, args);
+        out_dev_permissions->emplace_back(name, perm, uid, gid);
     }
+    return true;
 }
 
-static void parse_config(const char *fn, const std::string& data)
-{
-    char *args[UEVENTD_PARSER_MAXARGS];
+bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                   int line, std::string* err) {
+    if (args.size() != 2) {
+        *err = "subsystems must have exactly one name";
+        return false;
+    }
 
-    int nargs = 0;
-    parse_state state;
-    state.filename = fn;
-    state.line = 1;
-    state.ptr = strdup(data.c_str());  // TODO: fix this code!
-    state.nexttoken = 0;
-    state.parse_line = parse_line_no_op;
-    for (;;) {
-        int token = next_token(&state);
-        switch (token) {
-        case T_EOF:
-            parse_line(&state, args, nargs);
-            return;
-        case T_NEWLINE:
-            if (nargs) {
-                parse_line(&state, args, nargs);
-                nargs = 0;
-            }
-            state.line++;
-            break;
-        case T_TEXT:
-            if (nargs < UEVENTD_PARSER_MAXARGS) {
-                args[nargs++] = state.text;
-            }
-            break;
+    if (std::find(subsystems_->begin(), subsystems_->end(), args[1]) != subsystems_->end()) {
+        *err = "ignoring duplicate subsystem entry";
+        return false;
+    }
+
+    subsystem_.name_ = args[1];
+
+    return true;
+}
+
+bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
+    if (args[1] == "uevent_devname") {
+        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
+        return true;
+    }
+    if (args[1] == "uevent_devpath") {
+        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
+        return true;
+    }
+
+    *err = "invalid devname '" + args[1] + "'";
+    return false;
+}
+
+bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
+    if (args[1].front() != '/') {
+        *err = "dirname '" + args[1] + " ' does not start with '/'";
+        return false;
+    }
+
+    subsystem_.dir_name_ = args[1];
+    return true;
+}
+
+bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+    using OptionParser =
+        bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
+    static class OptionParserMap : public KeywordMap<OptionParser> {
+      private:
+        const Map& map() const override {
+            // clang-format off
+            static const Map option_parsers = {
+                {"devname",     {1,     1,      &SubsystemParser::ParseDevName}},
+                {"dirname",     {1,     1,      &SubsystemParser::ParseDirName}},
+            };
+            // clang-format on
+            return option_parsers;
         }
-    }
-}
+    } parser_map;
 
-int ueventd_parse_config_file(const char *fn)
-{
-    std::string data;
-    if (!read_file(fn, &data)) {
-        return -1;
+    auto parser = parser_map.FindFunction(args, err);
+
+    if (!parser) {
+        return false;
     }
 
-    data.push_back('\n'); // TODO: fix parse_config.
-    parse_config(fn, data);
-    return 0;
+    return (this->*parser)(std::move(args), err);
 }
 
-static void parse_line_device(parse_state*, int nargs, char** args) {
-    set_device_permission(nargs, args);
+void SubsystemParser::EndSection() {
+    subsystems_->emplace_back(std::move(subsystem_));
 }
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 907cc49..c1ce976 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2007 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,33 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_UEVENTD_PARSER_H_
-#define _INIT_UEVENTD_PARSER_H_
+#ifndef _INIT_UEVENTD_PARSER_H
+#define _INIT_UEVENTD_PARSER_H
 
-#include "ueventd.h"
+#include <string>
+#include <vector>
 
-#define UEVENTD_PARSER_MAXARGS 5
+#include "devices.h"
+#include "init_parser.h"
 
-int ueventd_parse_config_file(const char *fn);
-void set_device_permission(int nargs, char **args);
-struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name);
+class SubsystemParser : public SectionParser {
+  public:
+    SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
+    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
+                      std::string* err) override;
+    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+    void EndSection() override;
+
+  private:
+    bool ParseDevName(std::vector<std::string>&& args, std::string* err);
+    bool ParseDirName(std::vector<std::string>&& args, std::string* err);
+
+    Subsystem subsystem_;
+    std::vector<Subsystem>* subsystems_;
+};
+
+bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
+                          std::vector<SysfsPermissions>* out_sysfs_permissions,
+                          std::vector<Permissions>* out_dev_permissions);
 
 #endif
diff --git a/init/util.cpp b/init/util.cpp
index 4e3537b..dfdfeb4 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -14,26 +14,21 @@
  * limitations under the License.
  */
 
+#include "util.h"
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <ftw.h>
 #include <pwd.h>
 #include <stdarg.h>
-#include <stdlib.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 #include <time.h>
 #include <unistd.h>
 
-#include <selinux/android.h>
-#include <selinux/label.h>
-
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-
 #include <thread>
 
 #include <android-base/file.h>
@@ -42,59 +37,54 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-
 #include <cutils/android_reboot.h>
-/* for ANDROID_SOCKET_* */
 #include <cutils/sockets.h>
+#include <selinux/android.h>
 
-#include "init.h"
-#include "log.h"
 #include "reboot.h"
-#include "util.h"
 
-static unsigned int do_decode_uid(const char *s)
-{
-    unsigned int v;
+#ifdef _INIT_INIT_H
+#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#endif
 
-    if (!s || *s == '\0')
-        return UINT_MAX;
+using android::base::boot_clock;
+using namespace std::literals::string_literals;
 
-    if (isalpha(s[0])) {
-        struct passwd* pwd = getpwnam(s);
-        if (!pwd)
-            return UINT_MAX;
-        return pwd->pw_uid;
+// DecodeUid() - decodes and returns the given string, which can be either the
+// numeric or name representation, into the integer uid or gid. Returns
+// UINT_MAX on error.
+bool DecodeUid(const std::string& name, uid_t* uid, std::string* err) {
+    *uid = UINT_MAX;
+    *err = "";
+
+    if (isalpha(name[0])) {
+        passwd* pwd = getpwnam(name.c_str());
+        if (!pwd) {
+            *err = "getpwnam failed: "s + strerror(errno);
+            return false;
+        }
+        *uid = pwd->pw_uid;
+        return true;
     }
 
     errno = 0;
-    v = (unsigned int) strtoul(s, 0, 0);
-    if (errno)
-        return UINT_MAX;
-    return v;
-}
-
-/*
- * decode_uid - decodes and returns the given string, which can be either the
- * numeric or name representation, into the integer uid or gid. Returns
- * UINT_MAX on error.
- */
-unsigned int decode_uid(const char *s) {
-    unsigned int v = do_decode_uid(s);
-    if (v == UINT_MAX) {
-        LOG(ERROR) << "decode_uid: Unable to find UID for '" << s << "'; returning UINT_MAX";
+    uid_t result = static_cast<uid_t>(strtoul(name.c_str(), 0, 0));
+    if (errno) {
+        *err = "strtoul failed: "s + strerror(errno);
+        return false;
     }
-    return v;
+    *uid = result;
+    return true;
 }
 
 /*
- * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR
+ * CreateSocket - creates a Unix domain socket in ANDROID_SOCKET_DIR
  * ("/dev/socket") as dictated in init.rc. This socket is inherited by the
  * daemon. We communicate the file descriptor's value via the environment
  * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
  */
-int create_socket(const char *name, int type, mode_t perm, uid_t uid,
-                  gid_t gid, const char *socketcon)
-{
+int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
+                 const char* socketcon, selabel_handle* sehandle) {
     if (socketcon) {
         if (setsockcreatecon(socketcon) == -1) {
             PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
@@ -128,6 +118,14 @@
         }
     }
 
+    if (passcred) {
+        int on = 1;
+        if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
+            PLOG(ERROR) << "Failed to set SO_PASSCRED '" << name << "'";
+            return -1;
+        }
+    }
+
     int ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
     int savederrno = errno;
 
@@ -161,12 +159,14 @@
     return -1;
 }
 
-bool read_file(const std::string& path, std::string* content) {
+bool ReadFile(const std::string& path, std::string* content, std::string* err) {
     content->clear();
+    *err = "";
 
     android::base::unique_fd fd(
         TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
     if (fd == -1) {
+        *err = "Unable to open '" + path + "': " + strerror(errno);
         return false;
     }
 
@@ -174,93 +174,52 @@
     // or group-writable files.
     struct stat sb;
     if (fstat(fd, &sb) == -1) {
-        PLOG(ERROR) << "fstat failed for '" << path << "'";
+        *err = "fstat failed for '" + path + "': " + strerror(errno);
         return false;
     }
     if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
-        LOG(ERROR) << "skipping insecure file '" << path << "'";
+        *err = "Skipping insecure file '" + path + "'";
         return false;
     }
 
-    return android::base::ReadFdToString(fd, content);
+    if (!android::base::ReadFdToString(fd, content)) {
+        *err = "Unable to read '" + path + "': " + strerror(errno);
+        return false;
+    }
+    return true;
 }
 
-bool write_file(const std::string& path, const std::string& content) {
+bool WriteFile(const std::string& path, const std::string& content, std::string* err) {
+    *err = "";
+
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(
         open(path.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
     if (fd == -1) {
-        PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
+        *err = "Unable to open '" + path + "': " + strerror(errno);
         return false;
     }
-    bool success = android::base::WriteStringToFd(content, fd);
-    if (!success) {
-        PLOG(ERROR) << "write_file: Unable to write to '" << path << "'";
+    if (!android::base::WriteStringToFd(content, fd)) {
+        *err = "Unable to write to '" + path + "': " + strerror(errno);
+        return false;
     }
-    return success;
+    return true;
 }
 
-boot_clock::time_point boot_clock::now() {
-  timespec ts;
-  clock_gettime(CLOCK_BOOTTIME, &ts);
-  return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
-                                std::chrono::nanoseconds(ts.tv_nsec));
-}
-
-int mkdir_recursive(const char *pathname, mode_t mode)
-{
-    char buf[128];
-    const char *slash;
-    const char *p = pathname;
-    int width;
-    int ret;
-    struct stat info;
-
-    while ((slash = strchr(p, '/')) != NULL) {
-        width = slash - pathname;
-        p = slash + 1;
-        if (width < 0)
-            break;
-        if (width == 0)
-            continue;
-        if ((unsigned int)width > sizeof(buf) - 1) {
-            LOG(ERROR) << "path too long for mkdir_recursive";
-            return -1;
-        }
-        memcpy(buf, pathname, width);
-        buf[width] = 0;
-        if (stat(buf, &info) != 0) {
-            ret = make_dir(buf, mode);
-            if (ret && errno != EEXIST)
-                return ret;
+int mkdir_recursive(const std::string& path, mode_t mode, selabel_handle* sehandle) {
+    std::string::size_type slash = 0;
+    while ((slash = path.find('/', slash + 1)) != std::string::npos) {
+        auto directory = path.substr(0, slash);
+        struct stat info;
+        if (stat(directory.c_str(), &info) != 0) {
+            auto ret = make_dir(directory.c_str(), mode, sehandle);
+            if (ret && errno != EEXIST) return ret;
         }
     }
-    ret = make_dir(pathname, mode);
-    if (ret && errno != EEXIST)
-        return ret;
+    auto ret = make_dir(path.c_str(), mode, sehandle);
+    if (ret && errno != EEXIST) return ret;
     return 0;
 }
 
-/*
- * replaces any unacceptable characters with '_', the
- * length of the resulting string is equal to the input string
- */
-void sanitize(char *s)
-{
-    const char* accept =
-            "abcdefghijklmnopqrstuvwxyz"
-            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-            "0123456789"
-            "_-.";
-
-    if (!s)
-        return;
-
-    while (*s) {
-        s += strspn(s, accept);
-        if (*s) *s++ = '_';
-    }
-}
-
 int wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {
     boot_clock::time_point timeout_time = boot_clock::now() + timeout;
     while (boot_clock::now() < timeout_time) {
@@ -285,8 +244,7 @@
     }
 }
 
-int make_dir(const char *path, mode_t mode)
-{
+int make_dir(const char* path, mode_t mode, selabel_handle* sehandle) {
     int rc;
 
     char *secontext = NULL;
@@ -308,11 +266,6 @@
     return rc;
 }
 
-int restorecon(const char* pathname, int flags)
-{
-    return selinux_android_restorecon(pathname, flags);
-}
-
 /*
  * Writes hex_len hex characters (1/2 byte) to hex from bytes.
  */
@@ -414,6 +367,7 @@
 
 void panic() {
     LOG(ERROR) << "panic: rebooting to bootloader";
+    // Do not queue "shutdown" trigger since we want to shutdown immediately
     DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
 }
 
diff --git a/init/util.h b/init/util.h
index bb281bd..1ad6b77 100644
--- a/init/util.h
+++ b/init/util.h
@@ -25,57 +25,49 @@
 #include <ostream>
 #include <string>
 
+#include <android-base/chrono_utils.h>
+#include <selinux/label.h>
+
 #define COLDBOOT_DONE "/dev/.coldboot_done"
 
 const std::string kAndroidDtDir("/proc/device-tree/firmware/android/");
 
+using android::base::boot_clock;
 using namespace std::chrono_literals;
 
-int create_socket(const char *name, int type, mode_t perm,
-                  uid_t uid, gid_t gid, const char *socketcon);
+int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
+                 const char* socketcon, selabel_handle* sehandle);
 
-bool read_file(const std::string& path, std::string* content);
-bool write_file(const std::string& path, const std::string& content);
-
-// A std::chrono clock based on CLOCK_BOOTTIME.
-class boot_clock {
- public:
-  typedef std::chrono::nanoseconds duration;
-  typedef std::chrono::time_point<boot_clock, duration> time_point;
-  static constexpr bool is_steady = true;
-
-  static time_point now();
-};
+bool ReadFile(const std::string& path, std::string* content, std::string* err);
+bool WriteFile(const std::string& path, const std::string& content, std::string* err);
 
 class Timer {
- public:
-  Timer() : start_(boot_clock::now()) {
-  }
+  public:
+    Timer() : start_(boot_clock::now()) {}
 
-  double duration_s() const {
-    typedef std::chrono::duration<double> double_duration;
-    return std::chrono::duration_cast<double_duration>(boot_clock::now() - start_).count();
-  }
+    double duration_s() const {
+        typedef std::chrono::duration<double> double_duration;
+        return std::chrono::duration_cast<double_duration>(boot_clock::now() - start_).count();
+    }
 
-  int64_t duration_ms() const {
-    return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_).count();
-  }
+    int64_t duration_ms() const {
+        return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_)
+            .count();
+    }
 
- private:
-  boot_clock::time_point start_;
+  private:
+    android::base::boot_clock::time_point start_;
 };
 
 std::ostream& operator<<(std::ostream& os, const Timer& t);
 
-unsigned int decode_uid(const char *s);
+bool DecodeUid(const std::string& name, uid_t* uid, std::string* err);
 
-int mkdir_recursive(const char *pathname, mode_t mode);
-void sanitize(char *p);
+int mkdir_recursive(const std::string& pathname, mode_t mode, selabel_handle* sehandle);
 int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
 void import_kernel_cmdline(bool in_qemu,
                            const std::function<void(const std::string&, const std::string&, bool)>&);
-int make_dir(const char *path, mode_t mode);
-int restorecon(const char *pathname, int flags = 0);
+int make_dir(const char* path, mode_t mode, selabel_handle* sehandle);
 std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
 bool is_dir(const char* pathname);
 bool expand_props(const std::string& src, std::string* dst);
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 0c0350a..4bb8a83 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -24,53 +24,67 @@
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
-TEST(util, read_file_ENOENT) {
-  std::string s("hello");
-  errno = 0;
-  EXPECT_FALSE(read_file("/proc/does-not-exist", &s));
-  EXPECT_EQ(ENOENT, errno);
-  EXPECT_EQ("", s); // s was cleared.
+using namespace std::literals::string_literals;
+
+TEST(util, ReadFile_ENOENT) {
+    std::string s("hello");
+    std::string err;
+    errno = 0;
+    EXPECT_FALSE(ReadFile("/proc/does-not-exist", &s, &err));
+    EXPECT_EQ("Unable to open '/proc/does-not-exist': No such file or directory", err);
+    EXPECT_EQ(ENOENT, errno);
+    EXPECT_EQ("", s);  // s was cleared.
 }
 
-TEST(util, read_file_group_writeable) {
+TEST(util, ReadFileGroupWriteable) {
     std::string s("hello");
     TemporaryFile tf;
+    std::string err;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(write_file(tf.path, s)) << strerror(errno);
+    EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
+    EXPECT_EQ("", err);
     EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
-    EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
+    EXPECT_FALSE(ReadFile(tf.path, &s, &err)) << strerror(errno);
+    EXPECT_EQ("Skipping insecure file '"s + tf.path + "'", err);
     EXPECT_EQ("", s);  // s was cleared.
 }
 
-TEST(util, read_file_world_writeable) {
+TEST(util, ReadFileWorldWiteable) {
     std::string s("hello");
     TemporaryFile tf;
+    std::string err;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(write_file(tf.path, s.c_str())) << strerror(errno);
+    EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
+    EXPECT_EQ("", err);
     EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
-    EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
+    EXPECT_FALSE(ReadFile(tf.path, &s, &err)) << strerror(errno);
+    EXPECT_EQ("Skipping insecure file '"s + tf.path + "'", err);
     EXPECT_EQ("", s);  // s was cleared.
 }
 
-TEST(util, read_file_symbolic_link) {
+TEST(util, ReadFileSymbolicLink) {
     std::string s("hello");
     errno = 0;
     // lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
-    EXPECT_FALSE(read_file("/charger", &s));
+    std::string err;
+    EXPECT_FALSE(ReadFile("/charger", &s, &err));
+    EXPECT_EQ("Unable to open '/charger': Too many symbolic links encountered", err);
     EXPECT_EQ(ELOOP, errno);
     EXPECT_EQ("", s);  // s was cleared.
 }
 
-TEST(util, read_file_success) {
-  std::string s("hello");
-  EXPECT_TRUE(read_file("/proc/version", &s));
-  EXPECT_GT(s.length(), 6U);
-  EXPECT_EQ('\n', s[s.length() - 1]);
-  s[5] = 0;
-  EXPECT_STREQ("Linux", s.c_str());
+TEST(util, ReadFileSuccess) {
+    std::string s("hello");
+    std::string err;
+    EXPECT_TRUE(ReadFile("/proc/version", &s, &err));
+    EXPECT_EQ("", err);
+    EXPECT_GT(s.length(), 6U);
+    EXPECT_EQ('\n', s[s.length() - 1]);
+    s[5] = 0;
+    EXPECT_STREQ("Linux", s.c_str());
 }
 
-TEST(util, write_file_binary) {
+TEST(util, WriteFileBinary) {
     std::string contents("abcd");
     contents.push_back('\0');
     contents.push_back('\0');
@@ -78,22 +92,28 @@
     ASSERT_EQ(10u, contents.size());
 
     TemporaryFile tf;
+    std::string err;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(write_file(tf.path, contents)) << strerror(errno);
+    EXPECT_TRUE(WriteFile(tf.path, contents, &err)) << strerror(errno);
+    EXPECT_EQ("", err);
 
     std::string read_back_contents;
-    EXPECT_TRUE(read_file(tf.path, &read_back_contents)) << strerror(errno);
+    EXPECT_TRUE(ReadFile(tf.path, &read_back_contents, &err)) << strerror(errno);
+    EXPECT_EQ("", err);
     EXPECT_EQ(contents, read_back_contents);
     EXPECT_EQ(10u, read_back_contents.size());
 }
 
-TEST(util, write_file_not_exist) {
+TEST(util, WriteFileNotExist) {
     std::string s("hello");
     std::string s2("hello");
     TemporaryDir test_dir;
     std::string path = android::base::StringPrintf("%s/does-not-exist", test_dir.path);
-    EXPECT_TRUE(write_file(path, s));
-    EXPECT_TRUE(read_file(path, &s2));
+    std::string err;
+    EXPECT_TRUE(WriteFile(path, s, &err));
+    EXPECT_EQ("", err);
+    EXPECT_TRUE(ReadFile(path, &s2, &err));
+    EXPECT_EQ("", err);
     EXPECT_EQ(s, s2);
     struct stat sb;
     int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
@@ -103,20 +123,67 @@
     EXPECT_EQ(0, unlink(path.c_str()));
 }
 
-TEST(util, write_file_exist) {
+TEST(util, WriteFileExist) {
     std::string s2("");
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(write_file(tf.path, "1hello1")) << strerror(errno);
-    EXPECT_TRUE(read_file(tf.path, &s2));
+    std::string err;
+    EXPECT_TRUE(WriteFile(tf.path, "1hello1", &err)) << strerror(errno);
+    EXPECT_EQ("", err);
+    EXPECT_TRUE(ReadFile(tf.path, &s2, &err));
+    EXPECT_EQ("", err);
     EXPECT_STREQ("1hello1", s2.c_str());
-    EXPECT_TRUE(write_file(tf.path, "2ll2"));
-    EXPECT_TRUE(read_file(tf.path, &s2));
+    EXPECT_TRUE(WriteFile(tf.path, "2ll2", &err));
+    EXPECT_EQ("", err);
+    EXPECT_TRUE(ReadFile(tf.path, &s2, &err));
+    EXPECT_EQ("", err);
     EXPECT_STREQ("2ll2", s2.c_str());
 }
 
-TEST(util, decode_uid) {
-  EXPECT_EQ(0U, decode_uid("root"));
-  EXPECT_EQ(UINT_MAX, decode_uid("toot"));
-  EXPECT_EQ(123U, decode_uid("123"));
+TEST(util, DecodeUid) {
+    uid_t decoded_uid;
+    std::string err;
+
+    EXPECT_TRUE(DecodeUid("root", &decoded_uid, &err));
+    EXPECT_EQ("", err);
+    EXPECT_EQ(0U, decoded_uid);
+
+    EXPECT_FALSE(DecodeUid("toot", &decoded_uid, &err));
+    EXPECT_EQ("getpwnam failed: No such file or directory", err);
+    EXPECT_EQ(UINT_MAX, decoded_uid);
+
+    EXPECT_TRUE(DecodeUid("123", &decoded_uid, &err));
+    EXPECT_EQ("", err);
+    EXPECT_EQ(123U, decoded_uid);
+}
+
+TEST(util, is_dir) {
+    TemporaryDir test_dir;
+    EXPECT_TRUE(is_dir(test_dir.path));
+    TemporaryFile tf;
+    EXPECT_FALSE(is_dir(tf.path));
+}
+
+TEST(util, mkdir_recursive) {
+    TemporaryDir test_dir;
+    std::string path = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
+    EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
+    std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
+    EXPECT_TRUE(is_dir(path1.c_str()));
+    std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
+    EXPECT_TRUE(is_dir(path1.c_str()));
+    std::string path3 = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
+    EXPECT_TRUE(is_dir(path1.c_str()));
+}
+
+TEST(util, mkdir_recursive_extra_slashes) {
+    TemporaryDir test_dir;
+    std::string path = android::base::StringPrintf("%s/three////directories/deep//", test_dir.path);
+    EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
+    std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
+    EXPECT_TRUE(is_dir(path1.c_str()));
+    std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
+    EXPECT_TRUE(is_dir(path1.c_str()));
+    std::string path3 = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
+    EXPECT_TRUE(is_dir(path1.c_str()));
 }
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
index b196147..7baa487 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -16,14 +16,18 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/watchdog.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#include <linux/watchdog.h>
+#include <android-base/logging.h>
 
 #include "log.h"
-#include "util.h"
+
+#ifdef _INIT_INIT_H
+#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#endif
 
 #define DEV_NAME "/dev/watchdog"
 
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
index 0d57814..b0ac5c4 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -25,6 +25,7 @@
 
 cc_test {
     name: "libappfuse_test",
+    test_suites: ["device-tests"],
     defaults: ["libappfuse_defaults"],
     shared_libs: ["libappfuse"],
     srcs: [
diff --git a/libappfuse/AndroidTest.xml b/libappfuse/AndroidTest.xml
new file mode 100644
index 0000000..a9cd754
--- /dev/null
+++ b/libappfuse/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for libappfuse_test">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="libappfuse_test->/data/local/tmp/libappfuse_test" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="libappfuse_test" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
index 3f47066..0792307 100644
--- a/libappfuse/FuseBridgeLoop.cc
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -57,6 +57,12 @@
             return;
     }
 }
+
+void LogResponseError(const std::string& message, const FuseResponse& response) {
+    LOG(ERROR) << message << ": header.len=" << response.header.len
+               << " header.error=" << response.header.error
+               << " header.unique=" << response.header.unique;
+}
 }
 
 class FuseBridgeEntry {
@@ -135,6 +141,7 @@
         }
 
         if (!buffer_.response.Write(device_fd_)) {
+            LogResponseError("Failed to write a reply from proxy to device", buffer_.response);
             return FuseBridgeState::kClosing;
         }
 
@@ -200,6 +207,7 @@
         }
 
         if (!buffer_.response.Write(device_fd_)) {
+            LogResponseError("Failed to write a response to device", buffer_.response);
             return FuseBridgeState::kClosing;
         }
 
@@ -215,6 +223,11 @@
             case ResultOrAgain::kSuccess:
                 return FuseBridgeState::kWaitToReadEither;
             case ResultOrAgain::kFailure:
+                LOG(ERROR) << "Failed to write a request to proxy:"
+                           << " header.len=" << buffer_.request.header.len
+                           << " header.opcode=" << buffer_.request.header.opcode
+                           << " header.unique=" << buffer_.request.header.unique
+                           << " header.nodeid=" << buffer_.request.header.nodeid;
                 return FuseBridgeState::kClosing;
             case ResultOrAgain::kAgain:
                 return FuseBridgeState::kWaitToWriteProxy;
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
index 653e96b..1eab46c 100644
--- a/libappfuse/FuseBuffer.cc
+++ b/libappfuse/FuseBuffer.cc
@@ -115,7 +115,10 @@
                 case EAGAIN:
                     return ResultOrAgain::kAgain;
                 default:
-                    PLOG(ERROR) << "Failed to write a FUSE message";
+                    PLOG(ERROR) << "Failed to write a FUSE message: "
+                                << "fd=" << fd << " "
+                                << "sockflag=" << sockflag << " "
+                                << "data=" << data;
                     return ResultOrAgain::kFailure;
             }
         }
@@ -248,7 +251,9 @@
 void FuseBuffer::HandleNotImpl() {
   LOG(VERBOSE) << "NOTIMPL op=" << request.header.opcode << " uniq="
       << request.header.unique << " nid=" << request.header.nodeid;
-  const uint64_t unique = request.header.unique;
+  // Add volatile as a workaround for compiler issue which removes the temporary
+  // variable.
+  const volatile uint64_t unique = request.header.unique;
   response.Reset(0, -ENOSYS, unique);
 }
 
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 285aa6e..a643a29 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -110,9 +110,10 @@
                 "libunwind",
             ],
 
-            static_libs: ["libcutils"],
+            static_libs: ["libasync_safe", "libcutils"],
         },
     },
+    whole_static_libs: ["libdemangle"],
 }
 
 cc_library_shared {
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 3545661..e46d353 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -27,6 +27,8 @@
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
+#include <demangle.h>
+
 #include "BacktraceLog.h"
 #include "thread_utils.h"
 #include "UnwindCurrent.h"
@@ -62,8 +64,7 @@
   if (map->start == 0 || (map->flags & PROT_DEVICE_MAP)) {
     return "";
   }
-  std::string func_name = GetFunctionNameRaw(pc, offset);
-  return func_name;
+  return demangle(GetFunctionNameRaw(pc, offset).c_str());
 }
 
 bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, word_t* out_value) {
diff --git a/libbacktrace/BacktraceAsyncSafeLog.h b/libbacktrace/BacktraceAsyncSafeLog.h
new file mode 100644
index 0000000..14f51be
--- /dev/null
+++ b/libbacktrace/BacktraceAsyncSafeLog.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
+#define _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
+
+#if defined(__ANDROID__)
+
+#include <async_safe/log.h>
+
+// Logging macros for use in signal handler, only available on target.
+#define BACK_ASYNC_SAFE_LOGW(format, ...)                                                     \
+  async_safe_format_log(ANDROID_LOG_WARN, "libbacktrace", "%s: " format, __PRETTY_FUNCTION__, \
+                        ##__VA_ARGS__)
+
+#define BACK_ASYNC_SAFE_LOGE(format, ...)                                                      \
+  async_safe_format_log(ANDROID_LOG_ERROR, "libbacktrace", "%s: " format, __PRETTY_FUNCTION__, \
+                        ##__VA_ARGS__)
+
+#else
+
+#define BACK_ASYNC_SAFE_LOGW(format, ...)
+
+#define BACK_ASYNC_SAFE_LOGE(format, ...)
+
+#endif
+
+#endif  // _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index d7a3b01..fb76b85 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -31,8 +31,8 @@
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
+#include "BacktraceAsyncSafeLog.h"
 #include "BacktraceCurrent.h"
-#include "BacktraceLog.h"
 #include "ThreadEntry.h"
 #include "thread_utils.h"
 
@@ -47,7 +47,7 @@
     *out_value = *reinterpret_cast<word_t*>(ptr);
     return true;
   } else {
-    BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
+    BACK_ASYNC_SAFE_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
     *out_value = static_cast<word_t>(-1);
     return false;
   }
@@ -114,7 +114,8 @@
 static void SignalLogOnly(int, siginfo_t*, void*) {
   ErrnoRestorer restore;
 
-  BACK_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(), gettid(), THREAD_SIGNAL);
+  BACK_ASYNC_SAFE_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(), gettid(),
+                       THREAD_SIGNAL);
 }
 
 static void SignalHandler(int, siginfo_t*, void* sigcontext) {
@@ -122,7 +123,7 @@
 
   ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
   if (!entry) {
-    BACK_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
+    BACK_ASYNC_SAFE_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
     return;
   }
 
@@ -141,7 +142,7 @@
     entry->Wake();
   } else {
     // At this point, it is possible that entry has been freed, so just exit.
-    BACK_LOGE("Timed out waiting for unwind thread to indicate it completed.");
+    BACK_ASYNC_SAFE_LOGE("Timed out waiting for unwind thread to indicate it completed.");
   }
 }
 
@@ -159,7 +160,7 @@
   act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
   sigemptyset(&act.sa_mask);
   if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) {
-    BACK_LOGE("sigaction failed: %s", strerror(errno));
+    BACK_ASYNC_SAFE_LOGE("sigaction failed: %s", strerror(errno));
     ThreadEntry::Remove(entry);
     pthread_mutex_unlock(&g_sigaction_mutex);
     error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
@@ -212,7 +213,7 @@
     // Wait for the thread to indicate it is done with the ThreadEntry.
     if (!entry->Wait(3)) {
       // Send a warning, but do not mark as a failure to unwind.
-      BACK_LOGW("Timed out waiting for signal handler to indicate it finished.");
+      BACK_ASYNC_SAFE_LOGW("Timed out waiting for signal handler to indicate it finished.");
     }
   } else {
     // Check to see if the thread has disappeared.
@@ -220,7 +221,7 @@
       error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
     } else {
       error_ = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
-      BACK_LOGE("Timed out waiting for signal handler to get ucontext data.");
+      BACK_ASYNC_SAFE_LOGE("Timed out waiting for signal handler to get ucontext data.");
     }
   }
 
diff --git a/libbacktrace/ThreadEntry.cpp b/libbacktrace/ThreadEntry.cpp
index 084c1aa..9bd59e4 100644
--- a/libbacktrace/ThreadEntry.cpp
+++ b/libbacktrace/ThreadEntry.cpp
@@ -21,7 +21,7 @@
 #include <time.h>
 #include <ucontext.h>
 
-#include "BacktraceLog.h"
+#include "BacktraceAsyncSafeLog.h"
 #include "ThreadEntry.h"
 
 // Initialize static member variables.
@@ -106,7 +106,7 @@
   while (wait_value_ != value) {
     int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts);
     if (ret != 0) {
-      BACK_LOGW("pthread_cond_timedwait for value %d failed: %s", value, strerror(ret));
+      BACK_ASYNC_SAFE_LOGW("pthread_cond_timedwait for value %d failed: %s", value, strerror(ret));
       wait_completed = false;
       break;
     }
diff --git a/libbinderwrapper/Android.bp b/libbinderwrapper/Android.bp
new file mode 100644
index 0000000..6fac0d8
--- /dev/null
+++ b/libbinderwrapper/Android.bp
@@ -0,0 +1,61 @@
+//
+// 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.
+//
+
+cc_defaults {
+    name: "libbinderwrapper_defaults",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+
+        // for libchrome
+        "-Wno-sign-promo",
+    ],
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libbinder",
+        "libchrome",
+        "libutils",
+    ],
+}
+
+// libbinderwrapper shared library
+// ========================================================
+cc_library_shared {
+    name: "libbinderwrapper",
+    defaults: ["libbinderwrapper_defaults"],
+
+    srcs: [
+        "binder_wrapper.cc",
+        "real_binder_wrapper.cc",
+    ],
+}
+
+// libbinderwrapper_test_support static library
+// ========================================================
+cc_library_static {
+    name: "libbinderwrapper_test_support",
+    defaults: ["libbinderwrapper_defaults"],
+
+    static_libs: ["libgtest"],
+    shared_libs: ["libbinderwrapper"],
+
+    srcs: [
+        "binder_test_base.cc",
+        "stub_binder_wrapper.cc",
+    ],
+}
diff --git a/libbinderwrapper/Android.mk b/libbinderwrapper/Android.mk
deleted file mode 100644
index c768373..0000000
--- a/libbinderwrapper/Android.mk
+++ /dev/null
@@ -1,62 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-binderwrapperCommonCFlags := -Wall -Werror -Wno-unused-parameter
-binderwrapperCommonCFlags += -Wno-sign-promo  # for libchrome
-binderwrapperCommonExportCIncludeDirs := $(LOCAL_PATH)/include
-binderwrapperCommonCIncludes := $(LOCAL_PATH)/include
-binderwrapperCommonSharedLibraries := \
-  libbinder \
-  libchrome \
-  libutils \
-
-# libbinderwrapper shared library
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbinderwrapper
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := $(binderwrapperCommonCFlags)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(binderwrapperCommonExportCIncludeDirs)
-LOCAL_C_INCLUDES := $(binderwrapperCommonCIncludes)
-LOCAL_SHARED_LIBRARIES := $(binderwrapperCommonSharedLibraries)
-LOCAL_SRC_FILES := \
-  binder_wrapper.cc \
-  real_binder_wrapper.cc \
-
-include $(BUILD_SHARED_LIBRARY)
-
-# libbinderwrapper_test_support static library
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbinderwrapper_test_support
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := $(binderwrapperCommonCFlags)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(binderwrapperCommonExportCIncludeDirs)
-LOCAL_C_INCLUDES := $(binderwrapperCommonCIncludes)
-LOCAL_STATIC_LIBRARIES := libgtest
-LOCAL_SHARED_LIBRARIES := \
-  $(binderwrapperCommonSharedLibraries) \
-  libbinderwrapper \
-
-LOCAL_SRC_FILES := \
-  binder_test_base.cc \
-  stub_binder_wrapper.cc \
-
-include $(BUILD_STATIC_LIBRARY)
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 513736d..245deb1 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -24,7 +24,6 @@
     "socket_inaddr_any_server_unix.c",
     "socket_local_client_unix.c",
     "socket_local_server_unix.c",
-    "socket_loopback_server_unix.c",
     "socket_network_client_unix.c",
     "sockets_unix.cpp",
     "str_parms.c",
@@ -54,7 +53,7 @@
     host_supported: true,
     srcs: [
         "config_utils.c",
-        "fs_config.c",
+        "fs_config.cpp",
         "canned_fs_config.c",
         "hashmap.c",
         "iosched_policy.c",
@@ -95,6 +94,9 @@
             shared: {
                 enabled: false,
             },
+            cflags: [
+                "-D_GNU_SOURCE",
+            ],
         },
 
         android: {
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
index 92717c0..b4abb79 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.c
@@ -28,6 +28,7 @@
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
+#include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <unistd.h>
 
diff --git a/libcutils/canned_fs_config.c b/libcutils/canned_fs_config.c
index e0e6a34..96ca566 100644
--- a/libcutils/canned_fs_config.c
+++ b/libcutils/canned_fs_config.c
@@ -17,6 +17,7 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -41,7 +42,7 @@
 }
 
 int load_canned_fs_config(const char* fn) {
-    char line[PATH_MAX + 200];
+    char buf[PATH_MAX + 200];
     FILE* f;
 
     f = fopen(fn, "r");
@@ -50,17 +51,21 @@
         return -1;
     }
 
-    while (fgets(line, sizeof(line), f)) {
+    while (fgets(buf, sizeof(buf), f)) {
         Path* p;
         char* token;
+        char* line = buf;
+        bool rootdir;
 
         while (canned_used >= canned_alloc) {
             canned_alloc = (canned_alloc+1) * 2;
             canned_data = (Path*) realloc(canned_data, canned_alloc * sizeof(Path));
         }
         p = canned_data + canned_used;
-        p->path = strdup(strtok(line, " "));
-        p->uid = atoi(strtok(NULL, " "));
+        if (line[0] == '/') line++;
+        rootdir = line[0] == ' ';
+        p->path = strdup(rootdir ? "" : strtok(line, " "));
+        p->uid = atoi(strtok(rootdir ? line : NULL, " "));
         p->gid = atoi(strtok(NULL, " "));
         p->mode = strtol(strtok(NULL, " "), NULL, 8);   // mode is in octal
         p->capabilities = 0;
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.cpp
similarity index 77%
rename from libcutils/fs_config.c
rename to libcutils/fs_config.cpp
index 5b9d174..c39071c 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.cpp
@@ -14,15 +14,12 @@
  * limitations under the License.
  */
 
-/* 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.
-*/
+// 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.
 
 #define LOG_TAG "fs_config"
 
-#define _GNU_SOURCE
-
 #include <errno.h>
 #include <fcntl.h>
 #include <stdbool.h>
@@ -42,8 +39,10 @@
 #define O_BINARY 0
 #endif
 
-/* My kingdom for <endian.h> */
-static inline uint16_t get2LE(const uint8_t* src) { return src[0] | (src[1] << 8); }
+// My kingdom for <endian.h>
+static inline uint16_t get2LE(const uint8_t* src) {
+    return src[0] | (src[1] << 8);
+}
 
 static inline uint64_t get8LE(const uint8_t* src) {
     uint32_t low, high;
@@ -55,14 +54,13 @@
 
 #define ALIGN(x, alignment) (((x) + ((alignment)-1)) & ~((alignment)-1))
 
-/* Rules for directories.
-** These rules are applied based on "first match", so they
-** should start with the most specific path and work their
-** way up to the root.
-*/
+// Rules for directories.
+// These rules are applied based on "first match", so they
+// should start with the most specific path and work their
+// way up to the root.
 
 static const struct fs_path_config android_dirs[] = {
-    /* clang-format off */
+    // clang-format off
     { 00770, AID_SYSTEM,       AID_CACHE,        0, "cache" },
     { 00500, AID_ROOT,         AID_ROOT,         0, "config" },
     { 00771, AID_SYSTEM,       AID_SYSTEM,       0, "data/app" },
@@ -92,25 +90,26 @@
     { 00755, AID_ROOT,         AID_SHELL,        0, "system/xbin" },
     { 00755, AID_ROOT,         AID_SHELL,        0, "vendor" },
     { 00755, AID_ROOT,         AID_ROOT,         0, 0 },
-    /* clang-format on */
+    // clang-format on
 };
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__android_dirs = android_dirs;
+#endif
 
-/* Rules for files.
-** These rules are applied based on "first match", so they
-** should start with the most specific path and work their
-** way up to the root. Prefixes ending in * denotes wildcard
-** and will allow partial matches.
-*/
+// Rules for files.
+// These rules are applied based on "first match", so they
+// should start with the most specific path and work their
+// way up to the root. Prefixes ending in * denotes wildcard
+// and will allow partial matches.
 static const char sys_conf_dir[] = "/system/etc/fs_config_dirs";
 static const char sys_conf_file[] = "/system/etc/fs_config_files";
-/* No restrictions are placed on the vendor and oem file-system config files,
- * although the developer is advised to restrict the scope to the /vendor or
- * oem/ file-system since the intent is to provide support for customized
- * portions of a separate vendor.img or oem.img.  Has to remain open so that
- * customization can also land on /system/vendor, /system/oem or /system/odm.
- * We expect build-time checking or filtering when constructing the associated
- * fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
- */
+// No restrictions are placed on the vendor and oem file-system config files,
+// although the developer is advised to restrict the scope to the /vendor or
+// oem/ file-system since the intent is to provide support for customized
+// portions of a separate vendor.img or oem.img.  Has to remain open so that
+// customization can also land on /system/vendor, /system/oem or /system/odm.
+// We expect build-time checking or filtering when constructing the associated
+// fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
 static const char ven_conf_dir[] = "/vendor/etc/fs_config_dirs";
 static const char ven_conf_file[] = "/vendor/etc/fs_config_files";
 static const char oem_conf_dir[] = "/oem/etc/fs_config_dirs";
@@ -125,7 +124,7 @@
 };
 
 static const struct fs_path_config android_files[] = {
-    /* clang-format off */
+    // clang-format off
     { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app/*" },
     { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app-ephemeral/*" },
     { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app-private/*" },
@@ -141,14 +140,8 @@
     { 00600, AID_ROOT,      AID_ROOT,      0, "odm/default.prop" },
     { 00444, AID_ROOT,      AID_ROOT,      0, odm_conf_dir + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, odm_conf_file + 1 },
-    { 00600, AID_ROOT,      AID_ROOT,      0, "system/odm/build.prop" },
-    { 00600, AID_ROOT,      AID_ROOT,      0, "system/odm/default.prop" },
-    { 00444, AID_ROOT,      AID_ROOT,      0, "system/odm/etc/fs_config_dirs" },
-    { 00444, AID_ROOT,      AID_ROOT,      0, "system/odm/etc/fs_config_files" },
     { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_dir + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_file + 1 },
-    { 00444, AID_ROOT,      AID_ROOT,      0, "system/oem/etc/fs_config_dirs" },
-    { 00444, AID_ROOT,      AID_ROOT,      0, "system/oem/etc/fs_config_files" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/fs_mgr" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump32" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump64" },
@@ -165,22 +158,18 @@
     { 00555, AID_ROOT,      AID_ROOT,      0, "system/etc/ppp/*" },
     { 00555, AID_ROOT,      AID_ROOT,      0, "system/etc/rc.*" },
     { 00440, AID_ROOT,      AID_ROOT,      0, "system/etc/recovery.img" },
-    { 00600, AID_ROOT,      AID_ROOT,      0, "system/vendor/build.prop" },
-    { 00600, AID_ROOT,      AID_ROOT,      0, "system/vendor/default.prop" },
-    { 00444, AID_ROOT,      AID_ROOT,      0, "system/vendor/etc/fs_config_dirs" },
-    { 00444, AID_ROOT,      AID_ROOT,      0, "system/vendor/etc/fs_config_files" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "vendor/build.prop" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "vendor/default.prop" },
     { 00444, AID_ROOT,      AID_ROOT,      0, ven_conf_dir + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, ven_conf_file + 1 },
 
-    /* the following two files are INTENTIONALLY set-uid, but they
-     * are NOT included on user builds. */
+    // the following two files are INTENTIONALLY set-uid, but they
+    // are NOT included on user builds.
     { 06755, AID_ROOT,      AID_ROOT,      0, "system/xbin/procmem" },
     { 04750, AID_ROOT,      AID_SHELL,     0, "system/xbin/su" },
 
-    /* the following files have enhanced capabilities and ARE included
-     * in user builds. */
+    // the following files have enhanced capabilities and ARE included
+    // in user builds.
     { 00700, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
                                               "system/bin/inputflinger" },
     { 00550, AID_LOGD,      AID_LOGD,      CAP_MASK_LONG(CAP_SYSLOG) |
@@ -191,37 +180,27 @@
                                            CAP_MASK_LONG(CAP_SETGID),
                                               "system/bin/run-as" },
 
-    /* Support FIFO scheduling mode in SurfaceFlinger. */
+    // Support FIFO scheduling mode in SurfaceFlinger.
     { 00755, AID_SYSTEM,    AID_GRAPHICS,  CAP_MASK_LONG(CAP_SYS_NICE),
                                               "system/bin/surfaceflinger" },
 
-    /* Support hostapd administering a network interface. */
-    { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
-                                           CAP_MASK_LONG(CAP_NET_RAW),
-                                              "system/vendor/bin/hostapd" },
+    // Support hostapd administering a network interface.
     { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
                                            CAP_MASK_LONG(CAP_NET_RAW),
                                               "vendor/bin/hostapd" },
 
-    /* Support Bluetooth legacy hal accessing /sys/class/rfkill
-     * Support RT scheduling in Bluetooth */
-    { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN) |
-                                           CAP_MASK_LONG(CAP_SYS_NICE),
-                                              "system/vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
+    // Support Bluetooth legacy hal accessing /sys/class/rfkill
+    // Support RT scheduling in Bluetooth
     { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN) |
                                            CAP_MASK_LONG(CAP_SYS_NICE),
                                               "vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
 
-    /* Support wifi_hal_legacy administering a network interface. */
-    { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
-                                           CAP_MASK_LONG(CAP_NET_RAW),
-                                              "system/vendor/bin/hw/android.hardware.wifi@1.0-service" },
+    // Support wifi_hal_legacy administering a network interface.
     { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
                                            CAP_MASK_LONG(CAP_NET_RAW),
                                               "vendor/bin/hw/android.hardware.wifi@1.0-service" },
 
-    /* A non-privileged zygote that spawns
-     * isolated processes for web rendering. */
+    // A non-privileged zygote that spawns isolated processes for web rendering.
     { 0750,  AID_ROOT,      AID_ROOT,      CAP_MASK_LONG(CAP_SETUID) |
                                            CAP_MASK_LONG(CAP_SETGID) |
                                            CAP_MASK_LONG(CAP_SETPCAP),
@@ -231,7 +210,7 @@
                                            CAP_MASK_LONG(CAP_SETPCAP),
                                               "system/bin/webview_zygote64" },
 
-    /* generic defaults */
+    // generic defaults
     { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
     { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "init*" },
@@ -239,14 +218,15 @@
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/*" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib/valgrind/*" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib64/valgrind/*" },
-    { 00755, AID_ROOT,      AID_SHELL,     0, "system/vendor/bin/*" },
-    { 00755, AID_ROOT,      AID_SHELL,     0, "system/vendor/xbin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/xbin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/xbin/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, 0 },
-    /* clang-format on */
+    // clang-format on
 };
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__android_files = android_files;
+#endif
 
 static size_t strip(const char* path, size_t len, const char suffix[]) {
     if (len < strlen(suffix)) return len;
@@ -258,9 +238,9 @@
     int fd = -1;
 
     if (target_out_path && *target_out_path) {
-        /* target_out_path is the path to the directory holding content of
-         * system partition but as we cannot guarantee it ends with '/system'
-         * or with or without a trailing slash, need to strip them carefully. */
+        // target_out_path is the path to the directory holding content of
+        // system partition but as we cannot guarantee it ends with '/system'
+        // or with or without a trailing slash, need to strip them carefully.
         char* name = NULL;
         size_t len = strlen(target_out_path);
         len = strip(target_out_path, len, "/");
@@ -276,21 +256,51 @@
     return fd;
 }
 
+// if path is "vendor/<stuff>", "oem/<stuff>" or "odm/<stuff>"
+static bool is_partition(const char* path, size_t len) {
+    static const char* partitions[] = {"vendor/", "oem/", "odm/"};
+    for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
+        size_t plen = strlen(partitions[i]);
+        if (len <= plen) continue;
+        if (!strncmp(path, partitions[i], plen)) return true;
+    }
+    return false;
+}
+
+// alias prefixes of "<partition>/<stuff>" to "system/<partition>/<stuff>" or
+// "system/<partition>/<stuff>" to "<partition>/<stuff>"
+static bool prefix_cmp(const char* prefix, const char* path, size_t len) {
+    if (!strncmp(prefix, path, len)) return true;
+
+    static const char system[] = "system/";
+    if (!strncmp(path, system, strlen(system))) {
+        path += strlen(system);
+    } else if (len <= strlen(system)) {
+        return false;
+    } else if (strncmp(prefix, system, strlen(system))) {
+        return false;
+    } else {
+        prefix += strlen(system);
+        len -= strlen(system);
+    }
+    return is_partition(prefix, len) && !strncmp(prefix, path, len);
+}
+
 static bool fs_config_cmp(bool dir, const char* prefix, size_t len, const char* path, size_t plen) {
     if (dir) {
         if (plen < len) {
             return false;
         }
     } else {
-        /* If name ends in * then allow partial matches. */
+        // If name ends in * then allow partial matches.
         if (prefix[len - 1] == '*') {
-            return !strncmp(prefix, path, len - 1);
+            return prefix_cmp(prefix, path, len - 1);
         }
         if (plen != len) {
             return false;
         }
     }
-    return !strncmp(prefix, path, len);
+    return prefix_cmp(prefix, path, len);
 }
 
 void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
@@ -318,7 +328,7 @@
                 ALOGE("%s len is corrupted", conf[which][dir]);
                 break;
             }
-            prefix = calloc(1, remainder);
+            prefix = static_cast<char*>(calloc(1, remainder));
             if (!prefix) {
                 ALOGE("%s out of memory", conf[which][dir]);
                 break;
@@ -329,7 +339,7 @@
                 break;
             }
             len = strnlen(prefix, remainder);
-            if (len >= remainder) { /* missing a terminating null */
+            if (len >= remainder) {  // missing a terminating null
                 free(prefix);
                 ALOGE("%s is corrupted", conf[which][dir]);
                 break;
diff --git a/libcutils/include/cutils/bitops.h b/libcutils/include/cutils/bitops.h
index 045830d..38d2840 100644
--- a/libcutils/include/cutils/bitops.h
+++ b/libcutils/include/cutils/bitops.h
@@ -24,94 +24,15 @@
 
 __BEGIN_DECLS
 
-/*
- * Bitmask Operations
- *
- * Note this doesn't provide any locking/exclusion, and isn't atomic.
- * Additionally no bounds checking is done on the bitmask array.
- *
- * Example:
- *
- * int num_resources;
- * unsigned int resource_bits[BITS_TO_WORDS(num_resources)];
- * bitmask_init(resource_bits, num_resources);
- * ...
- * int bit = bitmask_ffz(resource_bits, num_resources);
- * bitmask_set(resource_bits, bit);
- * ...
- * if (bitmask_test(resource_bits, bit)) { ... }
- * ...
- * bitmask_clear(resource_bits, bit);
- *
- */
-
-#define BITS_PER_WORD    (sizeof(unsigned int) * 8)
-#define BITS_TO_WORDS(x) (((x) + BITS_PER_WORD - 1) / BITS_PER_WORD)
-#define BIT_IN_WORD(x)   ((x) % BITS_PER_WORD)
-#define BIT_WORD(x)      ((x) / BITS_PER_WORD)
-#define BIT_MASK(x)      (1 << BIT_IN_WORD(x))
-
-static inline void bitmask_init(unsigned int *bitmask, int num_bits)
-{
-    memset(bitmask, 0, BITS_TO_WORDS(num_bits)*sizeof(unsigned int));
-}
-
-static inline int bitmask_ffz(unsigned int *bitmask, int num_bits)
-{
-    int bit, result;
-    size_t i;
-
-    for (i = 0; i < BITS_TO_WORDS(num_bits); i++) {
-        bit = ffs(~bitmask[i]);
-        if (bit) {
-            // ffs is 1-indexed, return 0-indexed result
-            bit--;
-            result = BITS_PER_WORD * i + bit;
-            if (result >= num_bits)
-                return -1;
-            return result;
-        }
-    }
-    return -1;
-}
-
-static inline int bitmask_weight(unsigned int *bitmask, int num_bits)
-{
-    size_t i;
-    int weight = 0;
-
-    for (i = 0; i < BITS_TO_WORDS(num_bits); i++)
-        weight += __builtin_popcount(bitmask[i]);
-    return weight;
-}
-
-static inline void bitmask_set(unsigned int *bitmask, int bit)
-{
-    bitmask[BIT_WORD(bit)] |= BIT_MASK(bit);
-}
-
-static inline void bitmask_clear(unsigned int *bitmask, int bit)
-{
-    bitmask[BIT_WORD(bit)] &= ~BIT_MASK(bit);
-}
-
-static inline bool bitmask_test(unsigned int *bitmask, int bit)
-{
-    return bitmask[BIT_WORD(bit)] & BIT_MASK(bit);
-}
-
-static inline int popcount(unsigned int x)
-{
+static inline int popcount(unsigned int x) {
     return __builtin_popcount(x);
 }
 
-static inline int popcountl(unsigned long x)
-{
+static inline int popcountl(unsigned long x) {
     return __builtin_popcountl(x);
 }
 
-static inline int popcountll(unsigned long long x)
-{
+static inline int popcountll(unsigned long long x) {
     return __builtin_popcountll(x);
 }
 
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
index 7d6a988..abe6dd6 100644
--- a/libcutils/include/cutils/native_handle.h
+++ b/libcutils/include/cutils/native_handle.h
@@ -43,6 +43,8 @@
 #endif
 } native_handle_t;
 
+typedef const native_handle_t* buffer_handle_t;
+
 /*
  * native_handle_close
  * 
diff --git a/libcutils/include/cutils/sched_policy.h b/libcutils/include/cutils/sched_policy.h
index 15391d9..9683f91 100644
--- a/libcutils/include/cutils/sched_policy.h
+++ b/libcutils/include/cutils/sched_policy.h
@@ -47,6 +47,7 @@
     SP_AUDIO_APP  = 3,
     SP_AUDIO_SYS  = 4,
     SP_TOP_APP    = 5,
+    SP_RT_APP     = 6,
     SP_CNT,
     SP_MAX        = SP_CNT - 1,
     SP_SYSTEM_DEFAULT = SP_FOREGROUND,
diff --git a/libcutils/include/cutils/sockets.h b/libcutils/include/cutils/sockets.h
index d724dd6..b24468b 100644
--- a/libcutils/include/cutils/sockets.h
+++ b/libcutils/include/cutils/sockets.h
@@ -88,8 +88,6 @@
 cutils_socket_t socket_network_client(const char* host, int port, int type);
 int socket_network_client_timeout(const char* host, int port, int type,
                                   int timeout, int* getaddrinfo_error);
-int socket_loopback_server(int port, int type);
-int socket_loopback_server6(int port, int type);
 int socket_local_server(const char* name, int namespaceId, int type);
 int socket_local_server_bind(int s, const char* name, int namespaceId);
 int socket_local_client_connect(int fd, const char *name, int namespaceId,
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index bbba853..02141d6 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -53,7 +53,7 @@
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
-#if defined(__ANDROID__)
+#if defined(__BIONIC__)
 #include <linux/capability.h>
 #else
 #include "android_filesystem_capability.h"
@@ -130,6 +130,7 @@
 #define AID_MEDIA_OBB 1059       /* GID for OBB files on internal media storage */
 #define AID_ESE 1060             /* embedded secure element (eSE) subsystem */
 #define AID_OTA_UPDATE 1061      /* resource tracking UID for OTA updates */
+#define AID_AUTOMOTIVE_EVS 1062  /* Automotive rear and surround view system */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
@@ -152,6 +153,7 @@
 #define AID_NET_BW_ACCT 3007  /* change bandwidth statistics accounting */
 #define AID_READPROC 3009     /* Allow /proc read access */
 #define AID_WAKELOCK 3010     /* Allow system wakelock read/write access */
+#define AID_UHID 3011         /* Allow read/write to /dev/uhid node */
 
 /* The range 5000-5999 is also reserved for OEM, and must never be used here. */
 #define AID_OEM_RESERVED_2_START 5000
diff --git a/libcutils/native_handle.c b/libcutils/native_handle.c
index 9f4840a..95bbc41 100644
--- a/libcutils/native_handle.c
+++ b/libcutils/native_handle.c
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "NativeHandle"
+#include <cutils/native_handle.h>
 
 #include <errno.h>
 #include <stdint.h>
@@ -22,15 +22,12 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <android/log.h>
-#include <cutils/native_handle.h>
-
 static const int kMaxNativeFds = 1024;
 static const int kMaxNativeInts = 1024;
 
-native_handle_t* native_handle_init(char* storage, int numFds, int numInts)
-{
+native_handle_t* native_handle_init(char* storage, int numFds, int numInts) {
     if ((uintptr_t) storage % alignof(native_handle_t)) {
+        errno = EINVAL;
         return NULL;
     }
 
@@ -38,13 +35,12 @@
     handle->version = sizeof(native_handle_t);
     handle->numFds = numFds;
     handle->numInts = numInts;
-
     return handle;
 }
 
-native_handle_t* native_handle_create(int numFds, int numInts)
-{
+native_handle_t* native_handle_create(int numFds, int numInts) {
     if (numFds < 0 || numInts < 0 || numFds > kMaxNativeFds || numInts > kMaxNativeInts) {
+        errno = EINVAL;
         return NULL;
     }
 
@@ -58,14 +54,13 @@
     return h;
 }
 
-native_handle_t* native_handle_clone(const native_handle_t* handle)
-{
+native_handle_t* native_handle_clone(const native_handle_t* handle) {
     native_handle_t* clone = native_handle_create(handle->numFds, handle->numInts);
-    int i;
+    if (clone == NULL) return NULL;
 
-    for (i = 0; i < handle->numFds; i++) {
+    for (int i = 0; i < handle->numFds; i++) {
         clone->data[i] = dup(handle->data[i]);
-        if (clone->data[i] < 0) {
+        if (clone->data[i] == -1) {
             clone->numFds = i;
             native_handle_close(clone);
             native_handle_delete(clone);
@@ -74,30 +69,27 @@
     }
 
     memcpy(&clone->data[handle->numFds], &handle->data[handle->numFds],
-            sizeof(int) * handle->numInts);
+           sizeof(int) * handle->numInts);
 
     return clone;
 }
 
-int native_handle_delete(native_handle_t* h)
-{
+int native_handle_delete(native_handle_t* h) {
     if (h) {
-        if (h->version != sizeof(native_handle_t))
-            return -EINVAL;
+        if (h->version != sizeof(native_handle_t)) return -EINVAL;
         free(h);
     }
     return 0;
 }
 
-int native_handle_close(const native_handle_t* h)
-{
-    if (h->version != sizeof(native_handle_t))
-        return -EINVAL;
+int native_handle_close(const native_handle_t* h) {
+    if (h->version != sizeof(native_handle_t)) return -EINVAL;
 
+    int saved_errno = errno;
     const int numFds = h->numFds;
-    int i;
-    for (i=0 ; i<numFds ; i++) {
+    for (int i = 0; i < numFds; ++i) {
         close(h->data[i]);
     }
+    errno = saved_errno;
     return 0;
 }
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index 3837ca7..7170b48 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -70,6 +70,7 @@
 static int bg_schedboost_fd = -1;
 static int fg_schedboost_fd = -1;
 static int ta_schedboost_fd = -1;
+static int rt_schedboost_fd = -1;
 
 /* Add tid to the scheduling group defined by the policy */
 static int add_tid_to_cgroup(int tid, int fd)
@@ -173,6 +174,8 @@
                 fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
                 filename = "/dev/stune/background/tasks";
                 bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+                filename = "/dev/stune/rt/tasks";
+                rt_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
             }
         }
     }
@@ -263,24 +266,26 @@
 
     char grpBuf[32];
 
-    if (cpusets_enabled()) {
+    grpBuf[0] = '\0';
+    if (schedboost_enabled()) {
+        if (getCGroupSubsys(tid, "schedtune", grpBuf, sizeof(grpBuf)) < 0) return -1;
+    }
+    if ((grpBuf[0] == '\0') && cpusets_enabled()) {
         if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0) return -1;
-        if (grpBuf[0] == '\0') {
-            *policy = SP_FOREGROUND;
-        } else if (!strcmp(grpBuf, "foreground")) {
-            *policy = SP_FOREGROUND;
-        } else if (!strcmp(grpBuf, "background")) {
-            *policy = SP_BACKGROUND;
-        } else if (!strcmp(grpBuf, "top-app")) {
-            *policy = SP_TOP_APP;
-        } else {
-            errno = ERANGE;
-            return -1;
-        }
-    } else {
-        // In b/34193533, we removed bg_non_interactive cgroup, so now
-        // all threads are in FOREGROUND cgroup
+    }
+    if (grpBuf[0] == '\0') {
         *policy = SP_FOREGROUND;
+    } else if (!strcmp(grpBuf, "foreground")) {
+        *policy = SP_FOREGROUND;
+    } else if (!strcmp(grpBuf, "system-background")) {
+        *policy = SP_SYSTEM;
+    } else if (!strcmp(grpBuf, "background")) {
+        *policy = SP_BACKGROUND;
+    } else if (!strcmp(grpBuf, "top-app")) {
+        *policy = SP_TOP_APP;
+    } else {
+        errno = ERANGE;
+        return -1;
     }
     return 0;
 }
@@ -341,16 +346,25 @@
 static void set_timerslack_ns(int tid, unsigned long long slack) {
     // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
     // TODO: once we've backported this, log if the open(2) fails.
-    char buf[64];
-    snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
-    int fd = open(buf, O_WRONLY | O_CLOEXEC);
-    if (fd != -1) {
-        int len = snprintf(buf, sizeof(buf), "%llu", slack);
-        if (write(fd, buf, len) != len) {
-            SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
+    if (__sys_supports_timerslack) {
+        char buf[64];
+        snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
+        int fd = open(buf, O_WRONLY | O_CLOEXEC);
+        if (fd != -1) {
+            int len = snprintf(buf, sizeof(buf), "%llu", slack);
+            if (write(fd, buf, len) != len) {
+                SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
+            }
+            close(fd);
+            return;
         }
-        close(fd);
-        return;
+    }
+
+    // TODO: Remove when /proc/<tid>/timerslack_ns interface is backported.
+    if ((tid == 0) || (tid == gettid())) {
+        if (prctl(PR_SET_TIMERSLACK, slack) == -1) {
+            SLOGE("set_timerslack_ns prctl failed: %s\n", strerror(errno));
+        }
     }
 }
 
@@ -397,6 +411,9 @@
     case SP_SYSTEM:
         SLOGD("/// tid %d (%s)", tid, thread_name);
         break;
+    case SP_RT_APP:
+	SLOGD("RT  tid %d (%s)", tid, thread_name);
+	break;
     default:
         SLOGD("??? tid %d (%s)", tid, thread_name);
         break;
@@ -417,6 +434,9 @@
         case SP_TOP_APP:
             boost_fd = ta_schedboost_fd;
             break;
+        case SP_RT_APP:
+	    boost_fd = rt_schedboost_fd;
+	    break;
         default:
             boost_fd = -1;
             break;
@@ -429,10 +449,7 @@
 
     }
 
-    if (__sys_supports_timerslack) {
-        set_timerslack_ns(tid, policy == SP_BACKGROUND ?
-                               TIMER_SLACK_BG : TIMER_SLACK_FG);
-    }
+    set_timerslack_ns(tid, policy == SP_BACKGROUND ? TIMER_SLACK_BG : TIMER_SLACK_FG);
 
     return 0;
 }
@@ -464,6 +481,7 @@
        [SP_AUDIO_APP]  = "aa",
        [SP_AUDIO_SYS]  = "as",
        [SP_TOP_APP]    = "ta",
+       [SP_RT_APP]    = "rt",
     };
     if ((policy < SP_CNT) && (strings[policy] != NULL))
         return strings[policy];
diff --git a/libcutils/socket_loopback_server_unix.c b/libcutils/socket_loopback_server_unix.c
deleted file mode 100644
index 7b92fd6..0000000
--- a/libcutils/socket_loopback_server_unix.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
-
-#include <errno.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#define LISTEN_BACKLOG 4
-
-#if !defined(_WIN32)
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#endif
-
-#include <cutils/sockets.h>
-
-static int _socket_loopback_server(int family, int type, struct sockaddr * addr, size_t size)
-{
-    int s, n;
-
-    s = socket(family, type, 0);
-    if(s < 0)
-        return -1;
-
-    n = 1;
-    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));
-
-    if(bind(s, addr, size) < 0) {
-        close(s);
-        return -1;
-    }
-
-    if (type == SOCK_STREAM) {
-        int ret;
-
-        ret = listen(s, LISTEN_BACKLOG);
-
-        if (ret < 0) {
-            close(s);
-            return -1;
-        }
-    }
-
-    return s;
-}
-
-/* open listen() port on loopback IPv6 interface */
-int socket_loopback_server6(int port, int type)
-{
-    struct sockaddr_in6 addr;
-
-    memset(&addr, 0, sizeof(addr));
-    addr.sin6_family = AF_INET6;
-    addr.sin6_port = htons(port);
-    addr.sin6_addr = in6addr_loopback;
-
-    return _socket_loopback_server(AF_INET6, type, (struct sockaddr *) &addr, sizeof(addr));
-}
-
-/* open listen() port on loopback interface */
-int socket_loopback_server(int port, int type)
-{
-    struct sockaddr_in addr;
-
-    memset(&addr, 0, sizeof(addr));
-    addr.sin_family = AF_INET;
-    addr.sin_port = htons(port);
-    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
-    return _socket_loopback_server(AF_INET, type, (struct sockaddr *) &addr, sizeof(addr));
-}
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index 718d76b..7884190 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -27,7 +27,8 @@
                 "test_str_parms.cpp",
                 "android_get_control_socket_test.cpp",
                 "android_get_control_file_test.cpp",
-                "multiuser_test.cpp"
+                "multiuser_test.cpp",
+                "fs_config.cpp",
             ],
         },
 
@@ -62,6 +63,7 @@
 
 cc_test {
     name: "libcutils_test",
+    test_suites: ["device-tests"],
     defaults: ["libcutils_test_default"],
     host_supported: true,
     shared_libs: test_libraries,
@@ -69,6 +71,7 @@
 
 cc_test {
     name: "libcutils_test_static",
+    test_suites: ["device-tests"],
     defaults: ["libcutils_test_default"],
     static_libs: ["libc"] + test_libraries,
     stl: "libc++_static",
diff --git a/libcutils/tests/AndroidTest.xml b/libcutils/tests/AndroidTest.xml
new file mode 100644
index 0000000..dd7aca2
--- /dev/null
+++ b/libcutils/tests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for libcutils_test">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="libcutils_test->/data/local/tmp/libcutils_test" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="libcutils_test" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/libcutils/tests/AshmemTest.cpp b/libcutils/tests/AshmemTest.cpp
index 51c679f..a87e23e 100644
--- a/libcutils/tests/AshmemTest.cpp
+++ b/libcutils/tests/AshmemTest.cpp
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-#include <sys/mman.h>
+#include <android-base/unique_fd.h>
 #include <cutils/ashmem.h>
 #include <gtest/gtest.h>
-#include <android-base/unique_fd.h>
+#include <linux/fs.h>
+#include <sys/mman.h>
 
 using android::base::unique_fd;
 
@@ -29,8 +30,8 @@
     ASSERT_EQ(0, ashmem_set_prot_region(fd, prot));
 }
 
-void TestMmap(const unique_fd &fd, size_t size, int prot, void **region) {
-    *region = mmap(nullptr, size, prot, MAP_SHARED, fd, 0);
+void TestMmap(const unique_fd& fd, size_t size, int prot, void** region, off_t off = 0) {
+    *region = mmap(nullptr, size, prot, MAP_SHARED, fd, off);
     ASSERT_NE(MAP_FAILED, *region);
 }
 
@@ -38,6 +39,10 @@
     EXPECT_EQ(MAP_FAILED, mmap(nullptr, size, prot, MAP_SHARED, fd, 0));
 }
 
+void TestProtIs(const unique_fd& fd, int prot) {
+    EXPECT_EQ(prot, ioctl(fd, ASHMEM_GET_PROT_MASK));
+}
+
 void FillData(uint8_t* data, size_t dataLen) {
     for (size_t i = 0; i < dataLen; i++) {
         data[i] = i & 0xFF;
@@ -101,6 +106,63 @@
     EXPECT_EQ(0, munmap(region2, size));
 }
 
+TEST(AshmemTest, FileOperationsTest) {
+    unique_fd fd;
+    void* region;
+
+    // Allocate a 4-page buffer, but leave page-sized holes on either side
+    constexpr size_t size = PAGE_SIZE * 4;
+    constexpr size_t dataSize = PAGE_SIZE * 2;
+    constexpr size_t holeSize = PAGE_SIZE;
+    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
+    ASSERT_NO_FATAL_FAILURE(TestMmap(fd, dataSize, PROT_READ | PROT_WRITE, &region, holeSize));
+
+    uint8_t data[dataSize];
+    FillData(data, dataSize);
+    memcpy(region, data, dataSize);
+
+    constexpr off_t dataStart = holeSize;
+    constexpr off_t dataEnd = dataStart + dataSize;
+
+    // The sequence of seeks below looks something like this:
+    //
+    // [    ][data][data][    ]
+    // --^                          lseek(99, SEEK_SET)
+    //   ------^                    lseek(dataStart, SEEK_CUR)
+    // ------^                      lseek(0, SEEK_DATA)
+    //       ------------^          lseek(dataStart, SEEK_HOLE)
+    //                      ^--     lseek(-99, SEEK_END)
+    //                ^------       lseek(-dataStart, SEEK_CUR)
+    const struct {
+        // lseek() parameters
+        off_t offset;
+        int whence;
+        // Expected lseek() return value
+        off_t ret;
+    } seeks[] = {
+        {99, SEEK_SET, 99},         {dataStart, SEEK_CUR, dataStart + 99},
+        {0, SEEK_DATA, dataStart},  {dataStart, SEEK_HOLE, dataEnd},
+        {-99, SEEK_END, size - 99}, {-dataStart, SEEK_CUR, dataEnd - 99},
+    };
+    for (const auto& cfg : seeks) {
+        errno = 0;
+        auto off = lseek(fd, cfg.offset, cfg.whence);
+        ASSERT_EQ(cfg.ret, off) << "lseek(" << cfg.offset << ", " << cfg.whence << ") failed"
+                                << (errno ? ": " : "") << (errno ? strerror(errno) : "");
+
+        if (off >= dataStart && off < dataEnd) {
+            off_t dataOff = off - dataStart;
+            ssize_t readSize = dataSize - dataOff;
+            uint8_t buf[readSize];
+
+            ASSERT_EQ(readSize, TEMP_FAILURE_RETRY(read(fd, buf, readSize)));
+            EXPECT_EQ(0, memcmp(buf, data + dataOff, readSize));
+        }
+    }
+
+    EXPECT_EQ(0, munmap(region, dataSize));
+}
+
 TEST(AshmemTest, ProtTest) {
     unique_fd fd;
     constexpr size_t size = PAGE_SIZE;
@@ -108,13 +170,25 @@
 
     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ));
     TestProtDenied(fd, size, PROT_WRITE);
+    TestProtIs(fd, PROT_READ);
     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, &region));
     EXPECT_EQ(0, munmap(region, size));
 
     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_WRITE));
     TestProtDenied(fd, size, PROT_READ);
+    TestProtIs(fd, PROT_WRITE);
     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_WRITE, &region));
     EXPECT_EQ(0, munmap(region, size));
+
+    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
+    TestProtIs(fd, PROT_READ | PROT_WRITE);
+    ASSERT_EQ(0, ashmem_set_prot_region(fd, PROT_READ));
+    errno = 0;
+    ASSERT_EQ(-1, ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE))
+        << "kernel shouldn't allow adding protection bits";
+    EXPECT_EQ(EINVAL, errno);
+    TestProtIs(fd, PROT_READ);
+    TestProtDenied(fd, size, PROT_WRITE);
 }
 
 TEST(AshmemTest, ForkProtTest) {
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp
new file mode 100644
index 0000000..a62cd51
--- /dev/null
+++ b/libcutils/tests/fs_config.cpp
@@ -0,0 +1,201 @@
+/*
+ * 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 <inttypes.h>
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/fs_config.h>
+
+extern const fs_path_config* __for_testing_only__android_dirs;
+extern const fs_path_config* __for_testing_only__android_files;
+
+// Maximum entries in system/core/libcutils/fs_config.cpp:android_* before we
+// hit a nullptr termination, before we declare the list is just too big or
+// could be missing the nullptr.
+static constexpr size_t max_idx = 4096;
+
+static bool check_unique(std::vector<const char*>& paths, const std::string& config_name,
+                         const std::string& prefix) {
+    bool retval = false;
+
+    std::string alternate = "system/" + prefix;
+
+    for (size_t idx = 0; idx < paths.size(); ++idx) {
+        size_t second;
+        std::string path(paths[idx]);
+        // check if there are multiple identical paths
+        for (second = idx + 1; second < paths.size(); ++second) {
+            if (path == paths[second]) {
+                GTEST_LOG_(ERROR) << "duplicate paths in " << config_name << ": " << paths[idx];
+                retval = true;
+                break;
+            }
+        }
+
+        // check if path is <partition>/
+        if (android::base::StartsWith(path, prefix.c_str())) {
+            // rebuild path to be system/<partition>/... to check for alias
+            path = alternate + path.substr(prefix.size());
+            for (second = 0; second < paths.size(); ++second) {
+                if (path == paths[second]) {
+                    GTEST_LOG_(ERROR) << "duplicate alias paths in " << config_name << ": "
+                                      << paths[idx] << " and " << paths[second]
+                                      << " (remove latter)";
+                    retval = true;
+                    break;
+                }
+            }
+            continue;
+        }
+
+        // check if path is system/<partition>/
+        if (android::base::StartsWith(path, alternate.c_str())) {
+            // rebuild path to be <partition>/... to check for alias
+            path = prefix + path.substr(alternate.size());
+            for (second = 0; second < paths.size(); ++second) {
+                if (path == paths[second]) break;
+            }
+            if (second >= paths.size()) {
+                GTEST_LOG_(ERROR) << "replace path in " << config_name << ": " << paths[idx]
+                                  << " with " << path;
+                retval = true;
+            }
+        }
+    }
+    return retval;
+}
+
+static bool check_unique(const fs_path_config* paths, const char* type_name,
+                         const std::string& prefix) {
+    std::string config("system/core/libcutils/fs_config.cpp:android_");
+    config += type_name;
+    config += "[]";
+
+    bool retval = false;
+    std::vector<const char*> paths_tmp;
+    for (size_t idx = 0; paths[idx].prefix; ++idx) {
+        if (idx > max_idx) {
+            GTEST_LOG_(WARNING) << config << ": has no end (missing null prefix)";
+            retval = true;
+            break;
+        }
+        paths_tmp.push_back(paths[idx].prefix);
+    }
+
+    return check_unique(paths_tmp, config, prefix) || retval;
+}
+
+#define endof(pointer, field) (offsetof(typeof(*(pointer)), field) + sizeof((pointer)->field))
+
+static bool check_unique(const std::string& config, const std::string& prefix) {
+    int retval = false;
+
+    std::string data;
+    if (!android::base::ReadFileToString(config, &data)) return retval;
+
+    const fs_path_config_from_file* pc =
+        reinterpret_cast<const fs_path_config_from_file*>(data.c_str());
+    size_t len = data.size();
+
+    std::vector<const char*> paths_tmp;
+    size_t entry_number = 0;
+    while (len > 0) {
+        uint16_t host_len = (len >= endof(pc, len)) ? pc->len : INT16_MAX;
+        if (host_len > len) {
+            GTEST_LOG_(WARNING) << config << ": truncated at entry " << entry_number << " ("
+                                << host_len << " > " << len << ")";
+            const std::string unknown("?");
+            GTEST_LOG_(WARNING)
+                << config << ": entry[" << entry_number << "]={ "
+                << "len=" << ((len >= endof(pc, len))
+                                  ? android::base::StringPrintf("%" PRIu16, pc->len)
+                                  : unknown)
+                << ", mode=" << ((len >= endof(pc, mode))
+                                     ? android::base::StringPrintf("0%" PRIo16, pc->mode)
+                                     : unknown)
+                << ", uid=" << ((len >= endof(pc, uid))
+                                    ? android::base::StringPrintf("%" PRIu16, pc->uid)
+                                    : unknown)
+                << ", gid=" << ((len >= endof(pc, gid))
+                                    ? android::base::StringPrintf("%" PRIu16, pc->gid)
+                                    : unknown)
+                << ", capabilities="
+                << ((len >= endof(pc, capabilities))
+                        ? android::base::StringPrintf("0x%" PRIx64, pc->capabilities)
+                        : unknown)
+                << ", prefix="
+                << ((len >= offsetof(fs_path_config_from_file, prefix))
+                        ? android::base::StringPrintf(
+                              "\"%.*s...", (int)(len - offsetof(fs_path_config_from_file, prefix)),
+                              pc->prefix)
+                        : unknown)
+                << " }";
+            retval = true;
+            break;
+        }
+        paths_tmp.push_back(pc->prefix);
+
+        pc = reinterpret_cast<const fs_path_config_from_file*>(reinterpret_cast<const char*>(pc) +
+                                                               host_len);
+        len -= host_len;
+        ++entry_number;
+    }
+
+    return check_unique(paths_tmp, config, prefix) || retval;
+}
+
+void check_two(const fs_path_config* paths, const char* type_name, const char* prefix) {
+    ASSERT_FALSE(paths == nullptr);
+    ASSERT_FALSE(type_name == nullptr);
+    ASSERT_FALSE(prefix == nullptr);
+    bool check_internal = check_unique(paths, type_name, prefix);
+    EXPECT_FALSE(check_internal);
+    bool check_overrides =
+        check_unique(std::string("/") + prefix + "etc/fs_config_" + type_name, prefix);
+    EXPECT_FALSE(check_overrides);
+}
+
+TEST(fs_config, vendor_dirs_alias) {
+    check_two(__for_testing_only__android_dirs, "dirs", "vendor/");
+}
+
+TEST(fs_config, vendor_files_alias) {
+    check_two(__for_testing_only__android_files, "files", "vendor/");
+}
+
+TEST(fs_config, oem_dirs_alias) {
+    check_two(__for_testing_only__android_dirs, "dirs", "oem/");
+}
+
+TEST(fs_config, oem_files_alias) {
+    check_two(__for_testing_only__android_files, "files", "oem/");
+}
+
+TEST(fs_config, odm_dirs_alias) {
+    check_two(__for_testing_only__android_dirs, "dirs", "odm/");
+}
+
+TEST(fs_config, odm_files_alias) {
+    check_two(__for_testing_only__android_files, "files", "odm/");
+}
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index 54bfee5..cf03868 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -14,6 +14,7 @@
 
 cc_library_static {
     name: "libgrallocusage",
+    vendor_available: true,
     cppflags: [
         "-Weverything",
         "-Werror",
@@ -26,4 +27,5 @@
     srcs: ["GrallocUsageConversion.cpp"],
     export_include_dirs: ["include"],
     shared_libs: ["android.hardware.graphics.allocator@2.0"],
+    header_libs: ["libhardware_headers"],
 }
diff --git a/libkeyutils/.clang-format b/libkeyutils/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libkeyutils/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
new file mode 100644
index 0000000..0285259
--- /dev/null
+++ b/libkeyutils/Android.bp
@@ -0,0 +1,16 @@
+cc_library {
+    name: "libkeyutils",
+    cflags: ["-Werror"],
+    defaults: ["linux_bionic_supported"],
+    export_include_dirs: ["include/"],
+    local_include_dirs: ["include/"],
+    srcs: ["keyutils.cpp"],
+    stl: "none",
+}
+
+cc_test {
+    name: "libkeyutils-tests",
+    cflags: ["-Werror"],
+    shared_libs: ["libkeyutils"],
+    srcs: ["keyutils_test.cpp"],
+}
diff --git a/libkeyutils/include/keyutils.h b/libkeyutils/include/keyutils.h
new file mode 100644
index 0000000..585767d
--- /dev/null
+++ b/libkeyutils/include/keyutils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _KEYUTILS_H_
+#define _KEYUTILS_H_
+
+#include <linux/keyctl.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+typedef int32_t key_serial_t;
+
+key_serial_t add_key(const char* type, const char* description, const void* payload,
+                     size_t payload_length, key_serial_t ring_id);
+
+key_serial_t keyctl_get_keyring_ID(key_serial_t id, int create);
+
+long keyctl_revoke(key_serial_t id); /* TODO: remove this */
+
+long keyctl_search(key_serial_t ring_id, const char* type, const char* description,
+                   key_serial_t dest_ring_id);
+
+long keyctl_setperm(key_serial_t id, int permissions);
+
+long keyctl_unlink(key_serial_t key, key_serial_t keyring);
+
+__END_DECLS
+
+#endif
diff --git a/libkeyutils/keyutils.cpp b/libkeyutils/keyutils.cpp
new file mode 100644
index 0000000..58a2a17
--- /dev/null
+++ b/libkeyutils/keyutils.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <keyutils.h>
+
+#include <stdarg.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+// Deliberately not exposed. Callers should use the typed APIs instead.
+static long keyctl(int cmd, ...) {
+  va_list va;
+  va_start(va, cmd);
+  unsigned long arg2 = va_arg(va, unsigned long);
+  unsigned long arg3 = va_arg(va, unsigned long);
+  unsigned long arg4 = va_arg(va, unsigned long);
+  unsigned long arg5 = va_arg(va, unsigned long);
+  va_end(va);
+  return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
+}
+
+key_serial_t add_key(const char* type, const char* description, const void* payload,
+                     size_t payload_length, key_serial_t ring_id) {
+  return syscall(__NR_add_key, type, description, payload, payload_length, ring_id);
+}
+
+key_serial_t keyctl_get_keyring_ID(key_serial_t id, int create) {
+  return keyctl(KEYCTL_GET_KEYRING_ID, id, create);
+}
+
+long keyctl_revoke(key_serial_t id) {
+  return keyctl(KEYCTL_REVOKE, id);
+}
+
+long keyctl_search(key_serial_t ring_id, const char* type, const char* description,
+                   key_serial_t dest_ring_id) {
+  return keyctl(KEYCTL_SEARCH, ring_id, type, description, dest_ring_id);
+}
+
+long keyctl_setperm(key_serial_t id, int permissions) {
+  return keyctl(KEYCTL_SETPERM, id, permissions);
+}
+
+long keyctl_unlink(key_serial_t key, key_serial_t keyring) {
+  return keyctl(KEYCTL_UNLINK, key, keyring);
+}
diff --git a/libkeyutils/keyutils_test.cpp b/libkeyutils/keyutils_test.cpp
new file mode 100644
index 0000000..d41c91b
--- /dev/null
+++ b/libkeyutils/keyutils_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <keyutils.h>
+
+#include <dlfcn.h>
+
+#include <gtest/gtest.h>
+
+TEST(keyutils, smoke) {
+  // Check that the exported type is sane.
+  ASSERT_EQ(4U, sizeof(key_serial_t));
+
+  // Check that all the functions actually exist.
+  ASSERT_TRUE(dlsym(nullptr, "add_key") != nullptr);
+  ASSERT_TRUE(dlsym(nullptr, "keyctl_get_keyring_ID") != nullptr);
+  ASSERT_TRUE(dlsym(nullptr, "keyctl_revoke") != nullptr);
+  ASSERT_TRUE(dlsym(nullptr, "keyctl_search") != nullptr);
+  ASSERT_TRUE(dlsym(nullptr, "keyctl_setperm") != nullptr);
+  ASSERT_TRUE(dlsym(nullptr, "keyctl_unlink") != nullptr);
+}
diff --git a/liblog/event.logtags b/liblog/event.logtags
index 301e885..0a3b650 100644
--- a/liblog/event.logtags
+++ b/liblog/event.logtags
@@ -29,6 +29,7 @@
 # 4: Number of allocations
 # 5: Id
 # 6: Percent
+# s: Number of seconds (monotonic time)
 # Default value for data of type int/long is 2 (bytes).
 #
 # TODO: generate ".java" and ".h" files with integer constants from this file.
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 231f4d9..83064fd 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -230,9 +230,16 @@
   return it->second;
 }
 
+// The position after the end of a valid section of the tag string,
+// caller makes sure delimited appropriately.
+static const char* endOfTag(const char* cp) {
+  while (*cp && (isalnum(*cp) || strchr("_.-@,", *cp))) ++cp;
+  return cp;
+}
+
 // Scan one tag line.
 //
-// "*pData" should be pointing to the first digit in the tag number.  On
+// "pData" should be pointing to the first digit in the tag number.  On
 // successful return, it will be pointing to the last character in the
 // tag line (i.e. the character before the start of the next line).
 //
@@ -242,10 +249,11 @@
 // data and it will outlive the call.
 //
 // Returns 0 on success, nonzero on failure.
-static int scanTagLine(EventTagMap* map, char** pData, int lineNum) {
-  char* cp;
-  unsigned long val = strtoul(*pData, &cp, 10);
-  if (cp == *pData) {
+static int scanTagLine(EventTagMap* map, const char*& pData, int lineNum) {
+  char* ep;
+  unsigned long val = strtoul(pData, &ep, 10);
+  const char* cp = ep;
+  if (cp == pData) {
     if (lineNum) {
       fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
     }
@@ -274,14 +282,13 @@
   }
 
   const char* tag = cp;
-  // Determine whether "c" is a valid tag char.
-  while (isalnum(*++cp) || (*cp == '_')) {
-  }
+  cp = endOfTag(cp);
   size_t tagLen = cp - tag;
 
   if (!isspace(*cp)) {
     if (lineNum) {
-      fprintf(stderr, OUT_TAG ": invalid tag chars on line %d\n", lineNum);
+      fprintf(stderr, OUT_TAG ": invalid tag char %c on line %d\n", *cp,
+              lineNum);
     }
     errno = EINVAL;
     return -1;
@@ -290,9 +297,9 @@
   while (isspace(*cp) && (*cp != '\n')) ++cp;
   const char* fmt = NULL;
   size_t fmtLen = 0;
-  if (*cp != '#') {
+  if (*cp && (*cp != '#')) {
     fmt = cp;
-    while ((*cp != '\n') && (*cp != '#')) ++cp;
+    while (*cp && (*cp != '\n') && (*cp != '#')) ++cp;
     while ((cp > fmt) && isspace(*(cp - 1))) --cp;
     fmtLen = cp - fmt;
   }
@@ -302,7 +309,7 @@
   // recorded for the same uid, but recording that
   // unused detail in our database is too burdensome.
   bool verbose = true;
-  while ((*cp != '#') && (*cp != '\n')) ++cp;
+  while (*cp && (*cp != '#') && (*cp != '\n')) ++cp;
   if (*cp == '#') {
     do {
       ++cp;
@@ -310,11 +317,11 @@
     verbose = !!fastcmp<strncmp>(cp, "uid=", strlen("uid="));
   }
 
-  while (*cp != '\n') ++cp;
+  while (*cp && (*cp != '\n')) ++cp;
 #ifdef DEBUG
-  fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - *pData), *pData);
+  fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - pData), pData);
 #endif
-  *pData = cp;
+  pData = cp;
 
   if (lineNum) {
     if (map->emplaceUnique(tagIndex,
@@ -342,9 +349,9 @@
 
 // Parse the tags out of the file.
 static int parseMapLines(EventTagMap* map, size_t which) {
-  char* cp = static_cast<char*>(map->mapAddr[which]);
+  const char* cp = static_cast<char*>(map->mapAddr[which]);
   size_t len = map->mapLen[which];
-  char* endp = cp + len;
+  const char* endp = cp + len;
 
   // insist on EOL at EOF; simplifies parsing and null-termination
   if (!len || (*(endp - 1) != '\n')) {
@@ -371,7 +378,7 @@
         lineStart = false;
       } else if (isdigit(*cp)) {
         // looks like a tag; scan it out
-        if (scanTagLine(map, &cp, lineNum) != 0) {
+        if (scanTagLine(map, cp, lineNum) != 0) {
           if (!which || (errno != EMLINK)) {
             return -1;
           }
@@ -446,7 +453,7 @@
           mmap(NULL, end[which], which ? PROT_READ : PROT_READ | PROT_WRITE,
                which ? MAP_SHARED : MAP_PRIVATE, fd[which], 0);
       save_errno = errno;
-      close(fd[which]);
+      close(fd[which]); /* fd DONE */
       fd[which] = -1;
       if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
           (newTagMap->mapAddr[which] != NULL)) {
@@ -466,6 +473,7 @@
       delete newTagMap;
       return NULL;
     }
+    /* See 'fd DONE' comments above and below, no need to clean up here */
   }
 
   return newTagMap;
@@ -474,7 +482,7 @@
   save_errno = EINVAL;
   delete newTagMap;
 fail_close:
-  for (which = 0; which < NUM_MAPS; ++which) close(fd[which]);
+  for (which = 0; which < NUM_MAPS; ++which) close(fd[which]); /* fd DONE */
 fail_errno:
   errno = save_errno;
   return NULL;
@@ -495,14 +503,13 @@
   int ret = asprintf(&buf, command_template, tag);
   if (ret > 0) {
     // Add some buffer margin for an estimate of the full return content.
-    char* cp;
     size_t size =
         ret - strlen(command_template) +
         strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
     if (size > (size_t)ret) {
-      cp = static_cast<char*>(realloc(buf, size));
-      if (cp) {
-        buf = cp;
+      char* np = static_cast<char*>(realloc(buf, size));
+      if (np) {
+        buf = np;
       } else {
         size = ret;
       }
@@ -512,10 +519,12 @@
     // Ask event log tag service for an existing entry
     if (__send_log_msg(buf, size) >= 0) {
       buf[size - 1] = '\0';
-      unsigned long val = strtoul(buf, &cp, 10);        // return size
+      char* ep;
+      unsigned long val = strtoul(buf, &ep, 10);  // return size
+      const char* cp = ep;
       if ((buf != cp) && (val > 0) && (*cp == '\n')) {  // truncation OK
         ++cp;
-        if (!scanTagLine(map, &cp, 0)) {
+        if (!scanTagLine(map, cp, 0)) {
           free(buf);
           return map->find(tag);
         }
@@ -573,8 +582,9 @@
 LIBLOG_ABI_PUBLIC int android_lookupEventTagNum(EventTagMap* map,
                                                 const char* tagname,
                                                 const char* format, int prio) {
-  size_t len = strlen(tagname);
-  if (!len) {
+  const char* ep = endOfTag(tagname);
+  size_t len = ep - tagname;
+  if (!len || *ep) {
     errno = EINVAL;
     return -1;
   }
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index ae7a334..1483c24 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -15,9 +15,7 @@
  */
 /*
  * Intercepts log messages intended for the Android log device.
- * When running in the context of the simulator, the messages are
- * passed on to the underlying (fake) log device.  When not in the
- * simulator, messages are printed to stderr.
+ * Messages are printed to stderr.
  */
 #include <ctype.h>
 #include <errno.h>
@@ -561,7 +559,8 @@
  *  tag (N bytes -- null-terminated ASCII string)
  *  message (N bytes -- null-terminated ASCII string)
  */
-static ssize_t logWritev(int fd, const struct iovec* vector, int count) {
+LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
+                                    int count) {
   LogState* state;
 
   /* Make sure that no-one frees the LogState while we're using it.
@@ -626,8 +625,18 @@
 
 /*
  * Free up our state and close the fake descriptor.
+ *
+ * The logger API has no means or need to 'stop' or 'close' using the logs,
+ * and as such, there is no way for that 'stop' or 'close' to translate into
+ * a close operation to the fake log handler. fakeLogClose is provided for
+ * completeness only.
+ *
+ * We have no intention of adding a log close operation as it would complicate
+ * every user of the logging API with no gain since the only valid place to
+ * call is in the exit handler. Logging can continue in the exit handler to
+ * help debug HOST tools ...
  */
-static int logClose(int fd) {
+LIBLOG_HIDDEN int fakeLogClose(int fd) {
   deleteFakeFd(fd);
   return 0;
 }
@@ -635,7 +644,7 @@
 /*
  * Open a log output device and return a fake fd.
  */
-static int logOpen(const char* pathName, int flags __unused) {
+LIBLOG_HIDDEN int fakeLogOpen(const char* pathName) {
   LogState* logState;
   int fd = -1;
 
@@ -654,66 +663,6 @@
   return fd;
 }
 
-/*
- * Runtime redirection.  If this binary is running in the simulator,
- * just pass log messages to the emulated device.  If it's running
- * outside of the simulator, write the log messages to stderr.
- */
-
-static int (*redirectOpen)(const char* pathName, int flags) = NULL;
-static int (*redirectClose)(int fd) = NULL;
-static ssize_t (*redirectWritev)(int fd, const struct iovec* vector,
-                                 int count) = NULL;
-
-static void setRedirects() {
-  const char* ws;
-
-  /* Wrapsim sets this environment variable on children that it's
-   * created using its LD_PRELOAD wrapper.
-   */
-  ws = getenv("ANDROID_WRAPSIM");
-  if (ws != NULL && strcmp(ws, "1") == 0) {
-    /* We're running inside wrapsim, so we can just write to the device. */
-    redirectOpen = (int (*)(const char* pathName, int flags))open;
-    redirectClose = close;
-    redirectWritev = writev;
-  } else {
-    /* There's no device to delegate to; handle the logging ourselves. */
-    redirectOpen = logOpen;
-    redirectClose = logClose;
-    redirectWritev = logWritev;
-  }
-}
-
-LIBLOG_HIDDEN int fakeLogOpen(const char* pathName, int flags) {
-  if (redirectOpen == NULL) {
-    setRedirects();
-  }
-  return redirectOpen(pathName, flags);
-}
-
-/*
- * The logger API has no means or need to 'stop' or 'close' using the logs,
- * and as such, there is no way for that 'stop' or 'close' to translate into
- * a close operation to the fake log handler. fakeLogClose is provided for
- * completeness only.
- *
- * We have no intention of adding a log close operation as it would complicate
- * every user of the logging API with no gain since the only valid place to
- * call is in the exit handler. Logging can continue in the exit handler to
- * help debug HOST tools ...
- */
-LIBLOG_HIDDEN int fakeLogClose(int fd) {
-  /* Assume that open() was called first. */
-  return redirectClose(fd);
-}
-
-LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
-                                    int count) {
-  /* Assume that open() was called first. */
-  return redirectWritev(fd, vector, count);
-}
-
 LIBLOG_HIDDEN ssize_t __send_log_msg(char* buf __unused,
                                      size_t buf_size __unused) {
   return -ENODEV;
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index 462d026..7b0e745 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -23,7 +23,7 @@
 
 struct iovec;
 
-LIBLOG_HIDDEN int fakeLogOpen(const char* pathName, int flags);
+LIBLOG_HIDDEN int fakeLogOpen(const char* pathName);
 LIBLOG_HIDDEN int fakeLogClose(int fd);
 LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
                                     int count);
diff --git a/liblog/fake_writer.c b/liblog/fake_writer.c
index 969661a..403dc72 100644
--- a/liblog/fake_writer.c
+++ b/liblog/fake_writer.c
@@ -55,9 +55,9 @@
       continue;
     }
     snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
-    logFds[i] = fakeLogOpen(buf, O_WRONLY);
+    logFds[i] = fakeLogOpen(buf);
     if (logFds[i] < 0) {
-      fprintf(stderr, "fakeLogOpen(%s, O_WRONLY) failed\n", buf);
+      fprintf(stderr, "fakeLogOpen(%s) failed\n", buf);
     }
   }
   return 0;
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 6758c84..3a215e9 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -32,6 +32,7 @@
 #include <log/log_main.h>
 #include <log/log_radio.h>
 #include <log/log_read.h>
+#include <log/log_safetynet.h>
 #include <log/log_system.h>
 #include <log/log_time.h>
 #include <log/uio.h> /* helper to define iovec for portability */
@@ -167,31 +168,6 @@
 
 /* --------------------------------------------------------------------- */
 
-#ifndef _ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
-
-#define android_errorWriteLog(tag, subTag) \
-  __android_log_error_write(tag, subTag, -1, NULL, 0)
-
-#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \
-  __android_log_error_write(tag, subTag, uid, data, dataLen)
-
-int __android_log_error_write(int tag, const char* subTag, int32_t uid,
-                              const char* data, uint32_t dataLen);
-
-#endif /* __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE */
-
-/* --------------------------------------------------------------------- */
-
 #ifndef __ANDROID_USE_LIBLOG_CLOSE_INTERFACE
 #ifndef __ANDROID_API__
 #define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 1
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index 55953fc..bb1ce34 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -17,6 +17,7 @@
 #ifndef _LIBS_LOG_EVENT_LIST_H
 #define _LIBS_LOG_EVENT_LIST_H
 
+#include <errno.h>
 #include <stdint.h>
 
 #if (defined(__cplusplus) && defined(_USING_LIBCXX))
@@ -148,6 +149,7 @@
     return ctx;
   }
 
+  /* return errors or transmit status */
   int status() const {
     return ret;
   }
@@ -175,6 +177,12 @@
     return *this;
   }
 
+  android_log_event_list& operator<<(bool value) {
+    int retval = android_log_write_int32(ctx, value ? 1 : 0);
+    if (retval < 0) ret = retval;
+    return *this;
+  }
+
   android_log_event_list& operator<<(int64_t value) {
     int retval = android_log_write_int64(ctx, value);
     if (retval < 0) ret = retval;
@@ -209,14 +217,16 @@
   }
 
   int write(log_id_t id = LOG_ID_EVENTS) {
+    /* facilitate -EBUSY retry */
+    if ((ret == -EBUSY) || (ret > 0)) ret = 0;
     int retval = android_log_write_list(ctx, id);
-    if (retval < 0) ret = retval;
+    /* existing errors trump transmission errors */
+    if (!ret) ret = retval;
     return ret;
   }
 
   int operator<<(log_id_t id) {
-    int retval = android_log_write_list(ctx, id);
-    if (retval < 0) ret = retval;
+    write(id);
     android_log_destroy(&ctx);
     return ret;
   }
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
new file mode 100644
index 0000000..b8ca475
--- /dev/null
+++ b/liblog/include/log/log_safetynet.h
@@ -0,0 +1,44 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** This file is dual licensed.  It may be redistributed and/or modified
+** under the terms of the Apache 2.0 License OR version 2 of the GNU
+** General Public License.
+*/
+
+#ifndef _LIBS_LOG_SAFETYNET_H
+#define _LIBS_LOG_SAFETYNET_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
+#elif __ANDROID_API__ > 22 /* > Lollipop */
+#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
+
+#define android_errorWriteLog(tag, subTag) \
+  __android_log_error_write(tag, subTag, -1, NULL, 0)
+
+#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \
+  __android_log_error_write(tag, subTag, uid, data, dataLen)
+
+int __android_log_error_write(int tag, const char* subTag, int32_t uid,
+                              const char* data, uint32_t dataLen);
+
+#endif /* __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_SAFETYNET_H */
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 9ece0b3..3764faf 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -22,6 +22,8 @@
 
 /* struct log_time is a wire-format variant of struct timespec */
 #define NS_PER_SEC 1000000000ULL
+#define US_PER_SEC 1000000ULL
+#define MS_PER_SEC 1000ULL
 
 #ifndef __struct_log_time_defined
 #define __struct_log_time_defined
@@ -148,6 +150,14 @@
   uint64_t nsec() const {
     return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
   }
+  uint64_t usec() const {
+    return static_cast<uint64_t>(tv_sec) * US_PER_SEC +
+           tv_nsec / (NS_PER_SEC / US_PER_SEC);
+  }
+  uint64_t msec() const {
+    return static_cast<uint64_t>(tv_sec) * MS_PER_SEC +
+           tv_nsec / (NS_PER_SEC / MS_PER_SEC);
+  }
 
 #ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
   static const char default_format[];
diff --git a/liblog/include_vndk/log/log.h b/liblog/include_vndk/log/log.h
index f93b377..01623df 100644
--- a/liblog/include_vndk/log/log.h
+++ b/liblog/include_vndk/log/log.h
@@ -8,6 +8,7 @@
 #include <log/log_main.h>
 #include <log/log_radio.h>
 #include <log/log_read.h>
+#include <log/log_safetynet.h>
 #include <log/log_time.h>
 
 /*
diff --git a/liblog/include_vndk/log/log_safetynet.h b/liblog/include_vndk/log/log_safetynet.h
new file mode 120000
index 0000000..a4614e7
--- /dev/null
+++ b/liblog/include_vndk/log/log_safetynet.h
@@ -0,0 +1 @@
+../../include/log/log_safetynet.h
\ No newline at end of file
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 58fb148..3c4c1f1 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -33,6 +33,7 @@
     android_logger_get_prune_list; # vndk
     android_logger_set_prune_list; # vndk
     android_logger_get_statistics; # vndk
+    __android_log_error_write; # vndk
     __android_log_is_loggable;
 };
 
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index d322c0f..84feb20 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -410,16 +410,46 @@
   if (!tag) tag = "";
 
   /* XXX: This needs to go! */
-  if ((bufID != LOG_ID_RADIO) &&
-      (!strcmp(tag, "HTC_RIL") ||
-       !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
-       !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
-       !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") ||
-       !strcmp(tag, "CDMA") || !strcmp(tag, "PHONE") || !strcmp(tag, "SMS"))) {
-    bufID = LOG_ID_RADIO;
-    /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
-    snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
-    tag = tmp_tag;
+  if (bufID != LOG_ID_RADIO) {
+    switch (tag[0]) {
+      case 'H':
+        if (strcmp(tag + 1, "HTC_RIL" + 1)) break;
+        goto inform;
+      case 'R':
+        /* Any log tag with "RIL" as the prefix */
+        if (strncmp(tag + 1, "RIL" + 1, strlen("RIL") - 1)) break;
+        goto inform;
+      case 'Q':
+        /* Any log tag with "QC_RIL" as the prefix */
+        if (strncmp(tag + 1, "QC_RIL" + 1, strlen("QC_RIL") - 1)) break;
+        goto inform;
+      case 'I':
+        /* Any log tag with "IMS" as the prefix */
+        if (strncmp(tag + 1, "IMS" + 1, strlen("IMS") - 1)) break;
+        goto inform;
+      case 'A':
+        if (strcmp(tag + 1, "AT" + 1)) break;
+        goto inform;
+      case 'G':
+        if (strcmp(tag + 1, "GSM" + 1)) break;
+        goto inform;
+      case 'S':
+        if (strcmp(tag + 1, "STK" + 1) && strcmp(tag + 1, "SMS" + 1)) break;
+        goto inform;
+      case 'C':
+        if (strcmp(tag + 1, "CDMA" + 1)) break;
+        goto inform;
+      case 'P':
+        if (strcmp(tag + 1, "PHONE" + 1)) break;
+      /* FALLTHRU */
+      inform:
+        bufID = LOG_ID_RADIO;
+        snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
+        tag = tmp_tag;
+      /* FALLTHRU */
+      default:
+        break;
+    }
   }
 
 #if __BIONIC__
diff --git a/liblog/logprint.c b/liblog/logprint.c
index 2ade7b0..b62f8b4 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -326,6 +326,7 @@
   else if (!strcmp(formatString, "uid")) format = FORMAT_MODIFIER_UID;
   else if (!strcmp(formatString, "descriptive")) format = FORMAT_MODIFIER_DESCRIPT;
   /* clang-format on */
+
 #ifndef __MINGW32__
   else {
     extern char* tzname[2];
@@ -637,7 +638,8 @@
   TYPE_MILLISECONDS = '3',
   TYPE_ALLOCATIONS = '4',
   TYPE_ID = '5',
-  TYPE_PERCENT = '6'
+  TYPE_PERCENT = '6',
+  TYPE_MONOTONIC = 's'
 };
 
 static int android_log_printBinaryEvent(const unsigned char** pEventData,
@@ -651,7 +653,7 @@
   size_t outBufLen = *pOutBufLen;
   size_t outBufLenSave = outBufLen;
   unsigned char type;
-  size_t outCount;
+  size_t outCount = 0;
   int result = 0;
   const char* cp;
   size_t len;
@@ -690,6 +692,7 @@
    * 4: Number of allocations
    * 5: Id
    * 6: Percent
+   * s: Number of seconds (monotonic time)
    * Default value for data of type int/long is 2 (bytes).
    */
   if (!cp || !findChar(&cp, &len, '(')) {
@@ -921,6 +924,42 @@
             outCount = snprintf(outBuf, outBufLen, "ms");
           }
           break;
+        case TYPE_MONOTONIC: {
+          static const uint64_t minute = 60;
+          static const uint64_t hour = 60 * minute;
+          static const uint64_t day = 24 * hour;
+
+          /* Repaint as unsigned seconds, minutes, hours ... */
+          outBuf -= outCount;
+          outBufLen += outCount;
+          uint64_t val = lval;
+          if (val >= day) {
+            outCount = snprintf(outBuf, outBufLen, "%" PRIu64 "d ", val / day);
+            if (outCount >= outBufLen) break;
+            outBuf += outCount;
+            outBufLen -= outCount;
+            val = (val % day) + day;
+          }
+          if (val >= minute) {
+            if (val >= hour) {
+              outCount = snprintf(outBuf, outBufLen, "%" PRIu64 ":",
+                                  (val / hour) % (day / hour));
+              if (outCount >= outBufLen) break;
+              outBuf += outCount;
+              outBufLen -= outCount;
+            }
+            outCount =
+                snprintf(outBuf, outBufLen,
+                         (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
+                         (val / minute) % (hour / minute));
+            if (outCount >= outBufLen) break;
+            outBuf += outCount;
+            outBufLen -= outCount;
+          }
+          outCount = snprintf(outBuf, outBufLen,
+                              (val >= minute) ? "%02" PRIu64 : "%" PRIu64 "s",
+                              val % minute);
+        } break;
         case TYPE_ALLOCATIONS:
           outCount = 0;
           /* outCount = snprintf(outBuf, outBufLen, " allocations"); */
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index 0e6432c..ab96429 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -55,6 +55,7 @@
     -fno-builtin \
 
 test_src_files := \
+    libc_test.cpp \
     liblog_test_default.cpp \
     liblog_test_local.cpp \
     liblog_test_stderr.cpp \
@@ -65,14 +66,6 @@
     log_system_test.cpp \
     log_time_test.cpp
 
-# to prevent breaking the build if bionic not relatively visible to us
-ifneq ($(wildcard $(LOCAL_PATH)/../../../../bionic/libc/bionic/libc_logging.cpp),)
-
-test_src_files += \
-    libc_test.cpp
-
-endif
-
 # Build tests for the device (with .so). Run with:
 #   adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
 include $(CLEAR_VARS)
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 3f79552..c4bf959 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -527,11 +527,13 @@
 /*
  *	Measure the time it takes to submit the android event logging call
  * using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
- * under light load. Expect this to be a dozen or so syscall periods (40us)
+ * under light load. Expect this to be a long path to logger to convert the
+ * unknown tag (0) into a tagname (less than 200us).
  */
 static void BM_log_event_overhead(int iters) {
   for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
     StartBenchmarkTiming();
+    // log tag number 0 is not known, nor shall it ever be known
     __android_log_btwrite(0, EVENT_TYPE_LONG, &i, sizeof(i));
     StopBenchmarkTiming();
     logd_yield();
@@ -539,6 +541,28 @@
 }
 BENCHMARK(BM_log_event_overhead);
 
+/*
+ *	Measure the time it takes to submit the android event logging call
+ * using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
+ * under light load with a known logtag.  Expect this to be a dozen or so
+ * syscall periods (less than 40us)
+ */
+static void BM_log_event_overhead_42(int iters) {
+  for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
+    StartBenchmarkTiming();
+    // In system/core/logcat/event.logtags:
+    // # These are used for testing, do not modify without updating
+    // # tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
+    // # system/core/liblog/tests/liblog_benchmark.cpp
+    // # system/core/liblog/tests/liblog_test.cpp
+    // 42    answer (to life the universe etc|3)
+    __android_log_btwrite(42, EVENT_TYPE_LONG, &i, sizeof(i));
+    StopBenchmarkTiming();
+    logd_yield();
+  }
+}
+BENCHMARK(BM_log_event_overhead_42);
+
 static void BM_log_event_overhead_null(int iters) {
   set_log_null();
   BM_log_event_overhead(iters);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 71f74ab..46ec5ef 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1720,6 +1720,7 @@
 // Kills logd and toss all collected data, equivalent to logcat -b all -c,
 // except we also return errors to the logging callers.
 #ifdef USING_LOGGER_DEFAULT
+#ifdef __ANDROID__
 #ifdef TEST_PREFIX
 // helper to liblog.enoent to count end-to-end matching logging messages.
 static int count_matching_ts(log_time ts) {
@@ -1832,7 +1833,8 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
-#endif  // USING_LOCAL_LOGD
+#endif  // __ANDROID__
+#endif  // USING_LOGGER_DEFAULT
 
 // Below this point we run risks of setuid(AID_SYSTEM) which may affect others.
 
diff --git a/libmemunreachable/.clang-format b/libmemunreachable/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libmemunreachable/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 1b8830a..cdac76b 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -9,13 +9,20 @@
     clang: true,
     shared_libs: [
         "libbase",
-        "liblog",
     ],
+
+    target: {
+        android: {
+            static_libs: ["libasync_safe"],
+        },
+        host: {
+            shared_libs: ["liblog"],
+        }
+    },
 }
 
 cc_library_shared {
     name: "libmemunreachable",
-    vendor_available: true,
     defaults: ["libmemunreachable_defaults"],
     srcs: [
         "Allocator.cpp",
@@ -31,7 +38,6 @@
 
     static_libs: [
         "libc_malloc_debug_backtrace",
-        "libc_logging",
     ],
     // Only need this for arm since libc++ uses its own unwind code that
     // doesn't mix with the other default unwind code.
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index 62366f2..c365ae5 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -42,11 +42,9 @@
   } else {
     Range overlap = inserted.first->first;
     if (overlap != range) {
-      ALOGE("range %p-%p overlaps with existing range %p-%p",
-          reinterpret_cast<void*>(begin),
-          reinterpret_cast<void*>(end),
-          reinterpret_cast<void*>(overlap.begin),
-          reinterpret_cast<void*>(overlap.end));
+      MEM_ALOGE("range %p-%p overlaps with existing range %p-%p", reinterpret_cast<void*>(begin),
+                reinterpret_cast<void*>(end), reinterpret_cast<void*>(overlap.begin),
+                reinterpret_cast<void*>(overlap.end));
     }
     return false;
   }
@@ -154,7 +152,7 @@
 
   void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
   if (ret == MAP_FAILED) {
-    ALOGE("failed to map page at %p: %s", page, strerror(errno));
+    MEM_ALOGE("failed to map page at %p: %s", page, strerror(errno));
     return false;
   }
 
@@ -167,7 +165,7 @@
     handler.reset();
     return;
   }
-  ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
+  MEM_ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
   if (!MapOverPage(si->si_addr)) {
     handler.reset();
   }
diff --git a/libmemunreachable/LeakPipe.cpp b/libmemunreachable/LeakPipe.cpp
index 080f8a7..78117e2 100644
--- a/libmemunreachable/LeakPipe.cpp
+++ b/libmemunreachable/LeakPipe.cpp
@@ -44,11 +44,11 @@
 
   int ret = sendmsg(sock, &hdr, 0);
   if (ret < 0) {
-    ALOGE("failed to send fd: %s", strerror(errno));
+    MEM_ALOGE("failed to send fd: %s", strerror(errno));
     return false;
   }
   if (ret == 0) {
-    ALOGE("eof when sending fd");
+    MEM_ALOGE("eof when sending fd");
     return false;
   }
 
@@ -71,17 +71,17 @@
 
   int ret = recvmsg(sock, &hdr, 0);
   if (ret < 0) {
-    ALOGE("failed to receive fd: %s", strerror(errno));
+    MEM_ALOGE("failed to receive fd: %s", strerror(errno));
     return -1;
   }
   if (ret == 0) {
-    ALOGE("eof when receiving fd");
+    MEM_ALOGE("eof when receiving fd");
     return -1;
   }
 
   struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
   if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
-    ALOGE("missing fd while receiving fd");
+    MEM_ALOGE("missing fd while receiving fd");
     return -1;
   }
 
diff --git a/libmemunreachable/LeakPipe.h b/libmemunreachable/LeakPipe.h
index 3f4e0b7..3ea2d8f 100644
--- a/libmemunreachable/LeakPipe.h
+++ b/libmemunreachable/LeakPipe.h
@@ -36,7 +36,7 @@
   LeakPipe() {
     int ret = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sv_);
     if (ret < 0) {
-      LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
+      MEM_LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
     }
   }
 
@@ -105,10 +105,10 @@
     bool Send(const T& value) {
       ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, &value, sizeof(T)));
       if (ret < 0) {
-        ALOGE("failed to send value: %s", strerror(errno));
+        MEM_ALOGE("failed to send value: %s", strerror(errno));
         return false;
       } else if (static_cast<size_t>(ret) != sizeof(T)) {
-        ALOGE("eof while writing value");
+        MEM_ALOGE("eof while writing value");
         return false;
       }
 
@@ -124,10 +124,10 @@
 
       ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, vector.data(), size));
       if (ret < 0) {
-        ALOGE("failed to send vector: %s", strerror(errno));
+        MEM_ALOGE("failed to send vector: %s", strerror(errno));
         return false;
       } else if (static_cast<size_t>(ret) != size) {
-        ALOGE("eof while writing vector");
+        MEM_ALOGE("eof while writing vector");
         return false;
       }
 
@@ -143,10 +143,10 @@
     bool Receive(T* value) {
       ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, reinterpret_cast<void*>(value), sizeof(T)));
       if (ret < 0) {
-        ALOGE("failed to receive value: %s", strerror(errno));
+        MEM_ALOGE("failed to receive value: %s", strerror(errno));
         return false;
       } else if (static_cast<size_t>(ret) != sizeof(T)) {
-        ALOGE("eof while receiving value");
+        MEM_ALOGE("eof while receiving value");
         return false;
       }
 
@@ -166,10 +166,10 @@
       while (size > 0) {
         ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, ptr, size));
         if (ret < 0) {
-          ALOGE("failed to send vector: %s", strerror(errno));
+          MEM_ALOGE("failed to send vector: %s", strerror(errno));
           return false;
         } else if (ret == 0) {
-          ALOGE("eof while reading vector");
+          MEM_ALOGE("eof while reading vector");
           return false;
         }
         size -= ret;
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index ac19a66..1c84744 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -78,51 +78,49 @@
 
 bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads,
     const allocator::vector<Mapping>& mappings) {
-  ALOGI("searching process %d for allocations", pid_);
+  MEM_ALOGI("searching process %d for allocations", pid_);
   allocator::vector<Mapping> heap_mappings{mappings};
   allocator::vector<Mapping> anon_mappings{mappings};
   allocator::vector<Mapping> globals_mappings{mappings};
   allocator::vector<Mapping> stack_mappings{mappings};
-  if (!ClassifyMappings(mappings, heap_mappings, anon_mappings,
-      globals_mappings, stack_mappings)) {
+  if (!ClassifyMappings(mappings, heap_mappings, anon_mappings, globals_mappings, stack_mappings)) {
     return false;
   }
 
   for (auto it = heap_mappings.begin(); it != heap_mappings.end(); it++) {
-    ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
-    HeapIterate(*it, [&](uintptr_t base, size_t size) {
-      heap_walker_.Allocation(base, base + size);
-    });
+    MEM_ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+    HeapIterate(*it,
+                [&](uintptr_t base, size_t size) { heap_walker_.Allocation(base, base + size); });
   }
 
   for (auto it = anon_mappings.begin(); it != anon_mappings.end(); it++) {
-    ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+    MEM_ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
     heap_walker_.Allocation(it->begin, it->end);
   }
 
   for (auto it = globals_mappings.begin(); it != globals_mappings.end(); it++) {
-    ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+    MEM_ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
     heap_walker_.Root(it->begin, it->end);
   }
 
   for (auto thread_it = threads.begin(); thread_it != threads.end(); thread_it++) {
     for (auto it = stack_mappings.begin(); it != stack_mappings.end(); it++) {
       if (thread_it->stack.first >= it->begin && thread_it->stack.first <= it->end) {
-        ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
+        MEM_ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
         heap_walker_.Root(thread_it->stack.first, it->end);
       }
     }
     heap_walker_.Root(thread_it->regs);
   }
 
-  ALOGI("searching done");
+  MEM_ALOGI("searching done");
 
   return true;
 }
 
 bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks,
     size_t limit, size_t* num_leaks, size_t* leak_bytes) {
-  ALOGI("sweeping process %d for unreachable memory", pid_);
+  MEM_ALOGI("sweeping process %d for unreachable memory", pid_);
   leaks.clear();
 
   if (!heap_walker_.DetectLeaks()) {
@@ -133,9 +131,9 @@
   allocator::vector<Range> leaked1{allocator_};
   heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes);
 
-  ALOGI("sweeping done");
+  MEM_ALOGI("sweeping done");
 
-  ALOGI("folding related leaks");
+  MEM_ALOGI("folding related leaks");
 
   LeakFolding folding(allocator_, heap_walker_);
   if (!folding.FoldLeaks()) {
@@ -188,7 +186,7 @@
         std::min(leak->size, Leak::contents_length));
   }
 
-  ALOGI("folding done");
+  MEM_ALOGI("folding done");
 
   std::sort(leaks.begin(), leaks.end(), [](const Leak& a, const Leak& b) {
     return a.total_size > b.total_size;
@@ -276,7 +274,7 @@
     /////////////////////////////////////////////
     // Collection thread
     /////////////////////////////////////////////
-    ALOGI("collecting thread info for process %d...", parent_pid);
+    MEM_ALOGI("collecting thread info for process %d...", parent_pid);
 
     ThreadCapture thread_capture(parent_pid, heap);
     allocator::vector<ThreadInfo> thread_info(heap);
@@ -351,7 +349,7 @@
     } else {
       // Nothing left to do in the collection thread, return immediately,
       // releasing all the captured threads.
-      ALOGI("collection thread done");
+      MEM_ALOGI("collection thread done");
       return 0;
     }
   }};
@@ -397,10 +395,10 @@
     return false;
   }
 
-  ALOGI("unreachable memory detection done");
-  ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
-      info.leak_bytes, info.num_leaks, plural(info.num_leaks),
-      info.allocation_bytes, info.num_allocations, plural(info.num_allocations));
+  MEM_ALOGI("unreachable memory detection done");
+  MEM_ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
+            info.leak_bytes, info.num_leaks, plural(info.num_leaks), info.allocation_bytes,
+            info.num_allocations, plural(info.num_allocations));
   return true;
 }
 
@@ -504,7 +502,10 @@
 std::string GetUnreachableMemoryString(bool log_contents, size_t limit) {
   UnreachableMemoryInfo info;
   if (!GetUnreachableMemory(info, limit)) {
-    return "Failed to get unreachable memory\n";
+    return "Failed to get unreachable memory\n"
+           "If you are trying to get unreachable memory from a system app\n"
+           "(like com.android.systemui), disable selinux first using\n"
+           "setenforce 0\n";
   }
 
   return info.ToString(log_contents);
@@ -517,7 +518,7 @@
   }
 
   for (auto it = info.leaks.begin(); it != info.leaks.end(); it++) {
-    ALOGE("%s", it->ToString(log_contents).c_str());
+    MEM_ALOGE("%s", it->ToString(log_contents).c_str());
   }
   return true;
 }
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
index 4e3c41e..73b0493 100644
--- a/libmemunreachable/PtracerThread.cpp
+++ b/libmemunreachable/PtracerThread.cpp
@@ -70,7 +70,7 @@
     child_pid_(0) {
   stack_ = std::make_unique<Stack>(PTHREAD_STACK_MIN);
   if (stack_->top() == nullptr) {
-    LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
+    MEM_LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
   }
 
   func_ = std::function<int()>{[&, func]() -> int {
@@ -102,7 +102,7 @@
        CLONE_VM|CLONE_FS|CLONE_FILES/*|CLONE_UNTRACED*/,
        reinterpret_cast<void*>(&func_));
   if (child_pid_ < 0) {
-    ALOGE("failed to clone child: %s", strerror(errno));
+    MEM_ALOGE("failed to clone child: %s", strerror(errno));
     return false;
   }
 
@@ -120,7 +120,7 @@
   int status;
   int ret = TEMP_FAILURE_RETRY(waitpid(child_pid_, &status, __WALL));
   if (ret < 0) {
-    ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
+    MEM_ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
     return -1;
   }
 
@@ -131,7 +131,7 @@
   } else if (WIFSIGNALED(status)) {
     return -WTERMSIG(status);
   } else {
-    ALOGE("unexpected status %x", status);
+    MEM_ALOGE("unexpected status %x", status);
     return -1;
   }
 }
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
index 9beef9a..7f44953 100644
--- a/libmemunreachable/ScopedPipe.h
+++ b/libmemunreachable/ScopedPipe.h
@@ -26,7 +26,7 @@
   ScopedPipe() : pipefd_{-1, -1} {
     int ret = pipe2(pipefd_, O_CLOEXEC);
     if (ret < 0) {
-      LOG_ALWAYS_FATAL("failed to open pipe");
+      MEM_LOG_ALWAYS_FATAL("failed to open pipe");
     }
   }
   ~ScopedPipe() {
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index 1fd9d4d..ada2ae4 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -37,22 +37,18 @@
 
   template <class F>
   void install(int signal, F&& f) {
-    LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");
+    MEM_LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");
 
     handler_ = SignalFn(std::allocator_arg, allocator_,
-        [=](int signal, siginfo_t* si, void* uctx) {
-          f(*this, signal, si, uctx);
-        });
+                        [=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
 
-    struct sigaction act{};
-    act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) {
-      handler_(signal, si, uctx);
-    };
+    struct sigaction act {};
+    act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) { handler_(signal, si, uctx); };
     act.sa_flags = SA_SIGINFO;
 
     int ret = sigaction(signal, &act, &old_act_);
     if (ret < 0) {
-      LOG_ALWAYS_FATAL("failed to install segfault handler: %s", strerror(errno));
+      MEM_LOG_ALWAYS_FATAL("failed to install segfault handler: %s", strerror(errno));
     }
 
     signal_ = signal;
@@ -62,7 +58,7 @@
     if (signal_ != -1) {
       int ret = sigaction(signal_, &old_act_, NULL);
       if (ret < 0) {
-        ALOGE("failed to uninstall segfault handler");
+        MEM_ALOGE("failed to uninstall segfault handler");
       }
       handler_ = SignalFn{};
       signal_ = -1;
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
index 9155c29..3891f2d 100644
--- a/libmemunreachable/ThreadCapture.cpp
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -110,7 +110,7 @@
 
   android::base::unique_fd fd(open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
   if (fd == -1) {
-    ALOGE("failed to open %s: %s", path, strerror(errno));
+    MEM_ALOGE("failed to open %s: %s", path, strerror(errno));
     return false;
   }
 
@@ -126,7 +126,7 @@
   do {
     nread = syscall(SYS_getdents64, fd.get(), dirent_buf, sizeof(dirent_buf));
     if (nread < 0) {
-      ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
+      MEM_ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
       return false;
     } else if (nread > 0) {
       ssize_t off = 0;
@@ -177,8 +177,7 @@
 void ThreadCaptureImpl::PtraceDetach(pid_t tid, unsigned int signal) {
   void* sig_ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(signal));
   if (ptrace(PTRACE_DETACH, tid, NULL, sig_ptr) < 0 && errno != ESRCH) {
-    ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_,
-        strerror(errno));
+    MEM_ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_, strerror(errno));
   }
 }
 
@@ -187,8 +186,7 @@
 int ThreadCaptureImpl::PtraceAttach(pid_t tid) {
   int ret = ptrace(PTRACE_SEIZE, tid, NULL, NULL);
   if (ret < 0) {
-    ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_,
-        strerror(errno));
+    MEM_ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_, strerror(errno));
     return -1;
   }
 
@@ -200,8 +198,7 @@
     if (errno == ESRCH) {
       return 0;
     } else {
-      ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_,
-          strerror(errno));
+      MEM_ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_, strerror(errno));
       PtraceDetach(tid, 0);
       return -1;
     }
@@ -219,8 +216,7 @@
   iovec.iov_len = sizeof(regs);
 
   if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iovec)) {
-    ALOGE("ptrace getregset for thread %d of process %d failed: %s",
-        tid, pid_, strerror(errno));
+    MEM_ALOGE("ptrace getregset for thread %d of process %d failed: %s", tid, pid_, strerror(errno));
     return false;
   }
 
@@ -258,15 +254,13 @@
 
   int status = 0;
   if (TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)) < 0) {
-    ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_,
-        strerror(errno));
+    MEM_ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_, strerror(errno));
     PtraceDetach(tid, 0);
     return -1;
   }
 
   if (!WIFSTOPPED(status)) {
-    ALOGE("thread %d of process %d was not paused after waitpid, killed?",
-        tid, pid_);
+    MEM_ALOGE("thread %d of process %d was not paused after waitpid, killed?", tid, pid_);
     return 0;
   }
 
@@ -285,8 +279,8 @@
         // normal ptrace interrupt stop
         break;
       default:
-        ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d",
-            signal, tid, pid_);
+        MEM_ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d", signal,
+                  tid, pid_);
         return -1;
     }
   } else {
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h
index cdfbfd9..1725c53 100644
--- a/libmemunreachable/log.h
+++ b/libmemunreachable/log.h
@@ -19,6 +19,32 @@
 
 #define LOG_TAG "libmemunreachable"
 
+#if defined(__ANDROID__)
+
+#include <async_safe/log.h>
+
+#define MEM_ALOGE(...) async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGW(...) async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGI(...) async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGV(...) async_safe_format_log(ANDROID_LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__)
+
+#define MEM_LOG_ALWAYS_FATAL(...) async_safe_fatal(__VA_ARGS__)
+
+#define MEM_LOG_ALWAYS_FATAL_IF(cond, ...) \
+  ((__predict_false(cond)) ? async_safe_fatal(__VA_ARGS__) : (void)0)
+
+#else
+
 #include <log/log.h>
 
+#define MEM_ALOGW ALOGW
+#define MEM_ALOGE ALOGE
+#define MEM_ALOGV ALOGV
+#define MEM_ALOGI ALOGI
+
+#define MEM_LOG_ALWAYS_FATAL LOG_ALWAYS_FATAL
+#define MEM_LOG_ALWAYS_FATAL_IF LOG_ALWAYS_FATAL_IF
+
+#endif
+
 #endif // LIBMEMUNREACHABLE_LOG_H_
diff --git a/libmemunreachable/tests/DisableMalloc_test.cpp b/libmemunreachable/tests/DisableMalloc_test.cpp
index ea5c22c..4e6155b 100644
--- a/libmemunreachable/tests/DisableMalloc_test.cpp
+++ b/libmemunreachable/tests/DisableMalloc_test.cpp
@@ -74,7 +74,7 @@
     {
       alarm(100ms);
       ScopedDisableMalloc disable_malloc;
-      char* ptr = new(char);
+      char* ptr = new (std::nothrow)(char);
       ASSERT_NE(ptr, nullptr);
       delete(ptr);
     }
@@ -89,6 +89,8 @@
       alarm(250ms);
       ScopedDisableMalloc disable_malloc;
       delete(ptr);
+      // Force ptr usage or this code gets optimized away by the arm64 compiler.
+      ASSERT_NE(ptr, nullptr);
     }
   }, "");
 }
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
index 2ae3db8..71da365 100644
--- a/libmemunreachable/tests/MemUnreachable_test.cpp
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -23,8 +23,6 @@
 
 #include <memunreachable/memunreachable.h>
 
-void* ptr;
-
 class HiddenPointer {
  public:
   explicit HiddenPointer(size_t size = 256) {
@@ -92,10 +90,12 @@
   }
 }
 
+void* g_ptr;
+
 TEST(MemunreachableTest, global) {
   HiddenPointer hidden_ptr;
 
-  ptr = hidden_ptr.Get();
+  g_ptr = hidden_ptr.Get();
 
   {
     UnreachableMemoryInfo info;
@@ -104,7 +104,7 @@
     ASSERT_EQ(0U, info.leaks.size());
   }
 
-  ptr = NULL;
+  g_ptr = nullptr;
 
   {
     UnreachableMemoryInfo info;
@@ -126,7 +126,7 @@
 TEST(MemunreachableTest, tls) {
   HiddenPointer hidden_ptr;
   pthread_key_t key;
-  pthread_key_create(&key, NULL);
+  pthread_key_create(&key, nullptr);
 
   pthread_setspecific(key, hidden_ptr.Get());
 
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
index 41ed84e..44aabd7 100644
--- a/libmemunreachable/tests/ThreadCapture_test.cpp
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -55,7 +55,7 @@
     threads_.reserve(threads);
     tids_.reserve(threads);
     for (unsigned int i = 0; i < threads; i++) {
-      threads_.emplace_back([&, i, threads, this]() {
+      threads_.emplace_back([&, threads, this]() {
         {
           std::lock_guard<std::mutex> lk(m_);
           tids_.push_back(gettid());
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 79d6c3f..38859d1 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -23,6 +23,7 @@
         // 524291 corresponds to sysui_histogram, from
         // frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
         "-DHISTOGRAM_LOG_TAG=524292",
+        "-DCOUNT_LOG_TAG=524290",
     ],
 }
 
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index 26aa189..36e124d 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -24,13 +24,18 @@
 // buffer.
 void LogHistogram(const std::string& event, int32_t data);
 
+// Logs a Tron counter metric named |name| containing |val| count to the Tron
+// log buffer.
+void LogCounter(const std::string& name, int32_t val);
+
 // TODO: replace these with the metric_logger.proto definitions
 enum {
-  LOGBUILDER_CATEGORY = 757,
-  LOGBUILDER_NAME = 799,
-  LOGBUILDER_BUCKET = 801,
-  LOGBUILDER_VALUE = 802,
-  LOGBUILDER_HISTOGRAM = 804,
+    LOGBUILDER_CATEGORY = 757,
+    LOGBUILDER_NAME = 799,
+    LOGBUILDER_BUCKET = 801,
+    LOGBUILDER_VALUE = 802,
+    LOGBUILDER_COUNTER = 803,
+    LOGBUILDER_HISTOGRAM = 804,
 };
 
 }  // namespace metricslogger
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp
index 0d08f5c..6f65e10 100644
--- a/libmetricslogger/metrics_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -25,12 +25,16 @@
 
 // Mirror com.android.internal.logging.MetricsLogger#histogram().
 void LogHistogram(const std::string& event, int32_t data) {
-  android_log_event_list log(HISTOGRAM_LOG_TAG);
-  log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM
-      << LOGBUILDER_NAME << event
-      << LOGBUILDER_BUCKET << data
-      << LOGBUILDER_VALUE << 1
-      << LOG_ID_EVENTS;
+    android_log_event_list log(HISTOGRAM_LOG_TAG);
+    log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event
+        << LOGBUILDER_BUCKET << data << LOGBUILDER_VALUE << 1 << LOG_ID_EVENTS;
+}
+
+// Mirror com.android.internal.logging.MetricsLogger#count().
+void LogCounter(const std::string& name, int32_t val) {
+    android_log_event_list log(COUNT_LOG_TAG);
+    log << LOGBUILDER_CATEGORY << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE
+        << val << LOG_ID_EVENTS;
 }
 
 }  // namespace metricslogger
diff --git a/libmetricslogger/metrics_logger_test.cpp b/libmetricslogger/metrics_logger_test.cpp
index 5a30ad7..440645c 100644
--- a/libmetricslogger/metrics_logger_test.cpp
+++ b/libmetricslogger/metrics_logger_test.cpp
@@ -19,6 +19,10 @@
 #include <gtest/gtest.h>
 
 TEST(MetricsLoggerTest, AddSingleBootEvent) {
-  android::metricslogger::LogHistogram("test_event", 42);
-  // TODO(jhawkins): Verify the EventLog is updated.
+    android::metricslogger::LogHistogram("test_event", 42);
+    // TODO(jhawkins): Verify the EventLog is updated.
+}
+
+TEST(MetricsLoggerTest, AddCounterVal) {
+    android::metricslogger::LogCounter("test_count", 10);
 }
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 5fb56f2..377b7dd 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -22,3 +22,5 @@
         },
     },
 }
+
+subdirs = ["tests"]
\ No newline at end of file
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index cc30aaf..02b4fe7 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -577,15 +577,15 @@
 
 bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
                                         const char* anon_ns_library_path) {
-    if (NativeBridgeInitialized()) {
-        if (isCompatibleWith(NAMESPACE_VERSION)) {
-            return callbacks->initAnonymousNamespace(public_ns_sonames, anon_ns_library_path);
-        } else {
-            ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
-        }
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->initAnonymousNamespace(public_ns_sonames, anon_ns_library_path);
+    } else {
+      ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
     }
+  }
 
-    return false;
+  return false;
 }
 
 native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name,
@@ -612,15 +612,15 @@
 
 bool NativeBridgeLinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
                                 const char* shared_libs_sonames) {
-    if (NativeBridgeInitialized()) {
-        if (isCompatibleWith(NAMESPACE_VERSION)) {
-            return callbacks->linkNamespaces(from, to, shared_libs_sonames);
-        } else {
-            ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
-        }
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->linkNamespaces(from, to, shared_libs_sonames);
+    } else {
+      ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
     }
+  }
 
-    return false;
+  return false;
 }
 
 native_bridge_namespace_t* NativeBridgeGetVendorNamespace() {
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
new file mode 100644
index 0000000..efd3978
--- /dev/null
+++ b/libnativebridge/tests/Android.bp
@@ -0,0 +1,53 @@
+//
+// 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_defaults {
+    name: "libnativebridge-dummy-defaults",
+
+    host_supported: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    cppflags: ["-fvisibility=protected"],
+    target: {
+        android: {
+            shared_libs: ["libdl"],
+        },
+        host: {
+            host_ldlibs: ["-ldl"],
+        },
+    },
+}
+
+cc_library_shared {
+    name: "libnativebridge-dummy",
+    srcs: ["DummyNativeBridge.cpp"],
+    defaults: ["libnativebridge-dummy-defaults"],
+}
+
+cc_library_shared {
+    name: "libnativebridge2-dummy",
+    srcs: ["DummyNativeBridge2.cpp"],
+    defaults: ["libnativebridge-dummy-defaults"],
+}
+
+cc_library_shared {
+    name: "libnativebridge3-dummy",
+    srcs: ["DummyNativeBridge3.cpp"],
+    defaults: ["libnativebridge-dummy-defaults"],
+}
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index c9468f0..70b3fcc 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -1,8 +1,6 @@
 # Build the unit tests.
 LOCAL_PATH := $(call my-dir)
 
-include $(LOCAL_PATH)/Android.nativebridge-dummy.mk
-
 include $(CLEAR_VARS)
 
 # Build the unit tests.
diff --git a/libnativebridge/tests/Android.nativebridge-dummy.mk b/libnativebridge/tests/Android.nativebridge-dummy.mk
deleted file mode 100644
index 2d78be0..0000000
--- a/libnativebridge/tests/Android.nativebridge-dummy.mk
+++ /dev/null
@@ -1,108 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-NATIVE_BRIDGE_COMMON_SRC_FILES := \
-  DummyNativeBridge.cpp
-
-# Shared library for target
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_SHARED_LIBRARIES := libdl
-LOCAL_MULTILIB := both
-
-include $(BUILD_SHARED_LIBRARY)
-
-# Shared library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
-# v2.
-
-NATIVE_BRIDGE2_COMMON_SRC_FILES := \
-  DummyNativeBridge2.cpp
-
-# Shared library for target
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge2-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE2_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_SHARED_LIBRARIES := libdl
-LOCAL_MULTILIB := both
-
-include $(BUILD_SHARED_LIBRARY)
-
-# Shared library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge2-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE2_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
-# v3.
-
-NATIVE_BRIDGE3_COMMON_SRC_FILES := \
-  DummyNativeBridge3.cpp
-
-# Shared library for target
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge3-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_SHARED_LIBRARY)
-
-# Shared library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge3-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
diff --git a/libnativebridge/tests/DummyNativeBridge3.cpp b/libnativebridge/tests/DummyNativeBridge3.cpp
index b0dd6d0..4ef1c82 100644
--- a/libnativebridge/tests/DummyNativeBridge3.cpp
+++ b/libnativebridge/tests/DummyNativeBridge3.cpp
@@ -78,7 +78,7 @@
 
 extern "C" bool native_bridge3_initAnonymousNamespace(const char* /* public_ns_sonames */,
                                                       const char* /* anon_ns_library_path */) {
-    return true;
+  return true;
 }
 
 extern "C" android::native_bridge_namespace_t*
@@ -94,7 +94,7 @@
 extern "C" bool native_bridge3_linkNamespaces(android::native_bridge_namespace_t* /* from */,
                                               android::native_bridge_namespace_t* /* to */,
                                               const char* /* shared_libs_soname */) {
-    return true;
+  return true;
 }
 
 extern "C" void* native_bridge3_loadLibraryExt(const char* /* libpath */,
diff --git a/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
index 989a819..b0d6b09 100644
--- a/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
+++ b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
@@ -21,19 +21,19 @@
 constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
 
 TEST_F(NativeBridgeTest, V3_InitAnonymousNamespace) {
-    // Init
-    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
-    ASSERT_TRUE(NativeBridgeAvailable());
-    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
-    ASSERT_TRUE(NativeBridgeAvailable());
-    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
-    ASSERT_TRUE(NativeBridgeAvailable());
+  // Init
+  ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+  ASSERT_TRUE(NativeBridgeAvailable());
+  ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+  ASSERT_TRUE(NativeBridgeAvailable());
+  ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+  ASSERT_TRUE(NativeBridgeAvailable());
 
-    ASSERT_EQ(3U, NativeBridgeGetVersion());
-    ASSERT_EQ(true, NativeBridgeInitAnonymousNamespace(nullptr, nullptr));
+  ASSERT_EQ(3U, NativeBridgeGetVersion());
+  ASSERT_EQ(true, NativeBridgeInitAnonymousNamespace(nullptr, nullptr));
 
-    // Clean-up code_cache
-    ASSERT_EQ(0, rmdir(kCodeCache));
+  // Clean-up code_cache
+  ASSERT_EQ(0, rmdir(kCodeCache));
 }
 
 }  // namespace android
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index d63d619..9967ef8 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -18,3 +18,17 @@
 
     export_include_dirs: ["include"],
 }
+
+cc_binary {
+    name: "dhcpdbg",
+
+    srcs: [
+        "dhcptool.c",
+    ],
+
+    shared_libs: [
+        "libnetutils",
+    ],
+
+    cflags: ["-Werror"],
+}
diff --git a/libnetutils/dhcptool.c b/libnetutils/dhcptool.c
new file mode 100644
index 0000000..280b977
--- /dev/null
+++ b/libnetutils/dhcptool.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright 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 <err.h>
+#include <errno.h>
+#include <error.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <netutils/ifc.h>
+
+extern int do_dhcp(char*);
+
+int main(int argc, char* argv[]) {
+    if (argc != 2) {
+        error(EXIT_FAILURE, 0, "usage: %s INTERFACE", argv[0]);
+    }
+
+    char* interface = argv[1];
+    if (ifc_init()) {
+        err(errno, "dhcptool %s: ifc_init failed", interface);
+        ifc_close();
+        return EXIT_FAILURE;
+    }
+
+    int rc = do_dhcp(interface);
+    if (rc) {
+        err(errno, "dhcptool %s: do_dhcp failed", interface);
+    }
+    warn("IP assignment is for debug purposes ONLY");
+    ifc_close();
+
+    return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
new file mode 100644
index 0000000..1974f2c
--- /dev/null
+++ b/libprocessgroup/Android.bp
@@ -0,0 +1,11 @@
+cc_library {
+    srcs: ["processgroup.cpp"],
+    name: "libprocessgroup",
+    defaults: ["linux_bionic_supported"],
+    shared_libs: ["libbase"],
+    export_include_dirs: ["include"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/libprocessgroup/Android.mk b/libprocessgroup/Android.mk
deleted file mode 100644
index 0bfc391..0000000
--- a/libprocessgroup/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := processgroup.cpp
-LOCAL_MODULE := libprocessgroup
-LOCAL_STATIC_LIBRARIES := libbase
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := processgroup.cpp
-LOCAL_MODULE := libprocessgroup
-LOCAL_SHARED_LIBRARIES := libbase
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_SHARED_LIBRARY)
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 47f6ff3..f0c3795 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -22,8 +22,13 @@
 
 __BEGIN_DECLS
 
+// Return 0 and removes the cgroup if there are no longer any processes in it.
+// Returns -1 in the case of an error occurring or if there are processes still running
+// even after retrying for up to 200ms.
 int killProcessGroup(uid_t uid, int initialPid, int signal);
 
+// Returns the same as killProcessGroup(), however it does not retry, which means
+// that it only returns 0 in the case that the cgroup exists and it contains no processes.
 int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
 
 int createProcessGroup(uid_t uid, int initialPid);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 1572cb3..f5d4e1c 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -27,13 +27,16 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include <chrono>
 #include <memory>
 #include <mutex>
+#include <set>
 #include <thread>
 
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <private/android_filesystem_config.h>
 
 #include <processgroup/processgroup.h>
@@ -64,12 +67,27 @@
 
 std::once_flag init_path_flag;
 
-struct ctx {
-    bool initialized;
-    int fd;
-    char buf[128];
-    char *buf_ptr;
-    size_t buf_len;
+class ProcessGroup {
+  public:
+    ProcessGroup() : buf_ptr_(buf_), buf_len_(0) {}
+
+    bool Open(uid_t uid, int pid);
+
+    // Return positive number and sets *pid = next pid in process cgroup on success
+    // Returns 0 if there are no pids left in the process cgroup
+    // Returns -errno if an error was encountered
+    int GetOneAppProcess(pid_t* pid);
+
+  private:
+    // Returns positive number of bytes filled on success
+    // Returns 0 if there was nothing to read
+    // Returns -errno if an error was encountered
+    int RefillBuffer();
+
+    android::base::unique_fd fd_;
+    char buf_[128];
+    char* buf_ptr_;
+    size_t buf_len_;
 };
 
 static const char* getCgroupRootPath() {
@@ -105,87 +123,67 @@
             pid);
 }
 
-static int initCtx(uid_t uid, int pid, struct ctx *ctx)
-{
-    int ret;
+bool ProcessGroup::Open(uid_t uid, int pid) {
     char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
     convertUidPidToPath(path, sizeof(path), uid, pid);
     strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
 
     int fd = open(path, O_RDONLY);
-    if (fd < 0) {
-        ret = -errno;
-        PLOG(WARNING) << "failed to open " << path;
-        return ret;
-    }
+    if (fd < 0) return false;
 
-    ctx->fd = fd;
-    ctx->buf_ptr = ctx->buf;
-    ctx->buf_len = 0;
-    ctx->initialized = true;
+    fd_.reset(fd);
 
     LOG(VERBOSE) << "Initialized context for " << path;
 
-    return 0;
+    return true;
 }
 
-static int refillBuffer(struct ctx *ctx)
-{
-    memmove(ctx->buf, ctx->buf_ptr, ctx->buf_len);
-    ctx->buf_ptr = ctx->buf;
+int ProcessGroup::RefillBuffer() {
+    memmove(buf_, buf_ptr_, buf_len_);
+    buf_ptr_ = buf_;
 
-    ssize_t ret = read(ctx->fd, ctx->buf_ptr + ctx->buf_len,
-                sizeof(ctx->buf) - ctx->buf_len - 1);
+    ssize_t ret = read(fd_, buf_ptr_ + buf_len_, sizeof(buf_) - buf_len_ - 1);
     if (ret < 0) {
         return -errno;
     } else if (ret == 0) {
         return 0;
     }
 
-    ctx->buf_len += ret;
-    ctx->buf[ctx->buf_len] = 0;
-    LOG(VERBOSE) << "Read " << ret << " to buffer: " << ctx->buf;
+    buf_len_ += ret;
+    buf_[buf_len_] = 0;
+    LOG(VERBOSE) << "Read " << ret << " to buffer: " << buf_;
 
-    assert(ctx->buf_len <= sizeof(ctx->buf));
+    assert(buf_len_ <= sizeof(buf_));
 
     return ret;
 }
 
-static pid_t getOneAppProcess(uid_t uid, int appProcessPid, struct ctx *ctx)
-{
-    if (!ctx->initialized) {
-        int ret = initCtx(uid, appProcessPid, ctx);
-        if (ret < 0) {
-            return ret;
-        }
-    }
+int ProcessGroup::GetOneAppProcess(pid_t* out_pid) {
+    *out_pid = 0;
 
-    char *eptr;
-    while ((eptr = (char *)memchr(ctx->buf_ptr, '\n', ctx->buf_len)) == NULL) {
-        int ret = refillBuffer(ctx);
-        if (ret == 0) {
-            return -ERANGE;
-        }
-        if (ret < 0) {
-            return ret;
-        }
+    char* eptr;
+    while ((eptr = static_cast<char*>(memchr(buf_ptr_, '\n', buf_len_))) == nullptr) {
+        int ret = RefillBuffer();
+        if (ret <= 0) return ret;
     }
 
     *eptr = '\0';
-    char *pid_eptr = NULL;
+    char* pid_eptr = nullptr;
     errno = 0;
-    long pid = strtol(ctx->buf_ptr, &pid_eptr, 10);
+    long pid = strtol(buf_ptr_, &pid_eptr, 10);
     if (errno != 0) {
         return -errno;
     }
     if (pid_eptr != eptr) {
-        return -EINVAL;
+        errno = EINVAL;
+        return -errno;
     }
 
-    ctx->buf_len -= (eptr - ctx->buf_ptr) + 1;
-    ctx->buf_ptr = eptr + 1;
+    buf_len_ -= (eptr - buf_ptr_) + 1;
+    buf_ptr_ = eptr + 1;
 
-    return (pid_t)pid;
+    *out_pid = static_cast<pid_t>(pid);
+    return 1;
 }
 
 static int removeProcessGroup(uid_t uid, int pid)
@@ -219,8 +217,8 @@
             }
 
             snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name);
-            LOG(VERBOSE) << "removing " << path;
-            if (rmdir(path) == -1) PLOG(WARNING) << "failed to remove " << path;
+            LOG(VERBOSE) << "Removing " << path;
+            if (rmdir(path) == -1) PLOG(WARNING) << "Failed to remove " << path;
         }
     }
 }
@@ -231,7 +229,7 @@
     const char* cgroup_root_path = getCgroupRootPath();
     std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir);
     if (root == NULL) {
-        PLOG(ERROR) << "failed to open " << cgroup_root_path;
+        PLOG(ERROR) << "Failed to open " << cgroup_root_path;
     } else {
         dirent* dir;
         while ((dir = readdir(root.get())) != nullptr) {
@@ -246,20 +244,32 @@
 
             snprintf(path, sizeof(path), "%s/%s", cgroup_root_path, dir->d_name);
             removeUidProcessGroups(path);
-            LOG(VERBOSE) << "removing " << path;
-            if (rmdir(path) == -1) PLOG(WARNING) << "failed to remove " << path;
+            LOG(VERBOSE) << "Removing " << path;
+            if (rmdir(path) == -1) PLOG(WARNING) << "Failed to remove " << path;
         }
     }
 }
 
+// Returns number of processes killed on success
+// Returns 0 if there are no processes in the process cgroup left to kill
+// Returns -errno on error
 static int doKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
-    int processes = 0;
-    struct ctx ctx;
+    ProcessGroup process_group;
+    if (!process_group.Open(uid, initialPid)) {
+        PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
+        return -errno;
+    }
+
+    // We separate all of the pids in the cgroup into those pids that are also the leaders of
+    // process groups (stored in the pgids set) and those that are not (stored in the pids set).
+    std::set<pid_t> pgids;
+    pgids.emplace(initialPid);
+    std::set<pid_t> pids;
+
+    int ret;
     pid_t pid;
-
-    ctx.initialized = false;
-
-    while ((pid = getOneAppProcess(uid, initialPid, &ctx)) >= 0) {
+    int processes = 0;
+    while ((ret = process_group.GetOneAppProcess(&pid)) > 0 && pid >= 0) {
         processes++;
         if (pid == 0) {
             // Should never happen...  but if it does, trying to kill this
@@ -267,55 +277,100 @@
             LOG(WARNING) << "Yikes, we've been told to kill pid 0!  How about we don't do that?";
             continue;
         }
-        LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid
-                     << " as part of process group " << initialPid;
+        pid_t pgid = getpgid(pid);
+        if (pgid == -1) PLOG(ERROR) << "getpgid(" << pid << ") failed";
+        if (pgid == pid) {
+            pgids.emplace(pid);
+        } else {
+            pids.emplace(pid);
+        }
+    }
+
+    // Erase all pids that will be killed when we kill the process groups.
+    for (auto it = pids.begin(); it != pids.end();) {
+        pid_t pgid = getpgid(pid);
+        if (pgids.count(pgid) == 1) {
+            it = pids.erase(it);
+        } else {
+            ++it;
+        }
+    }
+
+    // Kill all process groups.
+    for (const auto pgid : pgids) {
+        LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
+                     << " as part of process cgroup " << initialPid;
+
+        if (kill(-pgid, signal) == -1) {
+            PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed";
+        }
+    }
+
+    // Kill remaining pids.
+    for (const auto pid : pids) {
+        LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
+                     << initialPid;
+
         if (kill(pid, signal) == -1) {
             PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
         }
     }
 
-    if (ctx.initialized) {
-        close(ctx.fd);
-    }
-
-    return processes;
+    return ret >= 0 ? processes : ret;
 }
 
-static int killProcessGroup(uid_t uid, int initialPid, int signal, int retry) {
+static int killProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
     std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
 
+    int retry = retries;
     int processes;
     while ((processes = doKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
-        LOG(VERBOSE) << "killed " << processes << " processes for processgroup " << initialPid;
+        LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
         if (retry > 0) {
             std::this_thread::sleep_for(5ms);
             --retry;
         } else {
-            LOG(ERROR) << "failed to kill " << processes << " processes for processgroup "
-                       << initialPid;
             break;
         }
     }
 
-    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
+    if (processes < 0) {
+        PLOG(ERROR) << "Error encountered killing process cgroup uid " << uid << " pid "
+                    << initialPid;
+        return -1;
+    }
 
+    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
     auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
-    LOG(VERBOSE) << "Killed process group uid " << uid << " pid " << initialPid << " in "
-                 << static_cast<int>(ms) << "ms, " << processes << " procs remain";
+
+    // We only calculate the number of 'processes' when killing the processes.
+    // In the retries == 0 case, we only kill the processes once and therefore
+    // will not have waited then recalculated how many processes are remaining
+    // after the first signals have been sent.
+    // Logging anything regarding the number of 'processes' here does not make sense.
 
     if (processes == 0) {
+        if (retries > 0) {
+            LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
+                      << " in " << static_cast<int>(ms) << "ms";
+        }
         return removeProcessGroup(uid, initialPid);
     } else {
+        if (retries > 0) {
+            LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
+                       << " in " << static_cast<int>(ms) << "ms, " << processes
+                       << " processes remain";
+        }
         return -1;
     }
 }
 
 int killProcessGroup(uid_t uid, int initialPid, int signal) {
-    return killProcessGroup(uid, initialPid, signal, 40 /*maxRetry*/);
+    return killProcessGroup(uid, initialPid, signal, 40 /*retries*/);
 }
 
 int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
-    return killProcessGroup(uid, initialPid, signal, 0 /*maxRetry*/);
+    return killProcessGroup(uid, initialPid, signal, 0 /*retries*/);
 }
 
 static bool mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
@@ -341,14 +396,14 @@
     convertUidToPath(path, sizeof(path), uid);
 
     if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
-        PLOG(ERROR) << "failed to make and chown " << path;
+        PLOG(ERROR) << "Failed to make and chown " << path;
         return -errno;
     }
 
     convertUidPidToPath(path, sizeof(path), uid, initialPid);
 
     if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
-        PLOG(ERROR) << "failed to make and chown " << path;
+        PLOG(ERROR) << "Failed to make and chown " << path;
         return -errno;
     }
 
@@ -357,7 +412,7 @@
     int fd = open(path, O_WRONLY);
     if (fd == -1) {
         int ret = -errno;
-        PLOG(ERROR) << "failed to open " << path;
+        PLOG(ERROR) << "Failed to open " << path;
         return ret;
     }
 
@@ -367,7 +422,7 @@
     int ret = 0;
     if (write(fd, pid, len) < 0) {
         ret = -errno;
-        PLOG(ERROR) << "failed to write '" << pid << "' to " << path;
+        PLOG(ERROR) << "Failed to write '" << pid << "' to " << path;
     }
 
     close(fd);
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index dd8b5fd..6ec0991 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -10,17 +10,23 @@
         "sparse.c",
         "sparse_crc32.c",
         "sparse_err.c",
-        "sparse_read.c",
+        "sparse_read.cpp",
     ],
     cflags: ["-Werror"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
     target: {
         host: {
-            shared_libs: ["libz-host"],
+            shared_libs: [
+                "libz-host",
+                "libbase",
+            ],
         },
         android: {
-            shared_libs: ["libz"],
+            shared_libs: [
+                "libz",
+                "libbase",
+            ],
         },
         windows: {
             enabled: true,
@@ -38,6 +44,7 @@
     static_libs: [
         "libsparse",
         "libz",
+        "libbase",
     ],
 
     cflags: ["-Werror"],
@@ -50,6 +57,7 @@
     static_libs: [
         "libsparse",
         "libz",
+        "libbase",
     ],
 
     cflags: ["-Werror"],
@@ -61,6 +69,7 @@
     static_libs: [
         "libsparse",
         "libz",
+        "libbase",
     ],
 
     cflags: ["-Werror"],
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
index 2115998..51e60ef 100644
--- a/libsparse/output_file.c
+++ b/libsparse/output_file.c
@@ -584,7 +584,7 @@
 				.file_hdr_sz = SPARSE_HEADER_LEN,
 				.chunk_hdr_sz = CHUNK_HEADER_LEN,
 				.blk_sz = out->block_size,
-				.total_blks = out->len / out->block_size,
+				.total_blks = DIV_ROUND_UP(out->len, out->block_size),
 				.total_chunks = chunks,
 				.image_checksum = 0
 		};
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 474c1fc..b67e94e 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -17,6 +17,10 @@
 #ifndef _OUTPUT_FILE_H_
 #define _OUTPUT_FILE_H_
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include <sparse/sparse.h>
 
 struct output_file;
@@ -38,4 +42,8 @@
 
 int read_all(int fd, void *buf, size_t len);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
diff --git a/libsparse/sparse_file.h b/libsparse/sparse_file.h
index 91a12e6..763f43f 100644
--- a/libsparse/sparse_file.h
+++ b/libsparse/sparse_file.h
@@ -17,6 +17,10 @@
 #ifndef _LIBSPARSE_SPARSE_FILE_H_
 #define _LIBSPARSE_SPARSE_FILE_H_
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include <sparse/sparse.h>
 
 struct sparse_file {
@@ -28,5 +32,8 @@
 	struct output_file *out;
 };
 
+#ifdef __cplusplus
+}
+#endif
 
 #endif /* _LIBSPARSE_SPARSE_FILE_H_ */
diff --git a/libsparse/sparse_format.h b/libsparse/sparse_format.h
index c41f12a..779e038 100644
--- a/libsparse/sparse_format.h
+++ b/libsparse/sparse_format.h
@@ -18,6 +18,10 @@
 #define _LIBSPARSE_SPARSE_FORMAT_H_
 #include "sparse_defs.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 typedef struct sparse_header {
   __le32	magic;		/* 0xed26ff3a */
   __le16	major_version;	/* (0x1) - reject images with higher major versions */
@@ -52,4 +56,8 @@
  *  For a CRC32 chunk, it's 4 bytes of CRC32
  */
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
diff --git a/libsparse/sparse_read.c b/libsparse/sparse_read.cpp
similarity index 88%
rename from libsparse/sparse_read.c
rename to libsparse/sparse_read.cpp
index a188202..bd66873 100644
--- a/libsparse/sparse_read.c
+++ b/libsparse/sparse_read.cpp
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#define _GNU_SOURCE
 #define _FILE_OFFSET_BITS 64
 #define _LARGEFILE64_SOURCE 1
 
+#include <algorithm>
 #include <inttypes.h>
 #include <fcntl.h>
 #include <stdarg.h>
@@ -25,17 +25,19 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
+#include <string>
 #include <unistd.h>
 
 #include <sparse/sparse.h>
 
+#include "android-base/stringprintf.h"
 #include "defs.h"
 #include "output_file.h"
 #include "sparse_crc32.h"
 #include "sparse_file.h"
 #include "sparse_format.h"
 
+
 #if defined(__APPLE__) && defined(__MACH__)
 #define lseek64 lseek
 #define off64_t off_t
@@ -45,57 +47,30 @@
 #define SPARSE_HEADER_LEN       (sizeof(sparse_header_t))
 #define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
 
-#define COPY_BUF_SIZE (1024U*1024U)
+static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
 static char *copybuf;
 
-#define min(a, b) \
-	({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
+static std::string ErrorString(int err)
+{
+	if (err == -EOVERFLOW) return "EOF while reading file";
+	if (err == -EINVAL) return "Invalid sparse file format";
+	if (err == -ENOMEM) return "Failed allocation while reading file";
+	return android::base::StringPrintf("Unknown error %d", err);
+}
 
 static void verbose_error(bool verbose, int err, const char *fmt, ...)
 {
-	char *s = "";
-	char *at = "";
+	if (!verbose) return;
+
+	std::string msg = ErrorString(err);
 	if (fmt) {
+		msg += " at ";
 		va_list argp;
-		int size;
-
 		va_start(argp, fmt);
-		size = vsnprintf(NULL, 0, fmt, argp);
+		android::base::StringAppendV(&msg, fmt, argp);
 		va_end(argp);
-
-		if (size < 0) {
-			return;
-		}
-
-		at = malloc(size + 1);
-		if (at == NULL) {
-			return;
-		}
-
-		va_start(argp, fmt);
-		vsnprintf(at, size, fmt, argp);
-		va_end(argp);
-		at[size] = 0;
-		s = " at ";
 	}
-	if (verbose) {
-#ifndef _WIN32
-		if (err == -EOVERFLOW) {
-			sparse_print_verbose("EOF while reading file%s%s\n", s, at);
-		} else
-#endif
-		if (err == -EINVAL) {
-			sparse_print_verbose("Invalid sparse file format%s%s\n", s, at);
-		} else if (err == -ENOMEM) {
-			sparse_print_verbose("Failed allocation while reading file%s%s\n",
-					s, at);
-		} else {
-			sparse_print_verbose("Unknown error %d%s%s\n", err, s, at);
-		}
-	}
-	if (fmt) {
-		free(at);
-	}
+	sparse_print_verbose("%s\n", msg.c_str());
 }
 
 static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
@@ -104,7 +79,7 @@
 {
 	int ret;
 	int chunk;
-	unsigned int len = blocks * s->block_size;
+	int64_t len = blocks * s->block_size;
 
 	if (chunk_size % s->block_size != 0) {
 		return -EINVAL;
@@ -121,7 +96,7 @@
 
 	if (crc32) {
 		while (len) {
-			chunk = min(len, COPY_BUF_SIZE);
+			chunk = std::min(len, COPY_BUF_SIZE);
 			ret = read_all(fd, copybuf, chunk);
 			if (ret < 0) {
 				return ret;
@@ -168,7 +143,7 @@
 		}
 
 		while (len) {
-			chunk = min(len, COPY_BUF_SIZE);
+			chunk = std::min(len, COPY_BUF_SIZE);
 			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
 			len -= chunk;
 		}
@@ -190,7 +165,7 @@
 		memset(copybuf, 0, COPY_BUF_SIZE);
 
 		while (len) {
-			int chunk = min(len, COPY_BUF_SIZE);
+			int chunk = std::min(len, COPY_BUF_SIZE);
 			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
 			len -= chunk;
 		}
@@ -284,7 +259,7 @@
 	off64_t offset;
 
 	if (!copybuf) {
-		copybuf = malloc(COPY_BUF_SIZE);
+		copybuf = (char *)malloc(COPY_BUF_SIZE);
 	}
 
 	if (!copybuf) {
@@ -357,7 +332,7 @@
 static int sparse_file_read_normal(struct sparse_file *s, int fd)
 {
 	int ret;
-	uint32_t *buf = malloc(s->block_size);
+	uint32_t *buf = (uint32_t *)malloc(s->block_size);
 	unsigned int block = 0;
 	int64_t remain = s->len;
 	int64_t offset = 0;
@@ -370,7 +345,7 @@
 	}
 
 	while (remain > 0) {
-		to_read = min(remain, s->block_size);
+		to_read = std::min(remain, (int64_t)(s->block_size));
 		ret = read_all(fd, buf, to_read);
 		if (ret < 0) {
 			error("failed to read sparse file");
diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c
index 4dedf7f..2da204a 100644
--- a/libsuspend/autosuspend_wakeup_count.c
+++ b/libsuspend/autosuspend_wakeup_count.c
@@ -24,6 +24,7 @@
 #include <stddef.h>
 #include <stdbool.h>
 #include <string.h>
+#include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -35,12 +36,24 @@
 #define SYS_POWER_STATE "/sys/power/state"
 #define SYS_POWER_WAKEUP_COUNT "/sys/power/wakeup_count"
 
+#define BASE_SLEEP_TIME 100000
+
 static int state_fd;
 static int wakeup_count_fd;
 static pthread_t suspend_thread;
 static sem_t suspend_lockout;
 static const char *sleep_state = "mem";
 static void (*wakeup_func)(bool success) = NULL;
+static int sleep_time = BASE_SLEEP_TIME;
+
+static void update_sleep_time(bool success) {
+    if (success) {
+        sleep_time = BASE_SLEEP_TIME;
+        return;
+    }
+    // double sleep time after each failure up to one minute
+    sleep_time = MIN(sleep_time * 2, 60000000);
+}
 
 static void *suspend_thread_func(void *arg __attribute__((unused)))
 {
@@ -48,10 +61,12 @@
     char wakeup_count[20];
     int wakeup_count_len;
     int ret;
-    bool success;
+    bool success = true;
 
     while (1) {
-        usleep(100000);
+        update_sleep_time(success);
+        usleep(sleep_time);
+        success = false;
         ALOGV("%s: read wakeup_count\n", __func__);
         lseek(wakeup_count_fd, 0, SEEK_SET);
         wakeup_count_len = TEMP_FAILURE_RETRY(read(wakeup_count_fd, wakeup_count,
@@ -75,7 +90,6 @@
             continue;
         }
 
-        success = true;
         ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count);
         ret = TEMP_FAILURE_RETRY(write(wakeup_count_fd, wakeup_count, wakeup_count_len));
         if (ret < 0) {
@@ -84,8 +98,8 @@
         } else {
             ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE);
             ret = TEMP_FAILURE_RETRY(write(state_fd, sleep_state, strlen(sleep_state)));
-            if (ret < 0) {
-                success = false;
+            if (ret >= 0) {
+                success = true;
             }
             void (*func)(bool success) = wakeup_func;
             if (func != NULL) {
diff --git a/libsystem/include/system/qemu_pipe.h b/libsystem/include/system/qemu_pipe.h
deleted file mode 100644
index af25079..0000000
--- a/libsystem/include/system/qemu_pipe.h
+++ /dev/null
@@ -1,134 +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.
- */
-#ifndef ANDROID_INCLUDE_SYSTEM_QEMU_PIPE_H
-#define ANDROID_INCLUDE_SYSTEM_QEMU_PIPE_H
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-
-// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
-// occurs during pipe operations. The macro should simply take a printf-style
-// formatting string followed by optional arguments.
-#ifndef QEMU_PIPE_DEBUG
-#  define  QEMU_PIPE_DEBUG(...)   (void)0
-#endif
-
-// Try to open a new Qemu fast-pipe. This function returns a file descriptor
-// that can be used to communicate with a named service managed by the
-// emulator.
-//
-// This file descriptor can be used as a standard pipe/socket descriptor.
-//
-// 'pipeName' is the name of the emulator service you want to connect to,
-// and must begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
-//
-// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
-//
-// EINVAL  -> unknown/unsupported pipeName
-// ENOSYS  -> fast pipes not available in this system.
-//
-// ENOSYS should never happen, except if you're trying to run within a
-// misconfigured emulator.
-//
-// You should be able to open several pipes to the same pipe service,
-// except for a few special cases (e.g. GSM modem), where EBUSY will be
-// returned if more than one client tries to connect to it.
-static __inline__ int qemu_pipe_open(const char* pipeName) {
-    // Sanity check.
-    if (!pipeName || memcmp(pipeName, "pipe:", 5) != 0) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
-    if (fd < 0) {
-        QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__,
-                        strerror(errno));
-        return -1;
-    }
-
-    // Write the pipe name, *including* the trailing zero which is necessary.
-    size_t pipeNameLen = strlen(pipeName);
-    ssize_t ret = TEMP_FAILURE_RETRY(write(fd, pipeName, pipeNameLen + 1U));
-    if (ret != (ssize_t)pipeNameLen + 1) {
-        QEMU_PIPE_DEBUG("%s: Could not connect to %s pipe service: %s",
-                        __FUNCTION__, pipeName, strerror(errno));
-        if (ret == 0) {
-            errno = ECONNRESET;
-        } else if (ret > 0) {
-            errno = EINVAL;
-        }
-        return -1;
-    }
-    return fd;
-}
-
-// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
-// This really adds a 4-hexchar prefix describing the payload size.
-// Returns 0 on success, and -1 on error.
-static int __inline__ qemu_pipe_frame_send(int fd,
-                                           const void* buff,
-                                           size_t len) {
-    char header[5];
-    snprintf(header, sizeof(header), "%04zx", len);
-    ssize_t ret = TEMP_FAILURE_RETRY(write(fd, header, 4));
-    if (ret != 4) {
-        QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
-        return -1;
-    }
-    ret = TEMP_FAILURE_RETRY(write(fd, buff, len));
-    if (ret != (ssize_t)len) {
-        QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
-        return -1;
-    }
-    return 0;
-}
-
-// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
-// If the framed message is larger than |len|, then this returns -1 and the
-// content is lost. Otherwise, this returns the size of the message. NOTE:
-// empty messages are possible in a framed wire protocol and do not mean
-// end-of-stream.
-static int __inline__ qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
-    char header[5];
-    ssize_t ret = TEMP_FAILURE_RETRY(read(fd, header, 4));
-    if (ret != 4) {
-        QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
-        return -1;
-    }
-    header[4] = '\0';
-    size_t size;
-    if (sscanf(header, "%04zx", &size) != 1) {
-        QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
-        return -1;
-    }
-    if (size > len) {
-        QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size,
-                        len);
-        return -1;
-    }
-    ret = TEMP_FAILURE_RETRY(read(fd, buff, size));
-    if (ret != (ssize_t)size) {
-        QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s",
-                        strerror(errno));
-        return -1;
-    }
-    return size;
-}
-
-#endif /* ANDROID_INCLUDE_HARDWARE_QEMUD_PIPE_H */
diff --git a/libsystem/include/system/window-deprecated.h b/libsystem/include/system/window-deprecated.h
deleted file mode 100644
index e836aea..0000000
--- a/libsystem/include/system/window-deprecated.h
+++ /dev/null
@@ -1,1114 +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.
- */
-
-/**************************************************************************************************
- **************************************************************************************************
- ****                                                                                          ****
- ****  DEPRECATED                                                                              ****
- ****                                                                                          ****
- ****  THIS FILE EXISTS ONLY FOR BACKWARD SOURCE COMPATIBILITY.                                ****
- ****                                                                                          ****
- ****  DO NOT ADD TO THIS FILE.                                                                ****
- ****                                                                                          ****
- ****  Driver implementors (vendors) should use vndk/window.h                                  ****
- ****  (frameworks/native/libs/nativewindow/include/vndk/window.h)                             ****
- ****                                                                                          ****
- ****  Internal definition can be found here:                                                  ****
- ****  frameworks/native/libs/nativewindow/include/system/window.h                             ****
- ****                                                                                          ****
- **************************************************************************************************
- **************************************************************************************************/
-
-#pragma once
-
-#include <cutils/native_handle.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/cdefs.h>
-#include <system/graphics.h>
-#include <unistd.h>
-#include <stdbool.h>
-
-#ifndef __UNUSED
-#define __UNUSED __attribute__((__unused__))
-#endif
-#ifndef __deprecated
-#define __deprecated __attribute__((__deprecated__))
-#endif
-
-__BEGIN_DECLS
-
-/*****************************************************************************/
-
-#ifdef __cplusplus
-#define ANDROID_NATIVE_UNSIGNED_CAST(x) static_cast<unsigned int>(x)
-#else
-#define ANDROID_NATIVE_UNSIGNED_CAST(x) ((unsigned int)(x))
-#endif
-
-#define ANDROID_NATIVE_MAKE_CONSTANT(a,b,c,d) \
-    ((ANDROID_NATIVE_UNSIGNED_CAST(a) << 24) | \
-     (ANDROID_NATIVE_UNSIGNED_CAST(b) << 16) | \
-     (ANDROID_NATIVE_UNSIGNED_CAST(c) << 8) | \
-     (ANDROID_NATIVE_UNSIGNED_CAST(d)))
-
-#define ANDROID_NATIVE_WINDOW_MAGIC \
-    ANDROID_NATIVE_MAKE_CONSTANT('_','w','n','d')
-
-#define ANDROID_NATIVE_BUFFER_MAGIC \
-    ANDROID_NATIVE_MAKE_CONSTANT('_','b','f','r')
-
-// ---------------------------------------------------------------------------
-
-// This #define may be used to conditionally compile device-specific code to
-// support either the prior ANativeWindow interface, which did not pass libsync
-// fences around, or the new interface that does.  This #define is only present
-// when the ANativeWindow interface does include libsync support.
-#define ANDROID_NATIVE_WINDOW_HAS_SYNC 1
-
-// ---------------------------------------------------------------------------
-
-typedef const native_handle_t* buffer_handle_t;
-
-// ---------------------------------------------------------------------------
-
-typedef struct android_native_rect_t
-{
-    int32_t left;
-    int32_t top;
-    int32_t right;
-    int32_t bottom;
-} android_native_rect_t;
-
-// ---------------------------------------------------------------------------
-
-typedef struct android_native_base_t
-{
-    /* a magic value defined by the actual EGL native type */
-    int magic;
-
-    /* the sizeof() of the actual EGL native type */
-    int version;
-
-    void* reserved[4];
-
-    /* reference-counting interface */
-    void (*incRef)(struct android_native_base_t* base);
-    void (*decRef)(struct android_native_base_t* base);
-} android_native_base_t;
-
-typedef struct ANativeWindowBuffer
-{
-#ifdef __cplusplus
-    ANativeWindowBuffer() {
-        common.magic = ANDROID_NATIVE_BUFFER_MAGIC;
-        common.version = sizeof(ANativeWindowBuffer);
-        memset(common.reserved, 0, sizeof(common.reserved));
-    }
-
-    // Implement the methods that sp<ANativeWindowBuffer> expects so that it
-    // can be used to automatically refcount ANativeWindowBuffer's.
-    void incStrong(const void* /*id*/) const {
-        common.incRef(const_cast<android_native_base_t*>(&common));
-    }
-    void decStrong(const void* /*id*/) const {
-        common.decRef(const_cast<android_native_base_t*>(&common));
-    }
-#endif
-
-    struct android_native_base_t common;
-
-    int width;
-    int height;
-    int stride;
-    int format;
-    int usage;
-    uintptr_t layerCount;
-
-    void* reserved[1];
-
-    buffer_handle_t handle;
-
-    void* reserved_proc[8];
-} ANativeWindowBuffer_t;
-
-// Old typedef for backwards compatibility.
-typedef ANativeWindowBuffer_t android_native_buffer_t;
-
-// ---------------------------------------------------------------------------
-
-/* attributes queriable with query() */
-enum {
-    NATIVE_WINDOW_WIDTH     = 0,
-    NATIVE_WINDOW_HEIGHT    = 1,
-    NATIVE_WINDOW_FORMAT    = 2,
-
-    /* The minimum number of buffers that must remain un-dequeued after a buffer
-     * has been queued.  This value applies only if set_buffer_count was used to
-     * override the number of buffers and if a buffer has since been queued.
-     * Users of the set_buffer_count ANativeWindow method should query this
-     * value before calling set_buffer_count.  If it is necessary to have N
-     * buffers simultaneously dequeued as part of the steady-state operation,
-     * and this query returns M then N+M buffers should be requested via
-     * native_window_set_buffer_count.
-     *
-     * Note that this value does NOT apply until a single buffer has been
-     * queued.  In particular this means that it is possible to:
-     *
-     * 1. Query M = min undequeued buffers
-     * 2. Set the buffer count to N + M
-     * 3. Dequeue all N + M buffers
-     * 4. Cancel M buffers
-     * 5. Queue, dequeue, queue, dequeue, ad infinitum
-     */
-    NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = 3,
-
-    /* Check whether queueBuffer operations on the ANativeWindow send the buffer
-     * to the window compositor.  The query sets the returned 'value' argument
-     * to 1 if the ANativeWindow DOES send queued buffers directly to the window
-     * compositor and 0 if the buffers do not go directly to the window
-     * compositor.
-     *
-     * This can be used to determine whether protected buffer content should be
-     * sent to the ANativeWindow.  Note, however, that a result of 1 does NOT
-     * indicate that queued buffers will be protected from applications or users
-     * capturing their contents.  If that behavior is desired then some other
-     * mechanism (e.g. the GRALLOC_USAGE_PROTECTED flag) should be used in
-     * conjunction with this query.
-     */
-    NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER = 4,
-
-    /* Get the concrete type of a ANativeWindow.  See below for the list of
-     * possible return values.
-     *
-     * This query should not be used outside the Android framework and will
-     * likely be removed in the near future.
-     */
-    NATIVE_WINDOW_CONCRETE_TYPE = 5,
-
-
-    /*
-     * Default width and height of ANativeWindow buffers, these are the
-     * dimensions of the window buffers irrespective of the
-     * NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS call and match the native window
-     * size unless overridden by NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS.
-     */
-    NATIVE_WINDOW_DEFAULT_WIDTH = 6,
-    NATIVE_WINDOW_DEFAULT_HEIGHT = 7,
-
-    /*
-     * transformation that will most-likely be applied to buffers. This is only
-     * a hint, the actual transformation applied might be different.
-     *
-     * INTENDED USE:
-     *
-     * The transform hint can be used by a producer, for instance the GLES
-     * driver, to pre-rotate the rendering such that the final transformation
-     * in the composer is identity. This can be very useful when used in
-     * conjunction with the h/w composer HAL, in situations where it
-     * cannot handle arbitrary rotations.
-     *
-     * 1. Before dequeuing a buffer, the GL driver (or any other ANW client)
-     *    queries the ANW for NATIVE_WINDOW_TRANSFORM_HINT.
-     *
-     * 2. The GL driver overrides the width and height of the ANW to
-     *    account for NATIVE_WINDOW_TRANSFORM_HINT. This is done by querying
-     *    NATIVE_WINDOW_DEFAULT_{WIDTH | HEIGHT}, swapping the dimensions
-     *    according to NATIVE_WINDOW_TRANSFORM_HINT and calling
-     *    native_window_set_buffers_dimensions().
-     *
-     * 3. The GL driver dequeues a buffer of the new pre-rotated size.
-     *
-     * 4. The GL driver renders to the buffer such that the image is
-     *    already transformed, that is applying NATIVE_WINDOW_TRANSFORM_HINT
-     *    to the rendering.
-     *
-     * 5. The GL driver calls native_window_set_transform to apply
-     *    inverse transformation to the buffer it just rendered.
-     *    In order to do this, the GL driver needs
-     *    to calculate the inverse of NATIVE_WINDOW_TRANSFORM_HINT, this is
-     *    done easily:
-     *
-     *        int hintTransform, inverseTransform;
-     *        query(..., NATIVE_WINDOW_TRANSFORM_HINT, &hintTransform);
-     *        inverseTransform = hintTransform;
-     *        if (hintTransform & HAL_TRANSFORM_ROT_90)
-     *            inverseTransform ^= HAL_TRANSFORM_ROT_180;
-     *
-     *
-     * 6. The GL driver queues the pre-transformed buffer.
-     *
-     * 7. The composer combines the buffer transform with the display
-     *    transform.  If the buffer transform happens to cancel out the
-     *    display transform then no rotation is needed.
-     *
-     */
-    NATIVE_WINDOW_TRANSFORM_HINT = 8,
-
-    /*
-     * Boolean that indicates whether the consumer is running more than
-     * one buffer behind the producer.
-     */
-    NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND = 9,
-
-    /*
-     * The consumer gralloc usage bits currently set by the consumer.
-     * The values are defined in hardware/libhardware/include/gralloc.h.
-     */
-    NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10,
-
-    /**
-     * Transformation that will by applied to buffers by the hwcomposer.
-     * This must not be set or checked by producer endpoints, and will
-     * disable the transform hint set in SurfaceFlinger (see
-     * NATIVE_WINDOW_TRANSFORM_HINT).
-     *
-     * INTENDED USE:
-     * Temporary - Please do not use this.  This is intended only to be used
-     * by the camera's LEGACY mode.
-     *
-     * In situations where a SurfaceFlinger client wishes to set a transform
-     * that is not visible to the producer, and will always be applied in the
-     * hardware composer, the client can set this flag with
-     * native_window_set_buffers_sticky_transform.  This can be used to rotate
-     * and flip buffers consumed by hardware composer without actually changing
-     * the aspect ratio of the buffers produced.
-     */
-    NATIVE_WINDOW_STICKY_TRANSFORM = 11,
-
-    /**
-     * The default data space for the buffers as set by the consumer.
-     * The values are defined in graphics.h.
-     */
-    NATIVE_WINDOW_DEFAULT_DATASPACE = 12,
-
-    /*
-     * Returns the age of the contents of the most recently dequeued buffer as
-     * the number of frames that have elapsed since it was last queued. For
-     * example, if the window is double-buffered, the age of any given buffer in
-     * steady state will be 2. If the dequeued buffer has never been queued, its
-     * age will be 0.
-     */
-    NATIVE_WINDOW_BUFFER_AGE = 13,
-
-    /*
-     * Returns the duration of the last dequeueBuffer call in microseconds
-     */
-    NATIVE_WINDOW_LAST_DEQUEUE_DURATION = 14,
-
-    /*
-     * Returns the duration of the last queueBuffer call in microseconds
-     */
-    NATIVE_WINDOW_LAST_QUEUE_DURATION = 15,
-
-    /*
-     * Returns the number of image layers that the ANativeWindow buffer
-     * contains. By default this is 1, unless a buffer is explicitly allocated
-     * to contain multiple layers.
-     */
-    NATIVE_WINDOW_LAYER_COUNT = 16,
-
-    /*
-     * Returns 1 if the native window is valid, 0 otherwise. native window is valid
-     * if it is safe (i.e. no crash will occur) to call any method on it.
-     */
-    NATIVE_WINDOW_IS_VALID = 17,
-
-    /*
-     * Returns 1 if NATIVE_WINDOW_GET_FRAME_TIMESTAMPS will return display
-     * present info, 0 if it won't.
-     */
-    NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT = 18,
-
-    /*
-     * The consumer end is capable of handling protected buffers, i.e. buffer
-     * with GRALLOC_USAGE_PROTECTED usage bits on.
-     */
-    NATIVE_WINDOW_CONSUMER_IS_PROTECTED = 19,
-};
-
-/* Valid operations for the (*perform)() hook.
- *
- * Values marked as 'deprecated' are supported, but have been superceded by
- * other functionality.
- *
- * Values marked as 'private' should be considered private to the framework.
- * HAL implementation code with access to an ANativeWindow should not use these,
- * as it may not interact properly with the framework's use of the
- * ANativeWindow.
- */
-enum {
-// clang-format off
-    NATIVE_WINDOW_SET_USAGE                 =  0,
-    NATIVE_WINDOW_CONNECT                   =  1,   /* deprecated */
-    NATIVE_WINDOW_DISCONNECT                =  2,   /* deprecated */
-    NATIVE_WINDOW_SET_CROP                  =  3,   /* private */
-    NATIVE_WINDOW_SET_BUFFER_COUNT          =  4,
-    NATIVE_WINDOW_SET_BUFFERS_GEOMETRY      =  5,   /* deprecated */
-    NATIVE_WINDOW_SET_BUFFERS_TRANSFORM     =  6,
-    NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP     =  7,
-    NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS    =  8,
-    NATIVE_WINDOW_SET_BUFFERS_FORMAT        =  9,
-    NATIVE_WINDOW_SET_SCALING_MODE          = 10,   /* private */
-    NATIVE_WINDOW_LOCK                      = 11,   /* private */
-    NATIVE_WINDOW_UNLOCK_AND_POST           = 12,   /* private */
-    NATIVE_WINDOW_API_CONNECT               = 13,   /* private */
-    NATIVE_WINDOW_API_DISCONNECT            = 14,   /* private */
-    NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */
-    NATIVE_WINDOW_SET_POST_TRANSFORM_CROP   = 16,   /* private */
-    NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17,/* private */
-    NATIVE_WINDOW_SET_SIDEBAND_STREAM       = 18,
-    NATIVE_WINDOW_SET_BUFFERS_DATASPACE     = 19,
-    NATIVE_WINDOW_SET_SURFACE_DAMAGE        = 20,   /* private */
-    NATIVE_WINDOW_SET_SHARED_BUFFER_MODE    = 21,
-    NATIVE_WINDOW_SET_AUTO_REFRESH          = 22,
-    NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION= 23,
-    NATIVE_WINDOW_GET_NEXT_FRAME_ID         = 24,
-    NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS   = 25,
-    NATIVE_WINDOW_GET_COMPOSITOR_TIMING     = 26,
-    NATIVE_WINDOW_GET_FRAME_TIMESTAMPS      = 27,
-    NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT    = 28,
-    NATIVE_WINDOW_GET_HDR_SUPPORT           = 29,
-// clang-format on
-};
-
-/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
-enum {
-    /* Buffers will be queued by EGL via eglSwapBuffers after being filled using
-     * OpenGL ES.
-     */
-    NATIVE_WINDOW_API_EGL = 1,
-
-    /* Buffers will be queued after being filled using the CPU
-     */
-    NATIVE_WINDOW_API_CPU = 2,
-
-    /* Buffers will be queued by Stagefright after being filled by a video
-     * decoder.  The video decoder can either be a software or hardware decoder.
-     */
-    NATIVE_WINDOW_API_MEDIA = 3,
-
-    /* Buffers will be queued by the the camera HAL.
-     */
-    NATIVE_WINDOW_API_CAMERA = 4,
-};
-
-/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */
-enum {
-    /* flip source image horizontally */
-    NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H ,
-    /* flip source image vertically */
-    NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V,
-    /* rotate source image 90 degrees clock-wise, and is applied after TRANSFORM_FLIP_{H|V} */
-    NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90,
-    /* rotate source image 180 degrees */
-    NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180,
-    /* rotate source image 270 degrees clock-wise */
-    NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270,
-    /* transforms source by the inverse transform of the screen it is displayed onto. This
-     * transform is applied last */
-    NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY = 0x08
-};
-
-/* parameter for NATIVE_WINDOW_SET_SCALING_MODE
- * keep in sync with Surface.java in frameworks/base */
-enum {
-    /* the window content is not updated (frozen) until a buffer of
-     * the window size is received (enqueued)
-     */
-    NATIVE_WINDOW_SCALING_MODE_FREEZE           = 0,
-    /* the buffer is scaled in both dimensions to match the window size */
-    NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW  = 1,
-    /* the buffer is scaled uniformly such that the smaller dimension
-     * of the buffer matches the window size (cropping in the process)
-     */
-    NATIVE_WINDOW_SCALING_MODE_SCALE_CROP       = 2,
-    /* the window is clipped to the size of the buffer's crop rectangle; pixels
-     * outside the crop rectangle are treated as if they are completely
-     * transparent.
-     */
-    NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP    = 3,
-};
-
-/* values returned by the NATIVE_WINDOW_CONCRETE_TYPE query */
-enum {
-    NATIVE_WINDOW_FRAMEBUFFER               = 0, /* FramebufferNativeWindow */
-    NATIVE_WINDOW_SURFACE                   = 1, /* Surface */
-};
-
-/* parameter for NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP
- *
- * Special timestamp value to indicate that timestamps should be auto-generated
- * by the native window when queueBuffer is called.  This is equal to INT64_MIN,
- * defined directly to avoid problems with C99/C++ inclusion of stdint.h.
- */
-static const int64_t NATIVE_WINDOW_TIMESTAMP_AUTO = (-9223372036854775807LL-1);
-
-/* parameter for NATIVE_WINDOW_GET_FRAME_TIMESTAMPS
- *
- * Special timestamp value to indicate the timestamps aren't yet known or
- * that they are invalid.
- */
-static const int64_t NATIVE_WINDOW_TIMESTAMP_PENDING = -2;
-static const int64_t NATIVE_WINDOW_TIMESTAMP_INVALID = -1;
-
-struct ANativeWindow
-{
-#ifdef __cplusplus
-    ANativeWindow()
-        : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0)
-    {
-        common.magic = ANDROID_NATIVE_WINDOW_MAGIC;
-        common.version = sizeof(ANativeWindow);
-        memset(common.reserved, 0, sizeof(common.reserved));
-    }
-
-    /* Implement the methods that sp<ANativeWindow> expects so that it
-       can be used to automatically refcount ANativeWindow's. */
-    void incStrong(const void* /*id*/) const {
-        common.incRef(const_cast<android_native_base_t*>(&common));
-    }
-    void decStrong(const void* /*id*/) const {
-        common.decRef(const_cast<android_native_base_t*>(&common));
-    }
-#endif
-
-    struct android_native_base_t common;
-
-    /* flags describing some attributes of this surface or its updater */
-    const uint32_t flags;
-
-    /* min swap interval supported by this updated */
-    const int   minSwapInterval;
-
-    /* max swap interval supported by this updated */
-    const int   maxSwapInterval;
-
-    /* horizontal and vertical resolution in DPI */
-    const float xdpi;
-    const float ydpi;
-
-    /* Some storage reserved for the OEM's driver. */
-    intptr_t    oem[4];
-
-    /*
-     * Set the swap interval for this surface.
-     *
-     * Returns 0 on success or -errno on error.
-     */
-    int     (*setSwapInterval)(struct ANativeWindow* window,
-                int interval);
-
-    /*
-     * Hook called by EGL to acquire a buffer. After this call, the buffer
-     * is not locked, so its content cannot be modified. This call may block if
-     * no buffers are available.
-     *
-     * The window holds a reference to the buffer between dequeueBuffer and
-     * either queueBuffer or cancelBuffer, so clients only need their own
-     * reference if they might use the buffer after queueing or canceling it.
-     * Holding a reference to a buffer after queueing or canceling it is only
-     * allowed if a specific buffer count has been set.
-     *
-     * Returns 0 on success or -errno on error.
-     *
-     * XXX: This function is deprecated.  It will continue to work for some
-     * time for binary compatibility, but the new dequeueBuffer function that
-     * outputs a fence file descriptor should be used in its place.
-     */
-    int     (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window,
-                struct ANativeWindowBuffer** buffer);
-
-    /*
-     * hook called by EGL to lock a buffer. This MUST be called before modifying
-     * the content of a buffer. The buffer must have been acquired with
-     * dequeueBuffer first.
-     *
-     * Returns 0 on success or -errno on error.
-     *
-     * XXX: This function is deprecated.  It will continue to work for some
-     * time for binary compatibility, but it is essentially a no-op, and calls
-     * to it should be removed.
-     */
-    int     (*lockBuffer_DEPRECATED)(struct ANativeWindow* window,
-                struct ANativeWindowBuffer* buffer);
-
-    /*
-     * Hook called by EGL when modifications to the render buffer are done.
-     * This unlocks and post the buffer.
-     *
-     * The window holds a reference to the buffer between dequeueBuffer and
-     * either queueBuffer or cancelBuffer, so clients only need their own
-     * reference if they might use the buffer after queueing or canceling it.
-     * Holding a reference to a buffer after queueing or canceling it is only
-     * allowed if a specific buffer count has been set.
-     *
-     * Buffers MUST be queued in the same order than they were dequeued.
-     *
-     * Returns 0 on success or -errno on error.
-     *
-     * XXX: This function is deprecated.  It will continue to work for some
-     * time for binary compatibility, but the new queueBuffer function that
-     * takes a fence file descriptor should be used in its place (pass a value
-     * of -1 for the fence file descriptor if there is no valid one to pass).
-     */
-    int     (*queueBuffer_DEPRECATED)(struct ANativeWindow* window,
-                struct ANativeWindowBuffer* buffer);
-
-    /*
-     * hook used to retrieve information about the native window.
-     *
-     * Returns 0 on success or -errno on error.
-     */
-    int     (*query)(const struct ANativeWindow* window,
-                int what, int* value);
-
-    /*
-     * hook used to perform various operations on the surface.
-     * (*perform)() is a generic mechanism to add functionality to
-     * ANativeWindow while keeping backward binary compatibility.
-     *
-     * DO NOT CALL THIS HOOK DIRECTLY.  Instead, use the helper functions
-     * defined below.
-     *
-     * (*perform)() returns -ENOENT if the 'what' parameter is not supported
-     * by the surface's implementation.
-     *
-     * See above for a list of valid operations, such as
-     * NATIVE_WINDOW_SET_USAGE or NATIVE_WINDOW_CONNECT
-     */
-    int     (*perform)(struct ANativeWindow* window,
-                int operation, ... );
-
-    /*
-     * Hook used to cancel a buffer that has been dequeued.
-     * No synchronization is performed between dequeue() and cancel(), so
-     * either external synchronization is needed, or these functions must be
-     * called from the same thread.
-     *
-     * The window holds a reference to the buffer between dequeueBuffer and
-     * either queueBuffer or cancelBuffer, so clients only need their own
-     * reference if they might use the buffer after queueing or canceling it.
-     * Holding a reference to a buffer after queueing or canceling it is only
-     * allowed if a specific buffer count has been set.
-     *
-     * XXX: This function is deprecated.  It will continue to work for some
-     * time for binary compatibility, but the new cancelBuffer function that
-     * takes a fence file descriptor should be used in its place (pass a value
-     * of -1 for the fence file descriptor if there is no valid one to pass).
-     */
-    int     (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window,
-                struct ANativeWindowBuffer* buffer);
-
-    /*
-     * Hook called by EGL to acquire a buffer. This call may block if no
-     * buffers are available.
-     *
-     * The window holds a reference to the buffer between dequeueBuffer and
-     * either queueBuffer or cancelBuffer, so clients only need their own
-     * reference if they might use the buffer after queueing or canceling it.
-     * Holding a reference to a buffer after queueing or canceling it is only
-     * allowed if a specific buffer count has been set.
-     *
-     * The libsync fence file descriptor returned in the int pointed to by the
-     * fenceFd argument will refer to the fence that must signal before the
-     * dequeued buffer may be written to.  A value of -1 indicates that the
-     * caller may access the buffer immediately without waiting on a fence.  If
-     * a valid file descriptor is returned (i.e. any value except -1) then the
-     * caller is responsible for closing the file descriptor.
-     *
-     * Returns 0 on success or -errno on error.
-     */
-    int     (*dequeueBuffer)(struct ANativeWindow* window,
-                struct ANativeWindowBuffer** buffer, int* fenceFd);
-
-    /*
-     * Hook called by EGL when modifications to the render buffer are done.
-     * This unlocks and post the buffer.
-     *
-     * The window holds a reference to the buffer between dequeueBuffer and
-     * either queueBuffer or cancelBuffer, so clients only need their own
-     * reference if they might use the buffer after queueing or canceling it.
-     * Holding a reference to a buffer after queueing or canceling it is only
-     * allowed if a specific buffer count has been set.
-     *
-     * The fenceFd argument specifies a libsync fence file descriptor for a
-     * fence that must signal before the buffer can be accessed.  If the buffer
-     * can be accessed immediately then a value of -1 should be used.  The
-     * caller must not use the file descriptor after it is passed to
-     * queueBuffer, and the ANativeWindow implementation is responsible for
-     * closing it.
-     *
-     * Returns 0 on success or -errno on error.
-     */
-    int     (*queueBuffer)(struct ANativeWindow* window,
-                struct ANativeWindowBuffer* buffer, int fenceFd);
-
-    /*
-     * Hook used to cancel a buffer that has been dequeued.
-     * No synchronization is performed between dequeue() and cancel(), so
-     * either external synchronization is needed, or these functions must be
-     * called from the same thread.
-     *
-     * The window holds a reference to the buffer between dequeueBuffer and
-     * either queueBuffer or cancelBuffer, so clients only need their own
-     * reference if they might use the buffer after queueing or canceling it.
-     * Holding a reference to a buffer after queueing or canceling it is only
-     * allowed if a specific buffer count has been set.
-     *
-     * The fenceFd argument specifies a libsync fence file decsriptor for a
-     * fence that must signal before the buffer can be accessed.  If the buffer
-     * can be accessed immediately then a value of -1 should be used.
-     *
-     * Note that if the client has not waited on the fence that was returned
-     * from dequeueBuffer, that same fence should be passed to cancelBuffer to
-     * ensure that future uses of the buffer are preceded by a wait on that
-     * fence.  The caller must not use the file descriptor after it is passed
-     * to cancelBuffer, and the ANativeWindow implementation is responsible for
-     * closing it.
-     *
-     * Returns 0 on success or -errno on error.
-     */
-    int     (*cancelBuffer)(struct ANativeWindow* window,
-                struct ANativeWindowBuffer* buffer, int fenceFd);
-};
-
- /* Backwards compatibility: use ANativeWindow (struct ANativeWindow in C).
-  * android_native_window_t is deprecated.
-  */
-typedef struct ANativeWindow ANativeWindow;
-typedef struct ANativeWindow android_native_window_t __deprecated;
-
-/*
- *  native_window_set_usage(..., usage)
- *  Sets the intended usage flags for the next buffers
- *  acquired with (*lockBuffer)() and on.
- *  By default (if this function is never called), a usage of
- *      GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE
- *  is assumed.
- *  Calling this function will usually cause following buffers to be
- *  reallocated.
- */
-
-static inline int native_window_set_usage(
-        struct ANativeWindow* window, int usage)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_USAGE, usage);
-}
-
-/* deprecated. Always returns 0. Don't call. */
-static inline int native_window_connect(
-        struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated;
-
-static inline int native_window_connect(
-        struct ANativeWindow* window __UNUSED, int api __UNUSED) {
-    return 0;
-}
-
-/* deprecated. Always returns 0. Don't call. */
-static inline int native_window_disconnect(
-        struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated;
-
-static inline int native_window_disconnect(
-        struct ANativeWindow* window __UNUSED, int api __UNUSED) {
-    return 0;
-}
-
-/*
- * native_window_set_crop(..., crop)
- * Sets which region of the next queued buffers needs to be considered.
- * Depending on the scaling mode, a buffer's crop region is scaled and/or
- * cropped to match the surface's size.  This function sets the crop in
- * pre-transformed buffer pixel coordinates.
- *
- * The specified crop region applies to all buffers queued after it is called.
- *
- * If 'crop' is NULL, subsequently queued buffers won't be cropped.
- *
- * An error is returned if for instance the crop region is invalid, out of the
- * buffer's bound or if the window is invalid.
- */
-static inline int native_window_set_crop(
-        struct ANativeWindow* window,
-        android_native_rect_t const * crop)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_CROP, crop);
-}
-
-/*
- * native_window_set_post_transform_crop(..., crop)
- * Sets which region of the next queued buffers needs to be considered.
- * Depending on the scaling mode, a buffer's crop region is scaled and/or
- * cropped to match the surface's size.  This function sets the crop in
- * post-transformed pixel coordinates.
- *
- * The specified crop region applies to all buffers queued after it is called.
- *
- * If 'crop' is NULL, subsequently queued buffers won't be cropped.
- *
- * An error is returned if for instance the crop region is invalid, out of the
- * buffer's bound or if the window is invalid.
- */
-static inline int native_window_set_post_transform_crop(
-        struct ANativeWindow* window,
-        android_native_rect_t const * crop)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_POST_TRANSFORM_CROP, crop);
-}
-
-/*
- * native_window_set_active_rect(..., active_rect)
- *
- * This function is deprecated and will be removed soon.  For now it simply
- * sets the post-transform crop for compatibility while multi-project commits
- * get checked.
- */
-static inline int native_window_set_active_rect(
-        struct ANativeWindow* window,
-        android_native_rect_t const * active_rect) __deprecated;
-
-static inline int native_window_set_active_rect(
-        struct ANativeWindow* window,
-        android_native_rect_t const * active_rect)
-{
-    return native_window_set_post_transform_crop(window, active_rect);
-}
-
-/*
- * native_window_set_buffer_count(..., count)
- * Sets the number of buffers associated with this native window.
- */
-static inline int native_window_set_buffer_count(
-        struct ANativeWindow* window,
-        size_t bufferCount)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount);
-}
-
-/*
- * native_window_set_buffers_geometry(..., int w, int h, int format)
- * All buffers dequeued after this call will have the dimensions and format
- * specified.  A successful call to this function has the same effect as calling
- * native_window_set_buffers_size and native_window_set_buffers_format.
- *
- * XXX: This function is deprecated.  The native_window_set_buffers_dimensions
- * and native_window_set_buffers_format functions should be used instead.
- */
-static inline int native_window_set_buffers_geometry(
-        struct ANativeWindow* window,
-        int w, int h, int format) __deprecated;
-
-static inline int native_window_set_buffers_geometry(
-        struct ANativeWindow* window,
-        int w, int h, int format)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
-            w, h, format);
-}
-
-/*
- * native_window_set_buffers_dimensions(..., int w, int h)
- * All buffers dequeued after this call will have the dimensions specified.
- * In particular, all buffers will have a fixed-size, independent from the
- * native-window size. They will be scaled according to the scaling mode
- * (see native_window_set_scaling_mode) upon window composition.
- *
- * If w and h are 0, the normal behavior is restored. That is, dequeued buffers
- * following this call will be sized to match the window's size.
- *
- * Calling this function will reset the window crop to a NULL value, which
- * disables cropping of the buffers.
- */
-static inline int native_window_set_buffers_dimensions(
-        struct ANativeWindow* window,
-        int w, int h)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS,
-            w, h);
-}
-
-/*
- * native_window_set_buffers_user_dimensions(..., int w, int h)
- *
- * Sets the user buffer size for the window, which overrides the
- * window's size.  All buffers dequeued after this call will have the
- * dimensions specified unless overridden by
- * native_window_set_buffers_dimensions.  All buffers will have a
- * fixed-size, independent from the native-window size. They will be
- * scaled according to the scaling mode (see
- * native_window_set_scaling_mode) upon window composition.
- *
- * If w and h are 0, the normal behavior is restored. That is, the
- * default buffer size will match the windows's size.
- *
- * Calling this function will reset the window crop to a NULL value, which
- * disables cropping of the buffers.
- */
-static inline int native_window_set_buffers_user_dimensions(
-        struct ANativeWindow* window,
-        int w, int h)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS,
-            w, h);
-}
-
-/*
- * native_window_set_buffers_format(..., int format)
- * All buffers dequeued after this call will have the format specified.
- *
- * If the specified format is 0, the default buffer format will be used.
- */
-static inline int native_window_set_buffers_format(
-        struct ANativeWindow* window,
-        int format)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_FORMAT, format);
-}
-
-/*
- * native_window_set_buffers_data_space(..., int dataSpace)
- * All buffers queued after this call will be associated with the dataSpace
- * parameter specified.
- *
- * dataSpace specifies additional information about the buffer that's dependent
- * on the buffer format and the endpoints. For example, it can be used to convey
- * the color space of the image data in the buffer, or it can be used to
- * indicate that the buffers contain depth measurement data instead of color
- * images.  The default dataSpace is 0, HAL_DATASPACE_UNKNOWN, unless it has been
- * overridden by the consumer.
- */
-static inline int native_window_set_buffers_data_space(
-        struct ANativeWindow* window,
-        android_dataspace_t dataSpace)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DATASPACE,
-            dataSpace);
-}
-
-/*
- * native_window_set_buffers_transform(..., int transform)
- * All buffers queued after this call will be displayed transformed according
- * to the transform parameter specified.
- */
-static inline int native_window_set_buffers_transform(
-        struct ANativeWindow* window,
-        int transform)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
-            transform);
-}
-
-/*
- * native_window_set_buffers_sticky_transform(..., int transform)
- * All buffers queued after this call will be displayed transformed according
- * to the transform parameter specified applied on top of the regular buffer
- * transform.  Setting this transform will disable the transform hint.
- *
- * Temporary - This is only intended to be used by the LEGACY camera mode, do
- *   not use this for anything else.
- */
-static inline int native_window_set_buffers_sticky_transform(
-        struct ANativeWindow* window,
-        int transform)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM,
-            transform);
-}
-
-/*
- * native_window_set_buffers_timestamp(..., int64_t timestamp)
- * All buffers queued after this call will be associated with the timestamp
- * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO
- * (the default), timestamps will be generated automatically when queueBuffer is
- * called. The timestamp is measured in nanoseconds, and is normally monotonically
- * increasing. The timestamp should be unaffected by time-of-day adjustments,
- * and for a camera should be strictly monotonic but for a media player may be
- * reset when the position is set.
- */
-static inline int native_window_set_buffers_timestamp(
-        struct ANativeWindow* window,
-        int64_t timestamp)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP,
-            timestamp);
-}
-
-/*
- * native_window_set_scaling_mode(..., int mode)
- * All buffers queued after this call will be associated with the scaling mode
- * specified.
- */
-static inline int native_window_set_scaling_mode(
-        struct ANativeWindow* window,
-        int mode)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_SCALING_MODE,
-            mode);
-}
-
-/*
- * native_window_api_connect(..., int api)
- * connects an API to this window. only one API can be connected at a time.
- * Returns -EINVAL if for some reason the window cannot be connected, which
- * can happen if it's connected to some other API.
- */
-static inline int native_window_api_connect(
-        struct ANativeWindow* window, int api)
-{
-    return window->perform(window, NATIVE_WINDOW_API_CONNECT, api);
-}
-
-/*
- * native_window_api_disconnect(..., int api)
- * disconnect the API from this window.
- * An error is returned if for instance the window wasn't connected in the
- * first place.
- */
-static inline int native_window_api_disconnect(
-        struct ANativeWindow* window, int api)
-{
-    return window->perform(window, NATIVE_WINDOW_API_DISCONNECT, api);
-}
-
-/*
- * native_window_dequeue_buffer_and_wait(...)
- * Dequeue a buffer and wait on the fence associated with that buffer.  The
- * buffer may safely be accessed immediately upon this function returning.  An
- * error is returned if either of the dequeue or the wait operations fail.
- */
-static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw,
-        struct ANativeWindowBuffer** anb) {
-    return anw->dequeueBuffer_DEPRECATED(anw, anb);
-}
-
-/*
- * native_window_set_sideband_stream(..., native_handle_t*)
- * Attach a sideband buffer stream to a native window.
- */
-static inline int native_window_set_sideband_stream(
-        struct ANativeWindow* window,
-        native_handle_t* sidebandHandle)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_SIDEBAND_STREAM,
-            sidebandHandle);
-}
-
-/*
- * native_window_set_surface_damage(..., android_native_rect_t* rects, int numRects)
- * Set the surface damage (i.e., the region of the surface that has changed
- * since the previous frame). The damage set by this call will be reset (to the
- * default of full-surface damage) after calling queue, so this must be called
- * prior to every frame with damage that does not cover the whole surface if the
- * caller desires downstream consumers to use this optimization.
- *
- * The damage region is specified as an array of rectangles, with the important
- * caveat that the origin of the surface is considered to be the bottom-left
- * corner, as in OpenGL ES.
- *
- * If numRects is set to 0, rects may be NULL, and the surface damage will be
- * set to the full surface (the same as if this function had not been called for
- * this frame).
- */
-static inline int native_window_set_surface_damage(
-        struct ANativeWindow* window,
-        const android_native_rect_t* rects, size_t numRects)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_SURFACE_DAMAGE,
-            rects, numRects);
-}
-
-/*
- * native_window_set_shared_buffer_mode(..., bool sharedBufferMode)
- * Enable/disable shared buffer mode
- */
-static inline int native_window_set_shared_buffer_mode(
-        struct ANativeWindow* window,
-        bool sharedBufferMode)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_SHARED_BUFFER_MODE,
-            sharedBufferMode);
-}
-
-/*
- * native_window_set_auto_refresh(..., autoRefresh)
- * Enable/disable auto refresh when in shared buffer mode
- */
-static inline int native_window_set_auto_refresh(
-        struct ANativeWindow* window,
-        bool autoRefresh)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_AUTO_REFRESH, autoRefresh);
-}
-
-static inline int native_window_get_refresh_cycle_duration(
-        struct ANativeWindow* window,
-        int64_t* outRefreshDuration)
-{
-    return window->perform(window, NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION,
-            outRefreshDuration);
-}
-
-static inline int native_window_get_next_frame_id(
-        struct ANativeWindow* window, uint64_t* frameId)
-{
-    return window->perform(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID, frameId);
-}
-
-static inline int native_window_enable_frame_timestamps(
-        struct ANativeWindow* window, bool enable)
-{
-    return window->perform(window, NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS,
-            enable);
-}
-
-static inline int native_window_get_compositor_timing(
-        struct ANativeWindow* window,
-        int64_t* compositeDeadline, int64_t* compositeInterval,
-        int64_t* compositeToPresentLatency)
-{
-    return window->perform(window, NATIVE_WINDOW_GET_COMPOSITOR_TIMING,
-            compositeDeadline, compositeInterval, compositeToPresentLatency);
-}
-
-static inline int native_window_get_frame_timestamps(
-        struct ANativeWindow* window, uint64_t frameId,
-        int64_t* outRequestedPresentTime, int64_t* outAcquireTime,
-        int64_t* outLatchTime, int64_t* outFirstRefreshStartTime,
-        int64_t* outLastRefreshStartTime, int64_t* outGpuCompositionDoneTime,
-        int64_t* outDisplayPresentTime, int64_t* outDequeueReadyTime,
-        int64_t* outReleaseTime)
-{
-    return window->perform(window, NATIVE_WINDOW_GET_FRAME_TIMESTAMPS,
-            frameId, outRequestedPresentTime, outAcquireTime, outLatchTime,
-            outFirstRefreshStartTime, outLastRefreshStartTime,
-            outGpuCompositionDoneTime, outDisplayPresentTime,
-            outDequeueReadyTime, outReleaseTime);
-}
-
-static inline int native_window_get_wide_color_support(
-    struct ANativeWindow* window, bool* outSupport) {
-  return window->perform(window, NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT,
-                         outSupport);
-}
-
-static inline int native_window_get_hdr_support(struct ANativeWindow* window,
-                                                bool* outSupport) {
-  return window->perform(window, NATIVE_WINDOW_GET_HDR_SUPPORT, outSupport);
-}
-
-__END_DECLS
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index ece623b..eb2b902 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -47,12 +47,19 @@
 
     srcs: [
         "ArmExidx.cpp",
+        "DwarfCfa.cpp",
+        "DwarfMemory.cpp",
+        "DwarfOp.cpp",
+        "DwarfSection.cpp",
         "Elf.cpp",
         "ElfInterface.cpp",
         "ElfInterfaceArm.cpp",
         "Log.cpp",
         "Regs.cpp",
+        "MapInfo.cpp",
+        "Maps.cpp",
         "Memory.cpp",
+        "Symbols.cpp",
     ],
 
     shared_libs: [
@@ -87,16 +94,26 @@
     srcs: [
         "tests/ArmExidxDecodeTest.cpp",
         "tests/ArmExidxExtractTest.cpp",
+        "tests/DwarfCfaLogTest.cpp",
+        "tests/DwarfCfaTest.cpp",
+        "tests/DwarfMemoryTest.cpp",
+        "tests/DwarfOpLogTest.cpp",
+        "tests/DwarfOpTest.cpp",
+        "tests/DwarfSectionTest.cpp",
+        "tests/DwarfSectionImplTest.cpp",
         "tests/ElfInterfaceArmTest.cpp",
         "tests/ElfInterfaceTest.cpp",
         "tests/ElfTest.cpp",
         "tests/LogFake.cpp",
+        "tests/MapInfoTest.cpp",
+        "tests/MapsTest.cpp",
         "tests/MemoryFake.cpp",
         "tests/MemoryFileTest.cpp",
         "tests/MemoryLocalTest.cpp",
         "tests/MemoryRangeTest.cpp",
         "tests/MemoryRemoteTest.cpp",
         "tests/RegsTest.cpp",
+        "tests/SymbolsTest.cpp",
     ],
 
     cflags: [
@@ -109,6 +126,10 @@
         "liblog",
     ],
 
+    static_libs: [
+        "libgmock",
+    ],
+
     target: {
         linux: {
             host_ldlibs: [
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
new file mode 100644
index 0000000..006f039
--- /dev/null
+++ b/libunwindstack/DwarfCfa.cpp
@@ -0,0 +1,713 @@
+/*
+ * 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 <inttypes.h>
+#include <stdint.h>
+
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+
+#include "DwarfCfa.h"
+#include "DwarfEncoding.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "DwarfStructs.h"
+#include "Log.h"
+
+template <typename AddressType>
+constexpr typename DwarfCfa<AddressType>::process_func DwarfCfa<AddressType>::kCallbackTable[64];
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
+                                            dwarf_loc_regs_t* loc_regs) {
+  if (cie_loc_regs_ != nullptr) {
+    for (const auto& entry : *cie_loc_regs_) {
+      (*loc_regs)[entry.first] = entry.second;
+    }
+  }
+  last_error_ = DWARF_ERROR_NONE;
+
+  memory_->set_cur_offset(start_offset);
+  uint64_t cfa_offset;
+  cur_pc_ = fde_->pc_start;
+  while ((cfa_offset = memory_->cur_offset()) < end_offset && cur_pc_ <= pc) {
+    operands_.clear();
+    // Read the cfa information.
+    uint8_t cfa_value;
+    if (!memory_->ReadBytes(&cfa_value, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    uint8_t cfa_low = cfa_value & 0x3f;
+    // Check the 2 high bits.
+    switch (cfa_value >> 6) {
+      case 1:
+        cur_pc_ += cfa_low * fde_->cie->code_alignment_factor;
+        break;
+      case 2: {
+        uint64_t offset;
+        if (!memory_->ReadULEB128(&offset)) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+        SignedType signed_offset =
+            static_cast<SignedType>(offset) * fde_->cie->data_alignment_factor;
+        (*loc_regs)[cfa_low] = {.type = DWARF_LOCATION_OFFSET,
+                                .values = {static_cast<uint64_t>(signed_offset)}};
+        break;
+      }
+      case 3: {
+        if (cie_loc_regs_ == nullptr) {
+          log(0, "restore while processing cie");
+          last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+          return false;
+        }
+
+        auto reg_entry = cie_loc_regs_->find(cfa_low);
+        if (reg_entry == cie_loc_regs_->end()) {
+          loc_regs->erase(cfa_low);
+        } else {
+          (*loc_regs)[cfa_low] = reg_entry->second;
+        }
+        break;
+      }
+      case 0: {
+        const auto handle_func = DwarfCfa<AddressType>::kCallbackTable[cfa_low];
+        if (handle_func == nullptr) {
+          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+          return false;
+        }
+
+        const auto cfa = &DwarfCfaInfo::kTable[cfa_low];
+        for (size_t i = 0; i < cfa->num_operands; i++) {
+          if (cfa->operands[i] == DW_EH_PE_block) {
+            uint64_t block_length;
+            if (!memory_->ReadULEB128(&block_length)) {
+              last_error_ = DWARF_ERROR_MEMORY_INVALID;
+              return false;
+            }
+            operands_.push_back(block_length);
+            memory_->set_cur_offset(memory_->cur_offset() + block_length);
+            continue;
+          }
+          uint64_t value;
+          if (!memory_->ReadEncodedValue<AddressType>(cfa->operands[i], &value)) {
+            last_error_ = DWARF_ERROR_MEMORY_INVALID;
+            return false;
+          }
+          operands_.push_back(value);
+        }
+
+        if (!(this->*handle_func)(loc_regs)) {
+          return false;
+        }
+        break;
+      }
+    }
+  }
+  return true;
+}
+
+template <typename AddressType>
+std::string DwarfCfa<AddressType>::GetOperandString(uint8_t operand, uint64_t value,
+                                                    uint64_t* cur_pc) {
+  std::string string;
+  switch (operand) {
+    case DwarfCfaInfo::DWARF_DISPLAY_REGISTER:
+      string = " register(" + std::to_string(value) + ")";
+      break;
+    case DwarfCfaInfo::DWARF_DISPLAY_SIGNED_NUMBER:
+      string += " " + std::to_string(static_cast<SignedType>(value));
+      break;
+    case DwarfCfaInfo::DWARF_DISPLAY_ADVANCE_LOC:
+      *cur_pc += value;
+    // Fall through to log the value.
+    case DwarfCfaInfo::DWARF_DISPLAY_NUMBER:
+      string += " " + std::to_string(value);
+      break;
+    case DwarfCfaInfo::DWARF_DISPLAY_SET_LOC:
+      *cur_pc = value;
+    // Fall through to log the value.
+    case DwarfCfaInfo::DWARF_DISPLAY_ADDRESS:
+      if (std::is_same<AddressType, uint32_t>::value) {
+        string += android::base::StringPrintf(" 0x%" PRIx32, static_cast<uint32_t>(value));
+      } else {
+        string += android::base::StringPrintf(" 0x%" PRIx64, static_cast<uint64_t>(value));
+      }
+      break;
+    default:
+      string = " unknown";
+  }
+  return string;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::LogOffsetRegisterString(uint32_t indent, uint64_t cfa_offset,
+                                                    uint8_t reg) {
+  uint64_t offset;
+  if (!memory_->ReadULEB128(&offset)) {
+    return false;
+  }
+  uint64_t end_offset = memory_->cur_offset();
+  memory_->set_cur_offset(cfa_offset);
+
+  std::string raw_data = "Raw Data:";
+  for (uint64_t i = cfa_offset; i < end_offset; i++) {
+    uint8_t value;
+    if (!memory_->ReadBytes(&value, 1)) {
+      return false;
+    }
+    raw_data += android::base::StringPrintf(" 0x%02x", value);
+  }
+  log(indent, "DW_CFA_offset register(%d) %" PRId64, reg, offset);
+  log(indent, "%s", raw_data.c_str());
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
+                                           uint64_t* cur_pc) {
+  const auto* cfa = &DwarfCfaInfo::kTable[op];
+  if (cfa->name == nullptr) {
+    log(indent, "Illegal");
+    log(indent, "Raw Data: 0x%02x", op);
+    return true;
+  }
+
+  std::string log_string(cfa->name);
+  std::vector<std::string> expression_lines;
+  for (size_t i = 0; i < cfa->num_operands; i++) {
+    if (cfa->operands[i] == DW_EH_PE_block) {
+      // This is a Dwarf Expression.
+      uint64_t end_offset;
+      if (!memory_->ReadULEB128(&end_offset)) {
+        return false;
+      }
+      log_string += " " + std::to_string(end_offset);
+      end_offset += memory_->cur_offset();
+
+      DwarfOp<AddressType> op(memory_, nullptr);
+      op.GetLogInfo(memory_->cur_offset(), end_offset, &expression_lines);
+      memory_->set_cur_offset(end_offset);
+    } else {
+      uint64_t value;
+      if (!memory_->ReadEncodedValue<AddressType>(cfa->operands[i], &value)) {
+        return false;
+      }
+      log_string += GetOperandString(cfa->display_operands[i], value, cur_pc);
+    }
+  }
+  log(indent, "%s", log_string.c_str());
+
+  // Get the raw bytes of the data.
+  uint64_t end_offset = memory_->cur_offset();
+  memory_->set_cur_offset(cfa_offset);
+  std::string raw_data("Raw Data:");
+  for (uint64_t i = 0; i < end_offset - cfa_offset; i++) {
+    uint8_t value;
+    if (!memory_->ReadBytes(&value, 1)) {
+      return false;
+    }
+
+    // Only show 10 raw bytes per line.
+    if ((i % 10) == 0 && i != 0) {
+      log(indent, "%s", raw_data.c_str());
+      raw_data.clear();
+    }
+    if (raw_data.empty()) {
+      raw_data = "Raw Data:";
+    }
+    raw_data += android::base::StringPrintf(" 0x%02x", value);
+  }
+  if (!raw_data.empty()) {
+    log(indent, "%s", raw_data.c_str());
+  }
+
+  // Log any of the expression data.
+  for (const auto line : expression_lines) {
+    log(indent + 1, "%s", line.c_str());
+  }
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t load_bias,
+                                uint64_t start_offset, uint64_t end_offset) {
+  memory_->set_cur_offset(start_offset);
+  uint64_t cfa_offset;
+  uint64_t cur_pc = fde_->pc_start;
+  uint64_t old_pc = cur_pc;
+  while ((cfa_offset = memory_->cur_offset()) < end_offset && cur_pc <= pc) {
+    // Read the cfa information.
+    uint8_t cfa_value;
+    if (!memory_->ReadBytes(&cfa_value, 1)) {
+      return false;
+    }
+
+    // Check the 2 high bits.
+    uint8_t cfa_low = cfa_value & 0x3f;
+    switch (cfa_value >> 6) {
+      case 0:
+        if (!LogInstruction(indent, cfa_offset, cfa_low, &cur_pc)) {
+          return false;
+        }
+        break;
+      case 1:
+        log(indent, "DW_CFA_advance_loc %d", cfa_low);
+        log(indent, "Raw Data: 0x%02x", cfa_value);
+        cur_pc += cfa_low * fde_->cie->code_alignment_factor;
+        break;
+      case 2:
+        if (!LogOffsetRegisterString(indent, cfa_offset, cfa_low)) {
+          return false;
+        }
+        break;
+      case 3:
+        log(indent, "DW_CFA_restore register(%d)", cfa_low);
+        log(indent, "Raw Data: 0x%02x", cfa_value);
+        break;
+    }
+    if (cur_pc != old_pc) {
+      log(indent, "");
+      log(indent, "PC 0x%" PRIx64, cur_pc + load_bias);
+    }
+    old_pc = cur_pc;
+  }
+  return true;
+}
+
+// Static data.
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_nop(dwarf_loc_regs_t*) {
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_set_loc(dwarf_loc_regs_t*) {
+  AddressType cur_pc = cur_pc_;
+  AddressType new_pc = operands_[0];
+  if (new_pc < cur_pc) {
+    if (std::is_same<AddressType, uint32_t>::value) {
+      log(0, "Warning: PC is moving backwards: old 0x%" PRIx32 " new 0x%" PRIx32, cur_pc, new_pc);
+    } else {
+      log(0, "Warning: PC is moving backwards: old 0x%" PRIx64 " new 0x%" PRIx64, cur_pc, new_pc);
+    }
+  }
+  cur_pc_ = new_pc;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_advance_loc(dwarf_loc_regs_t*) {
+  cur_pc_ += operands_[0] * fde_->cie->code_alignment_factor;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_offset(dwarf_loc_regs_t* loc_regs) {
+  AddressType reg = operands_[0];
+  (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {operands_[1]}};
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_restore(dwarf_loc_regs_t* loc_regs) {
+  AddressType reg = operands_[0];
+  if (cie_loc_regs_ == nullptr) {
+    log(0, "restore while processing cie");
+    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    return false;
+  }
+  auto reg_entry = cie_loc_regs_->find(reg);
+  if (reg_entry == cie_loc_regs_->end()) {
+    loc_regs->erase(reg);
+  } else {
+    (*loc_regs)[reg] = reg_entry->second;
+  }
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_undefined(dwarf_loc_regs_t* loc_regs) {
+  AddressType reg = operands_[0];
+  (*loc_regs)[reg] = {.type = DWARF_LOCATION_UNDEFINED};
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_same_value(dwarf_loc_regs_t* loc_regs) {
+  AddressType reg = operands_[0];
+  loc_regs->erase(reg);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_register(dwarf_loc_regs_t* loc_regs) {
+  AddressType reg = operands_[0];
+  AddressType reg_dst = operands_[1];
+  (*loc_regs)[reg] = {.type = DWARF_LOCATION_REGISTER, .values = {reg_dst}};
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_remember_state(dwarf_loc_regs_t* loc_regs) {
+  loc_reg_state_.push(*loc_regs);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_restore_state(dwarf_loc_regs_t* loc_regs) {
+  if (loc_reg_state_.size() == 0) {
+    log(0, "Warning: Attempt to restore without remember.");
+    return true;
+  }
+  *loc_regs = loc_reg_state_.top();
+  loc_reg_state_.pop();
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa(dwarf_loc_regs_t* loc_regs) {
+  (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {operands_[0], operands_[1]}};
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_register(dwarf_loc_regs_t* loc_regs) {
+  auto cfa_location = loc_regs->find(CFA_REG);
+  if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
+    log(0, "Attempt to set new register, but cfa is not already set to a register.");
+    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    return false;
+  }
+
+  cfa_location->second.values[0] = operands_[0];
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_offset(dwarf_loc_regs_t* loc_regs) {
+  // Changing the offset if this is not a register is illegal.
+  auto cfa_location = loc_regs->find(CFA_REG);
+  if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
+    log(0, "Attempt to set offset, but cfa is not set to a register.");
+    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    return false;
+  }
+  cfa_location->second.values[1] = operands_[0];
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_expression(dwarf_loc_regs_t* loc_regs) {
+  (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_EXPRESSION,
+                          .values = {operands_[0], memory_->cur_offset()}};
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_expression(dwarf_loc_regs_t* loc_regs) {
+  AddressType reg = operands_[0];
+  (*loc_regs)[reg] = {.type = DWARF_LOCATION_EXPRESSION,
+                      .values = {operands_[1], memory_->cur_offset()}};
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_offset_extended_sf(dwarf_loc_regs_t* loc_regs) {
+  AddressType reg = operands_[0];
+  SignedType value = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+  (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast<uint64_t>(value)}};
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_sf(dwarf_loc_regs_t* loc_regs) {
+  SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+  (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_REGISTER,
+                          .values = {operands_[0], static_cast<uint64_t>(offset)}};
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_offset_sf(dwarf_loc_regs_t* loc_regs) {
+  // Changing the offset if this is not a register is illegal.
+  auto cfa_location = loc_regs->find(CFA_REG);
+  if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
+    log(0, "Attempt to set offset, but cfa is not set to a register.");
+    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    return false;
+  }
+  SignedType offset = static_cast<SignedType>(operands_[0]) * fde_->cie->data_alignment_factor;
+  cfa_location->second.values[1] = static_cast<uint64_t>(offset);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_val_offset(dwarf_loc_regs_t* loc_regs) {
+  AddressType reg = operands_[0];
+  SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+  (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast<uint64_t>(offset)}};
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_val_offset_sf(dwarf_loc_regs_t* loc_regs) {
+  AddressType reg = operands_[0];
+  SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+  (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast<uint64_t>(offset)}};
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_val_expression(dwarf_loc_regs_t* loc_regs) {
+  AddressType reg = operands_[0];
+  (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_EXPRESSION,
+                      .values = {operands_[1], memory_->cur_offset()}};
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_gnu_negative_offset_extended(dwarf_loc_regs_t* loc_regs) {
+  AddressType reg = operands_[0];
+  SignedType offset = -static_cast<SignedType>(operands_[1]);
+  (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast<uint64_t>(offset)}};
+  return true;
+}
+
+const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = {
+    {
+        // 0x00 DW_CFA_nop
+        "DW_CFA_nop",
+        2,
+        0,
+        {},
+        {},
+    },
+    {
+        "DW_CFA_set_loc",  // 0x01 DW_CFA_set_loc
+        2,
+        1,
+        {DW_EH_PE_absptr},
+        {DWARF_DISPLAY_SET_LOC},
+    },
+    {
+        "DW_CFA_advance_loc1",  // 0x02 DW_CFA_advance_loc1
+        2,
+        1,
+        {DW_EH_PE_udata1},
+        {DWARF_DISPLAY_ADVANCE_LOC},
+    },
+    {
+        "DW_CFA_advance_loc2",  // 0x03 DW_CFA_advance_loc2
+        2,
+        1,
+        {DW_EH_PE_udata2},
+        {DWARF_DISPLAY_ADVANCE_LOC},
+    },
+    {
+        "DW_CFA_advance_loc4",  // 0x04 DW_CFA_advance_loc4
+        2,
+        1,
+        {DW_EH_PE_udata4},
+        {DWARF_DISPLAY_ADVANCE_LOC},
+    },
+    {
+        "DW_CFA_offset_extended",  // 0x05 DW_CFA_offset_extended
+        2,
+        2,
+        {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+        {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+    },
+    {
+        "DW_CFA_restore_extended",  // 0x06 DW_CFA_restore_extended
+        2,
+        1,
+        {DW_EH_PE_uleb128},
+        {DWARF_DISPLAY_REGISTER},
+    },
+    {
+        "DW_CFA_undefined",  // 0x07 DW_CFA_undefined
+        2,
+        1,
+        {DW_EH_PE_uleb128},
+        {DWARF_DISPLAY_REGISTER},
+    },
+    {
+        "DW_CFA_same_value",  // 0x08 DW_CFA_same_value
+        2,
+        1,
+        {DW_EH_PE_uleb128},
+        {DWARF_DISPLAY_REGISTER},
+    },
+    {
+        "DW_CFA_register",  // 0x09 DW_CFA_register
+        2,
+        2,
+        {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+        {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_REGISTER},
+    },
+    {
+        "DW_CFA_remember_state",  // 0x0a DW_CFA_remember_state
+        2,
+        0,
+        {},
+        {},
+    },
+    {
+        "DW_CFA_restore_state",  // 0x0b DW_CFA_restore_state
+        2,
+        0,
+        {},
+        {},
+    },
+    {
+        "DW_CFA_def_cfa",  // 0x0c DW_CFA_def_cfa
+        2,
+        2,
+        {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+        {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+    },
+    {
+        "DW_CFA_def_cfa_register",  // 0x0d DW_CFA_def_cfa_register
+        2,
+        1,
+        {DW_EH_PE_uleb128},
+        {DWARF_DISPLAY_REGISTER},
+    },
+    {
+        "DW_CFA_def_cfa_offset",  // 0x0e DW_CFA_def_cfa_offset
+        2,
+        1,
+        {DW_EH_PE_uleb128},
+        {DWARF_DISPLAY_NUMBER},
+    },
+    {
+        "DW_CFA_def_cfa_expression",  // 0x0f DW_CFA_def_cfa_expression
+        2,
+        1,
+        {DW_EH_PE_block},
+        {DWARF_DISPLAY_EVAL_BLOCK},
+    },
+    {
+        "DW_CFA_expression",  // 0x10 DW_CFA_expression
+        2,
+        2,
+        {DW_EH_PE_uleb128, DW_EH_PE_block},
+        {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
+    },
+    {
+        "DW_CFA_offset_extended_sf",  // 0x11 DW_CFA_offset_extend_sf
+        2,
+        2,
+        {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+        {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
+    },
+    {
+        "DW_CFA_def_cfa_sf",  // 0x12 DW_CFA_def_cfa_sf
+        2,
+        2,
+        {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+        {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
+    },
+    {
+        "DW_CFA_def_cfa_offset_sf",  // 0x13 DW_CFA_def_cfa_offset_sf
+        2,
+        1,
+        {DW_EH_PE_sleb128},
+        {DWARF_DISPLAY_SIGNED_NUMBER},
+    },
+    {
+        "DW_CFA_val_offset",  // 0x14 DW_CFA_val_offset
+        2,
+        2,
+        {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+        {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+    },
+    {
+        "DW_CFA_val_offset_sf",  // 0x15 DW_CFA_val_offset_sf
+        2,
+        2,
+        {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+        {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
+    },
+    {
+        "DW_CFA_val_expression",  // 0x16 DW_CFA_val_expression
+        2,
+        2,
+        {DW_EH_PE_uleb128, DW_EH_PE_block},
+        {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
+    },
+    {nullptr, 0, 0, {}, {}},  // 0x17 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x18 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x19 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x1a illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x1b illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x1c DW_CFA_lo_user (Treat as illegal)
+    {nullptr, 0, 0, {}, {}},  // 0x1d illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x1e illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x1f illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x20 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x21 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x22 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x23 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x24 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x25 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x26 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x27 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x28 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x29 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x2a illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x2b illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x2c illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
+    {
+        "DW_CFA_GNU_args_size",  // 0x2e DW_CFA_GNU_args_size
+        2,
+        1,
+        {DW_EH_PE_uleb128},
+        {DWARF_DISPLAY_NUMBER},
+    },
+    {
+        "DW_CFA_GNU_negative_offset_extended",  // 0x2f DW_CFA_GNU_negative_offset_extended
+        2,
+        2,
+        {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+        {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+    },
+    {nullptr, 0, 0, {}, {}},  // 0x31 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x32 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x33 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x34 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x35 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x36 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x37 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x38 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x39 illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x3a illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x3b illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x3c illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x3d illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x3e illegal cfa
+    {nullptr, 0, 0, {}, {}},  // 0x3f DW_CFA_hi_user (Treat as illegal)
+};
+
+// Explicitly instantiate DwarfCfa.
+template class DwarfCfa<uint32_t>;
+template class DwarfCfa<uint64_t>;
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
new file mode 100644
index 0000000..42ebae1
--- /dev/null
+++ b/libunwindstack/DwarfCfa.h
@@ -0,0 +1,255 @@
+/*
+ * 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_CFA_H
+#define _LIBUNWINDSTACK_DWARF_CFA_H
+
+#include <stdint.h>
+
+#include <stack>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "DwarfError.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfStructs.h"
+
+// DWARF Standard home: http://dwarfstd.org/
+// This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
+// See section 6.4.2.1 for a description of the DW_CFA_xxx values.
+
+class DwarfCfaInfo {
+ public:
+  enum DisplayType : uint8_t {
+    DWARF_DISPLAY_NONE = 0,
+    DWARF_DISPLAY_REGISTER,
+    DWARF_DISPLAY_NUMBER,
+    DWARF_DISPLAY_SIGNED_NUMBER,
+    DWARF_DISPLAY_EVAL_BLOCK,
+    DWARF_DISPLAY_ADDRESS,
+    DWARF_DISPLAY_SET_LOC,
+    DWARF_DISPLAY_ADVANCE_LOC,
+  };
+
+  struct Info {
+    const char* name;
+    uint8_t supported_version;
+    uint8_t num_operands;
+    uint8_t operands[2];
+    uint8_t display_operands[2];
+  };
+
+  const static Info kTable[64];
+};
+
+template <typename AddressType>
+class DwarfCfa {
+  // Signed version of AddressType
+  typedef typename std::make_signed<AddressType>::type SignedType;
+
+ public:
+  DwarfCfa(DwarfMemory* memory, const DwarfFde* fde) : memory_(memory), fde_(fde) {}
+  virtual ~DwarfCfa() = default;
+
+  bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
+                       dwarf_loc_regs_t* loc_regs);
+
+  bool Log(uint32_t indent, uint64_t pc, uint64_t load_bias, uint64_t start_offset,
+           uint64_t end_offset);
+
+  DwarfError last_error() { return last_error_; }
+
+  AddressType cur_pc() { return cur_pc_; }
+
+  void set_cie_loc_regs(const dwarf_loc_regs_t* cie_loc_regs) { cie_loc_regs_ = cie_loc_regs; }
+
+ protected:
+  std::string GetOperandString(uint8_t operand, uint64_t value, uint64_t* cur_pc);
+
+  bool LogOffsetRegisterString(uint32_t indent, uint64_t cfa_offset, uint8_t reg);
+
+  bool LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op, uint64_t* cur_pc);
+
+ private:
+  DwarfError last_error_;
+  DwarfMemory* memory_;
+  const DwarfFde* fde_;
+
+  AddressType cur_pc_;
+  const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
+  std::vector<AddressType> operands_;
+  std::stack<dwarf_loc_regs_t> loc_reg_state_;
+
+  // CFA processing functions.
+  bool cfa_nop(dwarf_loc_regs_t*);
+  bool cfa_set_loc(dwarf_loc_regs_t*);
+  bool cfa_advance_loc(dwarf_loc_regs_t*);
+  bool cfa_offset(dwarf_loc_regs_t*);
+  bool cfa_restore(dwarf_loc_regs_t*);
+  bool cfa_undefined(dwarf_loc_regs_t*);
+  bool cfa_same_value(dwarf_loc_regs_t*);
+  bool cfa_register(dwarf_loc_regs_t*);
+  bool cfa_remember_state(dwarf_loc_regs_t*);
+  bool cfa_restore_state(dwarf_loc_regs_t*);
+  bool cfa_def_cfa(dwarf_loc_regs_t*);
+  bool cfa_def_cfa_register(dwarf_loc_regs_t*);
+  bool cfa_def_cfa_offset(dwarf_loc_regs_t*);
+  bool cfa_def_cfa_expression(dwarf_loc_regs_t*);
+  bool cfa_expression(dwarf_loc_regs_t*);
+  bool cfa_offset_extended_sf(dwarf_loc_regs_t*);
+  bool cfa_def_cfa_sf(dwarf_loc_regs_t*);
+  bool cfa_def_cfa_offset_sf(dwarf_loc_regs_t*);
+  bool cfa_val_offset(dwarf_loc_regs_t*);
+  bool cfa_val_offset_sf(dwarf_loc_regs_t*);
+  bool cfa_val_expression(dwarf_loc_regs_t*);
+  bool cfa_gnu_negative_offset_extended(dwarf_loc_regs_t*);
+
+  using process_func = bool (DwarfCfa::*)(dwarf_loc_regs_t*);
+  constexpr static process_func kCallbackTable[64] = {
+      // 0x00 DW_CFA_nop
+      &DwarfCfa::cfa_nop,
+      // 0x01 DW_CFA_set_loc
+      &DwarfCfa::cfa_set_loc,
+      // 0x02 DW_CFA_advance_loc1
+      &DwarfCfa::cfa_advance_loc,
+      // 0x03 DW_CFA_advance_loc2
+      &DwarfCfa::cfa_advance_loc,
+      // 0x04 DW_CFA_advance_loc4
+      &DwarfCfa::cfa_advance_loc,
+      // 0x05 DW_CFA_offset_extended
+      &DwarfCfa::cfa_offset,
+      // 0x06 DW_CFA_restore_extended
+      &DwarfCfa::cfa_restore,
+      // 0x07 DW_CFA_undefined
+      &DwarfCfa::cfa_undefined,
+      // 0x08 DW_CFA_same_value
+      &DwarfCfa::cfa_same_value,
+      // 0x09 DW_CFA_register
+      &DwarfCfa::cfa_register,
+      // 0x0a DW_CFA_remember_state
+      &DwarfCfa::cfa_remember_state,
+      // 0x0b DW_CFA_restore_state
+      &DwarfCfa::cfa_restore_state,
+      // 0x0c DW_CFA_def_cfa
+      &DwarfCfa::cfa_def_cfa,
+      // 0x0d DW_CFA_def_cfa_register
+      &DwarfCfa::cfa_def_cfa_register,
+      // 0x0e DW_CFA_def_cfa_offset
+      &DwarfCfa::cfa_def_cfa_offset,
+      // 0x0f DW_CFA_def_cfa_expression
+      &DwarfCfa::cfa_def_cfa_expression,
+      // 0x10 DW_CFA_expression
+      &DwarfCfa::cfa_expression,
+      // 0x11 DW_CFA_offset_extended_sf
+      &DwarfCfa::cfa_offset_extended_sf,
+      // 0x12 DW_CFA_def_cfa_sf
+      &DwarfCfa::cfa_def_cfa_sf,
+      // 0x13 DW_CFA_def_cfa_offset_sf
+      &DwarfCfa::cfa_def_cfa_offset_sf,
+      // 0x14 DW_CFA_val_offset
+      &DwarfCfa::cfa_val_offset,
+      // 0x15 DW_CFA_val_offset_sf
+      &DwarfCfa::cfa_val_offset_sf,
+      // 0x16 DW_CFA_val_expression
+      &DwarfCfa::cfa_val_expression,
+      // 0x17 illegal cfa
+      nullptr,
+      // 0x18 illegal cfa
+      nullptr,
+      // 0x19 illegal cfa
+      nullptr,
+      // 0x1a illegal cfa
+      nullptr,
+      // 0x1b illegal cfa
+      nullptr,
+      // 0x1c DW_CFA_lo_user (Treat this as illegal)
+      nullptr,
+      // 0x1d illegal cfa
+      nullptr,
+      // 0x1e illegal cfa
+      nullptr,
+      // 0x1f illegal cfa
+      nullptr,
+      // 0x20 illegal cfa
+      nullptr,
+      // 0x21 illegal cfa
+      nullptr,
+      // 0x22 illegal cfa
+      nullptr,
+      // 0x23 illegal cfa
+      nullptr,
+      // 0x24 illegal cfa
+      nullptr,
+      // 0x25 illegal cfa
+      nullptr,
+      // 0x26 illegal cfa
+      nullptr,
+      // 0x27 illegal cfa
+      nullptr,
+      // 0x28 illegal cfa
+      nullptr,
+      // 0x29 illegal cfa
+      nullptr,
+      // 0x2a illegal cfa
+      nullptr,
+      // 0x2b illegal cfa
+      nullptr,
+      // 0x2c illegal cfa
+      nullptr,
+      // 0x2d DW_CFA_GNU_window_save (Treat this as illegal)
+      nullptr,
+      // 0x2e DW_CFA_GNU_args_size
+      &DwarfCfa::cfa_nop,
+      // 0x2f DW_CFA_GNU_negative_offset_extended
+      &DwarfCfa::cfa_gnu_negative_offset_extended,
+      // 0x30 illegal cfa
+      nullptr,
+      // 0x31 illegal cfa
+      nullptr,
+      // 0x32 illegal cfa
+      nullptr,
+      // 0x33 illegal cfa
+      nullptr,
+      // 0x34 illegal cfa
+      nullptr,
+      // 0x35 illegal cfa
+      nullptr,
+      // 0x36 illegal cfa
+      nullptr,
+      // 0x37 illegal cfa
+      nullptr,
+      // 0x38 illegal cfa
+      nullptr,
+      // 0x39 illegal cfa
+      nullptr,
+      // 0x3a illegal cfa
+      nullptr,
+      // 0x3b illegal cfa
+      nullptr,
+      // 0x3c illegal cfa
+      nullptr,
+      // 0x3d illegal cfa
+      nullptr,
+      // 0x3e illegal cfa
+      nullptr,
+      // 0x3f DW_CFA_hi_user (Treat this as illegal)
+      nullptr,
+  };
+};
+
+#endif  // _LIBUNWINDSTACK_DWARF_CFA_H
diff --git a/libunwindstack/DwarfEncoding.h b/libunwindstack/DwarfEncoding.h
new file mode 100644
index 0000000..0ff3b8c
--- /dev/null
+++ b/libunwindstack/DwarfEncoding.h
@@ -0,0 +1,47 @@
+/*
+ * 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_ENCODING_H
+#define _LIBUNWINDSTACK_DWARF_ENCODING_H
+
+#include <stdint.h>
+
+enum DwarfEncoding : uint8_t {
+  DW_EH_PE_omit = 0xff,
+
+  DW_EH_PE_absptr = 0x00,
+  DW_EH_PE_uleb128 = 0x01,
+  DW_EH_PE_udata2 = 0x02,
+  DW_EH_PE_udata4 = 0x03,
+  DW_EH_PE_udata8 = 0x04,
+  DW_EH_PE_sleb128 = 0x09,
+  DW_EH_PE_sdata2 = 0x0a,
+  DW_EH_PE_sdata4 = 0x0b,
+  DW_EH_PE_sdata8 = 0x0c,
+
+  DW_EH_PE_pcrel = 0x10,
+  DW_EH_PE_textrel = 0x20,
+  DW_EH_PE_datarel = 0x30,
+  DW_EH_PE_funcrel = 0x40,
+  DW_EH_PE_aligned = 0x50,
+
+  // The following are special values used to encode CFA and OP operands.
+  DW_EH_PE_udata1 = 0x0d,
+  DW_EH_PE_sdata1 = 0x0e,
+  DW_EH_PE_block = 0x0f,
+};
+
+#endif  // _LIBUNWINDSTACK_DWARF_ENCODING_H
diff --git a/libunwindstack/DwarfError.h b/libunwindstack/DwarfError.h
new file mode 100644
index 0000000..c00f17d
--- /dev/null
+++ b/libunwindstack/DwarfError.h
@@ -0,0 +1,34 @@
+/*
+ * 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_ERROR_H
+#define _LIBUNWINDSTACK_DWARF_ERROR_H
+
+#include <stdint.h>
+
+enum DwarfError : uint8_t {
+  DWARF_ERROR_NONE,
+  DWARF_ERROR_MEMORY_INVALID,
+  DWARF_ERROR_ILLEGAL_VALUE,
+  DWARF_ERROR_ILLEGAL_STATE,
+  DWARF_ERROR_STACK_INDEX_NOT_VALID,
+  DWARF_ERROR_NOT_IMPLEMENTED,
+  DWARF_ERROR_TOO_MANY_ITERATIONS,
+  DWARF_ERROR_CFA_NOT_DEFINED,
+  DWARF_ERROR_UNSUPPORTED_VERSION,
+};
+
+#endif  // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/DwarfLocation.h b/libunwindstack/DwarfLocation.h
new file mode 100644
index 0000000..062d125
--- /dev/null
+++ b/libunwindstack/DwarfLocation.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_LOCATION_H
+#define _LIBUNWINDSTACK_DWARF_LOCATION_H
+
+#include <stdint.h>
+
+#include <unordered_map>
+
+enum DwarfLocationEnum : uint8_t {
+  DWARF_LOCATION_INVALID = 0,
+  DWARF_LOCATION_UNDEFINED,
+  DWARF_LOCATION_OFFSET,
+  DWARF_LOCATION_VAL_OFFSET,
+  DWARF_LOCATION_REGISTER,
+  DWARF_LOCATION_EXPRESSION,
+  DWARF_LOCATION_VAL_EXPRESSION,
+};
+
+struct DwarfLocation {
+  DwarfLocationEnum type;
+  uint64_t values[2];
+};
+
+typedef std::unordered_map<uint16_t, DwarfLocation> dwarf_loc_regs_t;
+
+#endif  // _LIBUNWINDSTACK_DWARF_LOCATION_H
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
new file mode 100644
index 0000000..11806ea
--- /dev/null
+++ b/libunwindstack/DwarfMemory.cpp
@@ -0,0 +1,248 @@
+/*
+ * 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 <assert.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "DwarfEncoding.h"
+#include "DwarfMemory.h"
+#include "Memory.h"
+
+bool DwarfMemory::ReadBytes(void* dst, size_t num_bytes) {
+  if (!memory_->Read(cur_offset_, dst, num_bytes)) {
+    return false;
+  }
+  cur_offset_ += num_bytes;
+  return true;
+}
+
+template <typename SignedType>
+bool DwarfMemory::ReadSigned(uint64_t* value) {
+  SignedType signed_value;
+  if (!ReadBytes(&signed_value, sizeof(SignedType))) {
+    return false;
+  }
+  *value = static_cast<int64_t>(signed_value);
+  return true;
+}
+
+bool DwarfMemory::ReadULEB128(uint64_t* value) {
+  uint64_t cur_value = 0;
+  uint64_t shift = 0;
+  uint8_t byte;
+  do {
+    if (!ReadBytes(&byte, 1)) {
+      return false;
+    }
+    cur_value += static_cast<uint64_t>(byte & 0x7f) << shift;
+    shift += 7;
+  } while (byte & 0x80);
+  *value = cur_value;
+  return true;
+}
+
+bool DwarfMemory::ReadSLEB128(int64_t* value) {
+  uint64_t cur_value = 0;
+  uint64_t shift = 0;
+  uint8_t byte;
+  do {
+    if (!ReadBytes(&byte, 1)) {
+      return false;
+    }
+    cur_value += static_cast<uint64_t>(byte & 0x7f) << shift;
+    shift += 7;
+  } while (byte & 0x80);
+  if (byte & 0x40) {
+    // Negative value, need to sign extend.
+    cur_value |= static_cast<uint64_t>(-1) << shift;
+  }
+  *value = static_cast<int64_t>(cur_value);
+  return true;
+}
+
+template <typename AddressType>
+size_t DwarfMemory::GetEncodedSize(uint8_t encoding) {
+  switch (encoding & 0x0f) {
+    case DW_EH_PE_absptr:
+      return sizeof(AddressType);
+    case DW_EH_PE_udata1:
+    case DW_EH_PE_sdata1:
+      return 1;
+    case DW_EH_PE_udata2:
+    case DW_EH_PE_sdata2:
+      return 2;
+    case DW_EH_PE_udata4:
+    case DW_EH_PE_sdata4:
+      return 4;
+    case DW_EH_PE_udata8:
+    case DW_EH_PE_sdata8:
+      return 8;
+    case DW_EH_PE_uleb128:
+    case DW_EH_PE_sleb128:
+    default:
+      return 0;
+  }
+}
+
+bool DwarfMemory::AdjustEncodedValue(uint8_t encoding, uint64_t* value) {
+  assert((encoding & 0x0f) == 0);
+  assert(encoding != DW_EH_PE_aligned);
+
+  // Handle the encoding.
+  switch (encoding) {
+    case DW_EH_PE_absptr:
+      // Nothing to do.
+      break;
+    case DW_EH_PE_pcrel:
+      if (pc_offset_ == static_cast<uint64_t>(-1)) {
+        // Unsupported encoding.
+        return false;
+      }
+      *value += pc_offset_;
+      break;
+    case DW_EH_PE_textrel:
+      if (text_offset_ == static_cast<uint64_t>(-1)) {
+        // Unsupported encoding.
+        return false;
+      }
+      *value += text_offset_;
+      break;
+    case DW_EH_PE_datarel:
+      if (data_offset_ == static_cast<uint64_t>(-1)) {
+        // Unsupported encoding.
+        return false;
+      }
+      *value += data_offset_;
+      break;
+    case DW_EH_PE_funcrel:
+      if (func_offset_ == static_cast<uint64_t>(-1)) {
+        // Unsupported encoding.
+        return false;
+      }
+      *value += func_offset_;
+      break;
+    default:
+      return false;
+  }
+
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfMemory::ReadEncodedValue(uint8_t encoding, uint64_t* value) {
+  if (encoding == DW_EH_PE_omit) {
+    *value = 0;
+    return true;
+  } else if (encoding == DW_EH_PE_aligned) {
+    if (__builtin_add_overflow(cur_offset_, sizeof(AddressType) - 1, &cur_offset_)) {
+      return false;
+    }
+    cur_offset_ &= -sizeof(AddressType);
+
+    if (sizeof(AddressType) != sizeof(uint64_t)) {
+      *value = 0;
+    }
+    return ReadBytes(value, sizeof(AddressType));
+  }
+
+  // Get the data.
+  switch (encoding & 0x0f) {
+    case DW_EH_PE_absptr:
+      if (sizeof(AddressType) != sizeof(uint64_t)) {
+        *value = 0;
+      }
+      if (!ReadBytes(value, sizeof(AddressType))) {
+        return false;
+      }
+      break;
+    case DW_EH_PE_uleb128:
+      if (!ReadULEB128(value)) {
+        return false;
+      }
+      break;
+    case DW_EH_PE_sleb128:
+      int64_t signed_value;
+      if (!ReadSLEB128(&signed_value)) {
+        return false;
+      }
+      *value = static_cast<uint64_t>(signed_value);
+      break;
+    case DW_EH_PE_udata1: {
+      uint8_t value8;
+      if (!ReadBytes(&value8, 1)) {
+        return false;
+      }
+      *value = value8;
+    } break;
+    case DW_EH_PE_sdata1:
+      if (!ReadSigned<int8_t>(value)) {
+        return false;
+      }
+      break;
+    case DW_EH_PE_udata2: {
+      uint16_t value16;
+      if (!ReadBytes(&value16, 2)) {
+        return false;
+      }
+      *value = value16;
+    } break;
+    case DW_EH_PE_sdata2:
+      if (!ReadSigned<int16_t>(value)) {
+        return false;
+      }
+      break;
+    case DW_EH_PE_udata4: {
+      uint32_t value32;
+      if (!ReadBytes(&value32, 4)) {
+        return false;
+      }
+      *value = value32;
+    } break;
+    case DW_EH_PE_sdata4:
+      if (!ReadSigned<int32_t>(value)) {
+        return false;
+      }
+      break;
+    case DW_EH_PE_udata8:
+      if (!ReadBytes(value, sizeof(uint64_t))) {
+        return false;
+      }
+      break;
+    case DW_EH_PE_sdata8:
+      if (!ReadSigned<int64_t>(value)) {
+        return false;
+      }
+      break;
+    default:
+      return false;
+  }
+
+  return AdjustEncodedValue(encoding & 0xf0, value);
+}
+
+// Instantiate all of the needed template functions.
+template bool DwarfMemory::ReadSigned<int8_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int16_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int32_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int64_t>(uint64_t*);
+
+template size_t DwarfMemory::GetEncodedSize<uint32_t>(uint8_t);
+template size_t DwarfMemory::GetEncodedSize<uint64_t>(uint8_t);
+
+template bool DwarfMemory::ReadEncodedValue<uint32_t>(uint8_t, uint64_t*);
+template bool DwarfMemory::ReadEncodedValue<uint64_t>(uint8_t, uint64_t*);
diff --git a/libunwindstack/DwarfMemory.h b/libunwindstack/DwarfMemory.h
new file mode 100644
index 0000000..a304dd9
--- /dev/null
+++ b/libunwindstack/DwarfMemory.h
@@ -0,0 +1,72 @@
+/*
+ * 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_MEMORY_H
+#define _LIBUNWINDSTACK_DWARF_MEMORY_H
+
+#include <stdint.h>
+
+// Forward declarations.
+class Memory;
+
+class DwarfMemory {
+ public:
+  DwarfMemory(Memory* memory) : memory_(memory) {}
+  virtual ~DwarfMemory() = default;
+
+  bool ReadBytes(void* dst, size_t num_bytes);
+
+  template <typename SignedType>
+  bool ReadSigned(uint64_t* value);
+
+  bool ReadULEB128(uint64_t* value);
+
+  bool ReadSLEB128(int64_t* value);
+
+  template <typename AddressType>
+  size_t GetEncodedSize(uint8_t encoding);
+
+  bool AdjustEncodedValue(uint8_t encoding, uint64_t* value);
+
+  template <typename AddressType>
+  bool ReadEncodedValue(uint8_t encoding, uint64_t* value);
+
+  uint64_t cur_offset() { return cur_offset_; }
+  void set_cur_offset(uint64_t cur_offset) { cur_offset_ = cur_offset; }
+
+  void set_pc_offset(uint64_t offset) { pc_offset_ = offset; }
+  void clear_pc_offset() { pc_offset_ = static_cast<uint64_t>(-1); }
+
+  void set_data_offset(uint64_t offset) { data_offset_ = offset; }
+  void clear_data_offset() { data_offset_ = static_cast<uint64_t>(-1); }
+
+  void set_func_offset(uint64_t offset) { func_offset_ = offset; }
+  void clear_func_offset() { func_offset_ = static_cast<uint64_t>(-1); }
+
+  void set_text_offset(uint64_t offset) { text_offset_ = offset; }
+  void clear_text_offset() { text_offset_ = static_cast<uint64_t>(-1); }
+
+ private:
+  Memory* memory_;
+  uint64_t cur_offset_ = 0;
+
+  uint64_t pc_offset_ = static_cast<uint64_t>(-1);
+  uint64_t data_offset_ = static_cast<uint64_t>(-1);
+  uint64_t func_offset_ = static_cast<uint64_t>(-1);
+  uint64_t text_offset_ = static_cast<uint64_t>(-1);
+};
+
+#endif  // _LIBUNWINDSTACK_DWARF_MEMORY_H
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
new file mode 100644
index 0000000..507ca08
--- /dev/null
+++ b/libunwindstack/DwarfOp.cpp
@@ -0,0 +1,462 @@
+/*
+ * 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 <deque>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+
+#include "DwarfError.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "Log.h"
+#include "Memory.h"
+#include "Regs.h"
+
+template <typename AddressType>
+constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256];
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end, uint8_t dwarf_version) {
+  uint32_t iterations = 0;
+  is_register_ = false;
+  stack_.clear();
+  memory_->set_cur_offset(start);
+  while (memory_->cur_offset() < end) {
+    if (!Decode(dwarf_version)) {
+      return false;
+    }
+    // To protect against a branch that creates an infinite loop,
+    // terminate if the number of iterations gets too high.
+    if (iterations++ == 1000) {
+      last_error_ = DWARF_ERROR_TOO_MANY_ITERATIONS;
+      return false;
+    }
+  }
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::Decode(uint8_t dwarf_version) {
+  last_error_ = DWARF_ERROR_NONE;
+  if (!memory_->ReadBytes(&cur_op_, 1)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  const auto* op = &kCallbackTable[cur_op_];
+  const auto handle_func = op->handle_func;
+  if (handle_func == nullptr) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+
+  // Check for an unsupported opcode.
+  if (dwarf_version < op->supported_version) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+
+  // Make sure that the required number of stack elements is available.
+  if (stack_.size() < op->num_required_stack_values) {
+    last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+    return false;
+  }
+
+  operands_.clear();
+  for (size_t i = 0; i < op->num_operands; i++) {
+    uint64_t value;
+    if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    operands_.push_back(value);
+  }
+  return (this->*handle_func)();
+}
+
+template <typename AddressType>
+void DwarfOp<AddressType>::GetLogInfo(uint64_t start, uint64_t end,
+                                      std::vector<std::string>* lines) {
+  memory_->set_cur_offset(start);
+  while (memory_->cur_offset() < end) {
+    uint8_t cur_op;
+    if (!memory_->ReadBytes(&cur_op, 1)) {
+      return;
+    }
+
+    std::string raw_string(android::base::StringPrintf("Raw Data: 0x%02x", cur_op));
+    std::string log_string;
+    const auto* op = &kCallbackTable[cur_op];
+    if (op->handle_func == nullptr) {
+      log_string = "Illegal";
+    } else {
+      log_string = op->name;
+      uint64_t start_offset = memory_->cur_offset();
+      for (size_t i = 0; i < op->num_operands; i++) {
+        uint64_t value;
+        if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) {
+          return;
+        }
+        log_string += ' ' + std::to_string(value);
+      }
+      uint64_t end_offset = memory_->cur_offset();
+
+      memory_->set_cur_offset(start_offset);
+      for (size_t i = start_offset; i < end_offset; i++) {
+        uint8_t byte;
+        if (!memory_->ReadBytes(&byte, 1)) {
+          return;
+        }
+        raw_string += android::base::StringPrintf(" 0x%02x", byte);
+      }
+      memory_->set_cur_offset(end_offset);
+    }
+    lines->push_back(std::move(log_string));
+    lines->push_back(std::move(raw_string));
+  }
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_deref() {
+  // Read the address and dereference it.
+  AddressType addr = StackPop();
+  AddressType value;
+  if (!regular_memory()->Read(addr, &value, sizeof(value))) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  stack_.push_front(value);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_deref_size() {
+  AddressType bytes_to_read = OperandAt(0);
+  if (bytes_to_read > sizeof(AddressType) || bytes_to_read == 0) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+  // Read the address and dereference it.
+  AddressType addr = StackPop();
+  AddressType value = 0;
+  if (!regular_memory()->Read(addr, &value, bytes_to_read)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  stack_.push_front(value);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_push() {
+  // Push all of the operands.
+  for (auto operand : operands_) {
+    stack_.push_front(operand);
+  }
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_dup() {
+  stack_.push_front(StackAt(0));
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_drop() {
+  StackPop();
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_over() {
+  stack_.push_front(StackAt(1));
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_pick() {
+  AddressType index = OperandAt(0);
+  if (index > StackSize()) {
+    last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+    return false;
+  }
+  stack_.push_front(StackAt(index));
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_swap() {
+  AddressType old_value = stack_[0];
+  stack_[0] = stack_[1];
+  stack_[1] = old_value;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_rot() {
+  AddressType top = stack_[0];
+  stack_[0] = stack_[1];
+  stack_[1] = stack_[2];
+  stack_[2] = top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_abs() {
+  SignedType signed_value = static_cast<SignedType>(stack_[0]);
+  if (signed_value < 0) {
+    signed_value = -signed_value;
+  }
+  stack_[0] = static_cast<AddressType>(signed_value);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_and() {
+  AddressType top = StackPop();
+  stack_[0] &= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_div() {
+  AddressType top = StackPop();
+  if (top == 0) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+  SignedType signed_divisor = static_cast<SignedType>(top);
+  SignedType signed_dividend = static_cast<SignedType>(stack_[0]);
+  stack_[0] = static_cast<AddressType>(signed_dividend / signed_divisor);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_minus() {
+  AddressType top = StackPop();
+  stack_[0] -= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_mod() {
+  AddressType top = StackPop();
+  if (top == 0) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+  stack_[0] %= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_mul() {
+  AddressType top = StackPop();
+  stack_[0] *= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_neg() {
+  SignedType signed_value = static_cast<SignedType>(stack_[0]);
+  stack_[0] = static_cast<AddressType>(-signed_value);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_not() {
+  stack_[0] = ~stack_[0];
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_or() {
+  AddressType top = StackPop();
+  stack_[0] |= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_plus() {
+  AddressType top = StackPop();
+  stack_[0] += top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_plus_uconst() {
+  stack_[0] += OperandAt(0);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shl() {
+  AddressType top = StackPop();
+  stack_[0] <<= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shr() {
+  AddressType top = StackPop();
+  stack_[0] >>= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shra() {
+  AddressType top = StackPop();
+  SignedType signed_value = static_cast<SignedType>(stack_[0]) >> top;
+  stack_[0] = static_cast<AddressType>(signed_value);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_xor() {
+  AddressType top = StackPop();
+  stack_[0] ^= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_bra() {
+  // Requires one stack element.
+  AddressType top = StackPop();
+  int16_t offset = static_cast<int16_t>(OperandAt(0));
+  uint64_t cur_offset;
+  if (top != 0) {
+    cur_offset = memory_->cur_offset() + offset;
+  } else {
+    cur_offset = memory_->cur_offset() - offset;
+  }
+  memory_->set_cur_offset(cur_offset);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_eq() {
+  AddressType top = StackPop();
+  stack_[0] = bool_to_dwarf_bool(stack_[0] == top);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_ge() {
+  AddressType top = StackPop();
+  stack_[0] = bool_to_dwarf_bool(stack_[0] >= top);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_gt() {
+  AddressType top = StackPop();
+  stack_[0] = bool_to_dwarf_bool(stack_[0] > top);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_le() {
+  AddressType top = StackPop();
+  stack_[0] = bool_to_dwarf_bool(stack_[0] <= top);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_lt() {
+  AddressType top = StackPop();
+  stack_[0] = bool_to_dwarf_bool(stack_[0] < top);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_ne() {
+  AddressType top = StackPop();
+  stack_[0] = bool_to_dwarf_bool(stack_[0] != top);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_skip() {
+  int16_t offset = static_cast<int16_t>(OperandAt(0));
+  uint64_t cur_offset = memory_->cur_offset() + offset;
+  memory_->set_cur_offset(cur_offset);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_lit() {
+  stack_.push_front(cur_op() - 0x30);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_reg() {
+  is_register_ = true;
+  stack_.push_front(cur_op() - 0x50);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_regx() {
+  is_register_ = true;
+  stack_.push_front(OperandAt(0));
+  return true;
+}
+
+// It's not clear for breg/bregx, if this op should read the current
+// value of the register, or where we think that register is located.
+// For simplicity, the code will read the value before doing the unwind.
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_breg() {
+  uint16_t reg = cur_op() - 0x70;
+  if (reg >= regs_->total_regs()) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+  stack_.push_front((*regs_)[reg] + OperandAt(0));
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_bregx() {
+  AddressType reg = OperandAt(0);
+  if (reg >= regs_->total_regs()) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+  stack_.push_front((*regs_)[reg] + OperandAt(1));
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_nop() {
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_not_implemented() {
+  last_error_ = DWARF_ERROR_NOT_IMPLEMENTED;
+  return false;
+}
+
+// Explicitly instantiate DwarfOp.
+template class DwarfOp<uint32_t>;
+template class DwarfOp<uint64_t>;
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
new file mode 100644
index 0000000..0f4b36d
--- /dev/null
+++ b/libunwindstack/DwarfOp.h
@@ -0,0 +1,1636 @@
+/*
+ * 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_OP_H
+#define _LIBUNWINDSTACK_DWARF_OP_H
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "DwarfEncoding.h"
+
+enum DwarfVersion : uint8_t {
+  DWARF_VERSION_2 = 2,
+  DWARF_VERSION_3 = 3,
+  DWARF_VERSION_4 = 4,
+  DWARF_VERSION_MAX = DWARF_VERSION_4,
+};
+
+// Forward declarations.
+class DwarfMemory;
+class Memory;
+template <typename AddressType>
+class RegsImpl;
+
+template <typename AddressType>
+class DwarfOp {
+  // Signed version of AddressType
+  typedef typename std::make_signed<AddressType>::type SignedType;
+
+  struct OpCallback {
+    const char* name;
+    bool (DwarfOp::*handle_func)();
+    uint8_t supported_version;
+    uint8_t num_required_stack_values;
+    uint8_t num_operands;
+    uint8_t operands[2];
+  };
+
+ public:
+  DwarfOp(DwarfMemory* memory, Memory* regular_memory)
+      : memory_(memory), regular_memory_(regular_memory) {}
+  virtual ~DwarfOp() = default;
+
+  bool Decode(uint8_t dwarf_version);
+
+  bool Eval(uint64_t start, uint64_t end, uint8_t dwarf_version);
+
+  void GetLogInfo(uint64_t start, uint64_t end, std::vector<std::string>* lines);
+
+  AddressType StackAt(size_t index) { return stack_[index]; }
+  size_t StackSize() { return stack_.size(); }
+
+  void set_regs(RegsImpl<AddressType>* regs) { regs_ = regs; }
+
+  DwarfError last_error() { return last_error_; }
+
+  bool is_register() { return is_register_; }
+
+  uint8_t cur_op() { return cur_op_; }
+
+  Memory* regular_memory() { return regular_memory_; }
+
+ protected:
+  AddressType OperandAt(size_t index) { return operands_[index]; }
+  size_t OperandsSize() { return operands_.size(); }
+
+  AddressType StackPop() {
+    AddressType value = stack_.front();
+    stack_.pop_front();
+    return value;
+  }
+
+ private:
+  DwarfMemory* memory_;
+  Memory* regular_memory_;
+
+  RegsImpl<AddressType>* regs_;
+  bool is_register_ = false;
+  DwarfError last_error_ = DWARF_ERROR_NONE;
+  uint8_t cur_op_;
+  std::vector<AddressType> operands_;
+  std::deque<AddressType> stack_;
+
+  inline AddressType bool_to_dwarf_bool(bool value) { return value ? 1 : 0; }
+
+  // Op processing functions.
+  bool op_deref();
+  bool op_deref_size();
+  bool op_push();
+  bool op_dup();
+  bool op_drop();
+  bool op_over();
+  bool op_pick();
+  bool op_swap();
+  bool op_rot();
+  bool op_abs();
+  bool op_and();
+  bool op_div();
+  bool op_minus();
+  bool op_mod();
+  bool op_mul();
+  bool op_neg();
+  bool op_not();
+  bool op_or();
+  bool op_plus();
+  bool op_plus_uconst();
+  bool op_shl();
+  bool op_shr();
+  bool op_shra();
+  bool op_xor();
+  bool op_bra();
+  bool op_eq();
+  bool op_ge();
+  bool op_gt();
+  bool op_le();
+  bool op_lt();
+  bool op_ne();
+  bool op_skip();
+  bool op_lit();
+  bool op_reg();
+  bool op_regx();
+  bool op_breg();
+  bool op_bregx();
+  bool op_nop();
+  bool op_not_implemented();
+
+  constexpr static OpCallback kCallbackTable[256] = {
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0x00 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0x01 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0x02 illegal op
+      {
+          // 0x03 DW_OP_addr
+          "DW_OP_addr",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_absptr},
+      },
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0x04 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0x05 illegal op
+      {
+          // 0x06 DW_OP_deref
+          "DW_OP_deref",
+          &DwarfOp::op_deref,
+          DWARF_VERSION_2,
+          1,
+          0,
+          {},
+      },
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0x07 illegal op
+      {
+          // 0x08 DW_OP_const1u
+          "DW_OP_const1u",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_udata1},
+      },
+      {
+          // 0x09 DW_OP_const1s
+          "DW_OP_const1s",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sdata1},
+      },
+      {
+          // 0x0a DW_OP_const2u
+          "DW_OP_const2u",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_udata2},
+      },
+      {
+          // 0x0b DW_OP_const2s
+          "DW_OP_const2s",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sdata2},
+      },
+      {
+          // 0x0c DW_OP_const4u
+          "DW_OP_const4u",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_udata4},
+      },
+      {
+          // 0x0d DW_OP_const4s
+          "DW_OP_const4s",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sdata4},
+      },
+      {
+          // 0x0e DW_OP_const8u
+          "DW_OP_const8u",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_udata8},
+      },
+      {
+          // 0x0f DW_OP_const8s
+          "DW_OP_const8s",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sdata8},
+      },
+      {
+          // 0x10 DW_OP_constu
+          "DW_OP_constu",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_uleb128},
+      },
+      {
+          // 0x11 DW_OP_consts
+          "DW_OP_consts",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x12 DW_OP_dup
+          "DW_OP_dup",
+          &DwarfOp::op_dup,
+          DWARF_VERSION_2,
+          1,
+          0,
+          {},
+      },
+      {
+          // 0x13 DW_OP_drop
+          "DW_OP_drop",
+          &DwarfOp::op_drop,
+          DWARF_VERSION_2,
+          1,
+          0,
+          {},
+      },
+      {
+          // 0x14 DW_OP_over
+          "DW_OP_over",
+          &DwarfOp::op_over,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x15 DW_OP_pick
+          "DW_OP_pick",
+          &DwarfOp::op_pick,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_udata1},
+      },
+      {
+          // 0x16 DW_OP_swap
+          "DW_OP_swap",
+          &DwarfOp::op_swap,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x17 DW_OP_rot
+          "DW_OP_rot",
+          &DwarfOp::op_rot,
+          DWARF_VERSION_2,
+          3,
+          0,
+          {},
+      },
+      {
+          // 0x18 DW_OP_xderef
+          "DW_OP_xderef",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x19 DW_OP_abs
+          "DW_OP_abs",
+          &DwarfOp::op_abs,
+          DWARF_VERSION_2,
+          1,
+          0,
+          {},
+      },
+      {
+          // 0x1a DW_OP_and
+          "DW_OP_and",
+          &DwarfOp::op_and,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x1b DW_OP_div
+          "DW_OP_div",
+          &DwarfOp::op_div,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x1c DW_OP_minus
+          "DW_OP_minus",
+          &DwarfOp::op_minus,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x1d DW_OP_mod
+          "DW_OP_mod",
+          &DwarfOp::op_mod,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x1e DW_OP_mul
+          "DW_OP_mul",
+          &DwarfOp::op_mul,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x1f DW_OP_neg
+          "DW_OP_neg",
+          &DwarfOp::op_neg,
+          DWARF_VERSION_2,
+          1,
+          0,
+          {},
+      },
+      {
+          // 0x20 DW_OP_not
+          "DW_OP_not",
+          &DwarfOp::op_not,
+          DWARF_VERSION_2,
+          1,
+          0,
+          {},
+      },
+      {
+          // 0x21 DW_OP_or
+          "DW_OP_or",
+          &DwarfOp::op_or,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x22 DW_OP_plus
+          "DW_OP_plus",
+          &DwarfOp::op_plus,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x23 DW_OP_plus_uconst
+          "DW_OP_plus_uconst",
+          &DwarfOp::op_plus_uconst,
+          DWARF_VERSION_2,
+          1,
+          1,
+          {DW_EH_PE_uleb128},
+      },
+      {
+          // 0x24 DW_OP_shl
+          "DW_OP_shl",
+          &DwarfOp::op_shl,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x25 DW_OP_shr
+          "DW_OP_shr",
+          &DwarfOp::op_shr,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x26 DW_OP_shra
+          "DW_OP_shra",
+          &DwarfOp::op_shra,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x27 DW_OP_xor
+          "DW_OP_xor",
+          &DwarfOp::op_xor,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x28 DW_OP_bra
+          "DW_OP_bra",
+          &DwarfOp::op_bra,
+          DWARF_VERSION_2,
+          1,
+          1,
+          {DW_EH_PE_sdata2},
+      },
+      {
+          // 0x29 DW_OP_eq
+          "DW_OP_eq",
+          &DwarfOp::op_eq,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x2a DW_OP_ge
+          "DW_OP_ge",
+          &DwarfOp::op_ge,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x2b DW_OP_gt
+          "DW_OP_gt",
+          &DwarfOp::op_gt,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x2c DW_OP_le
+          "DW_OP_le",
+          &DwarfOp::op_le,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x2d DW_OP_lt
+          "DW_OP_lt",
+          &DwarfOp::op_lt,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x2e DW_OP_ne
+          "DW_OP_ne",
+          &DwarfOp::op_ne,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x2f DW_OP_skip
+          "DW_OP_skip",
+          &DwarfOp::op_skip,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sdata2},
+      },
+      {
+          // 0x30 DW_OP_lit0
+          "DW_OP_lit0",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x31 DW_OP_lit1
+          "DW_OP_lit1",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x32 DW_OP_lit2
+          "DW_OP_lit2",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x33 DW_OP_lit3
+          "DW_OP_lit3",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x34 DW_OP_lit4
+          "DW_OP_lit4",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x35 DW_OP_lit5
+          "DW_OP_lit5",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x36 DW_OP_lit6
+          "DW_OP_lit6",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x37 DW_OP_lit7
+          "DW_OP_lit7",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x38 DW_OP_lit8
+          "DW_OP_lit8",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x39 DW_OP_lit9
+          "DW_OP_lit9",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x3a DW_OP_lit10
+          "DW_OP_lit10",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x3b DW_OP_lit11
+          "DW_OP_lit11",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x3c DW_OP_lit12
+          "DW_OP_lit12",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x3d DW_OP_lit13
+          "DW_OP_lit13",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x3e DW_OP_lit14
+          "DW_OP_lit14",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x3f DW_OP_lit15
+          "DW_OP_lit15",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x40 DW_OP_lit16
+          "DW_OP_lit16",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x41 DW_OP_lit17
+          "DW_OP_lit17",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x42 DW_OP_lit18
+          "DW_OP_lit18",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x43 DW_OP_lit19
+          "DW_OP_lit19",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x44 DW_OP_lit20
+          "DW_OP_lit20",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x45 DW_OP_lit21
+          "DW_OP_lit21",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x46 DW_OP_lit22
+          "DW_OP_lit22",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x47 DW_OP_lit23
+          "DW_OP_lit23",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x48 DW_OP_lit24
+          "DW_OP_lit24",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x49 DW_OP_lit25
+          "DW_OP_lit25",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x4a DW_OP_lit26
+          "DW_OP_lit26",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x4b DW_OP_lit27
+          "DW_OP_lit27",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x4c DW_OP_lit28
+          "DW_OP_lit28",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x4d DW_OP_lit29
+          "DW_OP_lit29",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x4e DW_OP_lit30
+          "DW_OP_lit30",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x4f DW_OP_lit31
+          "DW_OP_lit31",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x50 DW_OP_reg0
+          "DW_OP_reg0",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x51 DW_OP_reg1
+          "DW_OP_reg1",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x52 DW_OP_reg2
+          "DW_OP_reg2",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x53 DW_OP_reg3
+          "DW_OP_reg3",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x54 DW_OP_reg4
+          "DW_OP_reg4",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x55 DW_OP_reg5
+          "DW_OP_reg5",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x56 DW_OP_reg6
+          "DW_OP_reg6",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x57 DW_OP_reg7
+          "DW_OP_reg7",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x58 DW_OP_reg8
+          "DW_OP_reg8",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x59 DW_OP_reg9
+          "DW_OP_reg9",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x5a DW_OP_reg10
+          "DW_OP_reg10",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x5b DW_OP_reg11
+          "DW_OP_reg11",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x5c DW_OP_reg12
+          "DW_OP_reg12",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x5d DW_OP_reg13
+          "DW_OP_reg13",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x5e DW_OP_reg14
+          "DW_OP_reg14",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x5f DW_OP_reg15
+          "DW_OP_reg15",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x60 DW_OP_reg16
+          "DW_OP_reg16",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x61 DW_OP_reg17
+          "DW_OP_reg17",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x62 DW_OP_reg18
+          "DW_OP_reg18",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x63 DW_OP_reg19
+          "DW_OP_reg19",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x64 DW_OP_reg20
+          "DW_OP_reg20",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x65 DW_OP_reg21
+          "DW_OP_reg21",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x66 DW_OP_reg22
+          "DW_OP_reg22",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x67 DW_OP_reg23
+          "DW_OP_reg23",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x68 DW_OP_reg24
+          "DW_OP_reg24",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x69 DW_OP_reg25
+          "DW_OP_reg25",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x6a DW_OP_reg26
+          "DW_OP_reg26",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x6b DW_OP_reg27
+          "DW_OP_reg27",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x6c DW_OP_reg28
+          "DW_OP_reg28",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x6d DW_OP_reg29
+          "DW_OP_reg29",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x6e DW_OP_reg30
+          "DW_OP_reg30",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x6f DW_OP_reg31
+          "DW_OP_reg31",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x70 DW_OP_breg0
+          "DW_OP_breg0",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x71 DW_OP_breg1
+          "DW_OP_breg1",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x72 DW_OP_breg2
+          "DW_OP_breg2",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x73 DW_OP_breg3
+          "DW_OP_breg3",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x74 DW_OP_breg4
+          "DW_OP_breg4",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x75 DW_OP_breg5
+          "DW_OP_breg5",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x76 DW_OP_breg6
+          "DW_OP_breg6",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x77 DW_OP_breg7
+          "DW_OP_breg7",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x78 DW_OP_breg8
+          "DW_OP_breg8",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x79 DW_OP_breg9
+          "DW_OP_breg9",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x7a DW_OP_breg10
+          "DW_OP_breg10",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x7b DW_OP_breg11
+          "DW_OP_breg11",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x7c DW_OP_breg12
+          "DW_OP_breg12",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x7d DW_OP_breg13
+          "DW_OP_breg13",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x7e DW_OP_breg14
+          "DW_OP_breg14",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x7f DW_OP_breg15
+          "DW_OP_breg15",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x80 DW_OP_breg16
+          "DW_OP_breg16",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x81 DW_OP_breg17
+          "DW_OP_breg17",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x82 DW_OP_breg18
+          "DW_OP_breg18",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x83 DW_OP_breg19
+          "DW_OP_breg19",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x84 DW_OP_breg20
+          "DW_OP_breg20",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x85 DW_OP_breg21
+          "DW_OP_breg21",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x86 DW_OP_breg22
+          "DW_OP_breg22",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x87 DW_OP_breg23
+          "DW_OP_breg23",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x88 DW_OP_breg24
+          "DW_OP_breg24",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x89 DW_OP_breg25
+          "DW_OP_breg25",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x8a DW_OP_breg26
+          "DW_OP_breg26",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x8b DW_OP_breg27
+          "DW_OP_breg27",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x8c DW_OP_breg28
+          "DW_OP_breg28",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x8d DW_OP_breg29
+          "DW_OP_breg29",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x8e DW_OP_breg30
+          "DW_OP_breg30",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x8f DW_OP_breg31
+          "DW_OP_breg31",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x90 DW_OP_regx
+          "DW_OP_regx",
+          &DwarfOp::op_regx,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_uleb128},
+      },
+      {
+          // 0x91 DW_OP_fbreg
+          "DW_OP_fbreg",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x92 DW_OP_bregx
+          "DW_OP_bregx",
+          &DwarfOp::op_bregx,
+          DWARF_VERSION_2,
+          0,
+          2,
+          {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+      },
+      {
+          // 0x93 DW_OP_piece
+          "DW_OP_piece",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_uleb128},
+      },
+      {
+          // 0x94 DW_OP_deref_size
+          "DW_OP_deref_size",
+          &DwarfOp::op_deref_size,
+          DWARF_VERSION_2,
+          1,
+          1,
+          {DW_EH_PE_udata1},
+      },
+      {
+          // 0x95 DW_OP_xderef_size
+          "DW_OP_xderef_size",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_udata1},
+      },
+      {
+          // 0x96 DW_OP_nop
+          "DW_OP_nop",
+          &DwarfOp::op_nop,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x97 DW_OP_push_object_address
+          "DW_OP_push_object_address",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_3,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x98 DW_OP_call2
+          "DW_OP_call2",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_3,
+          0,
+          1,
+          {DW_EH_PE_udata2},
+      },
+      {
+          // 0x99 DW_OP_call4
+          "DW_OP_call4",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_3,
+          0,
+          1,
+          {DW_EH_PE_udata4},
+      },
+      {
+          // 0x9a DW_OP_call_ref
+          "DW_OP_call_ref",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_3,
+          0,
+          0,  // Has a different sized operand (4 bytes or 8 bytes).
+          {},
+      },
+      {
+          // 0x9b DW_OP_form_tls_address
+          "DW_OP_form_tls_address",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_3,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x9c DW_OP_call_frame_cfa
+          "DW_OP_call_frame_cfa",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_3,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x9d DW_OP_bit_piece
+          "DW_OP_bit_piece",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_3,
+          0,
+          2,
+          {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+      },
+      {
+          // 0x9e DW_OP_implicit_value
+          "DW_OP_implicit_value",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_4,
+          0,
+          1,
+          {DW_EH_PE_uleb128},
+      },
+      {
+          // 0x9f DW_OP_stack_value
+          "DW_OP_stack_value",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_4,
+          1,
+          0,
+          {},
+      },
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa0 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa1 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa2 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa3 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa4 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa5 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa6 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa7 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa8 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa9 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xaa illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xab illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xac illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xad illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xae illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xaf illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb0 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb1 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb2 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb3 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb4 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb5 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb6 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb7 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb8 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb9 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xba illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbb illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbc illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbd illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbe illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbf illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc0 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc1 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc2 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc3 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc4 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc5 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc6 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc7 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc8 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc9 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xca illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcb illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcc illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcd illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xce illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcf illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd0 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd1 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd2 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd3 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd4 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd5 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd6 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd7 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd8 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd9 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xda illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdb illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdc illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdd illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xde illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdf illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe0 DW_OP_lo_user
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe1 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe2 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe3 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe4 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe5 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe6 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe7 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe8 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe9 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xea illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xeb illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xec illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xed illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xee illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xef illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf0 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf1 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf2 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf3 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf4 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf5 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf6 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf7 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf8 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf9 illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfa illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfb illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfc illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfd illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfe illegal op
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0xff DW_OP_hi_user
+  };
+};
+
+#endif  // _LIBUNWINDSTACK_DWARF_OP_H
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
new file mode 100644
index 0000000..4c98aa3
--- /dev/null
+++ b/libunwindstack/DwarfSection.cpp
@@ -0,0 +1,543 @@
+/*
+ * 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 "DwarfCfa.h"
+#include "DwarfError.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "DwarfSection.h"
+#include "DwarfStructs.h"
+#include "Log.h"
+#include "Memory.h"
+#include "Regs.h"
+
+const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
+  uint64_t fde_offset;
+  if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
+    return nullptr;
+  }
+  const DwarfFde* fde = GetFdeFromOffset(fde_offset);
+  // Guaranteed pc >= pc_start, need to check pc in the fde range.
+  if (pc < fde->pc_end) {
+    return fde;
+  }
+  last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+  return nullptr;
+}
+
+bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
+  const DwarfFde* fde = GetFdeFromPc(pc);
+  if (fde == nullptr || fde->cie == nullptr) {
+    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    return false;
+  }
+
+  // Now get the location information for this pc.
+  dwarf_loc_regs_t loc_regs;
+  if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
+    return false;
+  }
+
+  // Now eval the actual registers.
+  return Eval(fde->cie, process_memory, loc_regs, regs);
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, uint8_t version,
+                                                   Memory* regular_memory, AddressType* value) {
+  DwarfOp<AddressType> op(&memory_, regular_memory);
+
+  // Need to evaluate the op data.
+  uint64_t start = loc.values[1];
+  uint64_t end = start + loc.values[0];
+  if (!op.Eval(start, end, version)) {
+    last_error_ = op.last_error();
+    return false;
+  }
+  if (op.StackSize() == 0) {
+    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    return false;
+  }
+  // We don't support an expression that evaluates to a register number.
+  if (op.is_register()) {
+    last_error_ = DWARF_ERROR_NOT_IMPLEMENTED;
+    return false;
+  }
+  *value = op.StackAt(0);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
+                                         const dwarf_loc_regs_t& loc_regs, Regs* regs) {
+  RegsImpl<AddressType>* cur_regs = reinterpret_cast<RegsImpl<AddressType>*>(regs);
+  if (cie->return_address_register >= cur_regs->total_regs()) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+
+  // Get the cfa value;
+  auto cfa_entry = loc_regs.find(CFA_REG);
+  if (cfa_entry == loc_regs.end()) {
+    last_error_ = DWARF_ERROR_CFA_NOT_DEFINED;
+    return false;
+  }
+
+  AddressType prev_pc = regs->pc();
+  AddressType prev_cfa = regs->sp();
+
+  AddressType cfa;
+  const DwarfLocation* loc = &cfa_entry->second;
+  // Only a few location types are valid for the cfa.
+  switch (loc->type) {
+    case DWARF_LOCATION_REGISTER:
+      if (loc->values[0] >= cur_regs->total_regs()) {
+        last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+        return false;
+      }
+      // If the stack pointer register is the CFA, and the stack
+      // pointer register does not have any associated location
+      // information, use the current cfa value.
+      if (regs->sp_reg() == loc->values[0] && loc_regs.count(regs->sp_reg()) == 0) {
+        cfa = prev_cfa;
+      } else {
+        cfa = (*cur_regs)[loc->values[0]];
+      }
+      cfa += loc->values[1];
+      break;
+    case DWARF_LOCATION_EXPRESSION:
+    case DWARF_LOCATION_VAL_EXPRESSION: {
+      AddressType value;
+      if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
+        return false;
+      }
+      if (loc->type == DWARF_LOCATION_EXPRESSION) {
+        if (!regular_memory->Read(value, &cfa, sizeof(AddressType))) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+      } else {
+        cfa = value;
+      }
+      break;
+    }
+    default:
+      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+  }
+
+  // This code is not guaranteed to work in cases where a register location
+  // is a double indirection to the actual value. For example, if r3 is set
+  // to r5 + 4, and r5 is set to CFA + 4, then this won't necessarily work
+  // because it does not guarantee that r5 is evaluated before r3.
+  // Check that this case does not exist, and error if it does.
+  bool return_address_undefined = false;
+  for (const auto& entry : loc_regs) {
+    uint16_t reg = entry.first;
+    // Already handled the CFA register.
+    if (reg == CFA_REG) continue;
+
+    if (reg >= cur_regs->total_regs()) {
+      // Skip this unknown register.
+      continue;
+    }
+
+    const DwarfLocation* loc = &entry.second;
+    switch (loc->type) {
+      case DWARF_LOCATION_OFFSET:
+        if (!regular_memory->Read(cfa + loc->values[0], &(*cur_regs)[reg], sizeof(AddressType))) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+        break;
+      case DWARF_LOCATION_VAL_OFFSET:
+        (*cur_regs)[reg] = cfa + loc->values[0];
+        break;
+      case DWARF_LOCATION_REGISTER: {
+        uint16_t cur_reg = loc->values[0];
+        if (cur_reg >= cur_regs->total_regs()) {
+          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+          return false;
+        }
+        if (loc_regs.find(cur_reg) != loc_regs.end()) {
+          // This is a double indirection, a register definition references
+          // another register which is also defined as something other
+          // than a register.
+          log(0,
+              "Invalid indirection: register %d references register %d which is "
+              "not a plain register.\n",
+              reg, cur_reg);
+          last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+          return false;
+        }
+        (*cur_regs)[reg] = (*cur_regs)[cur_reg] + loc->values[1];
+        break;
+      }
+      case DWARF_LOCATION_EXPRESSION:
+      case DWARF_LOCATION_VAL_EXPRESSION: {
+        AddressType value;
+        if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
+          return false;
+        }
+        if (loc->type == DWARF_LOCATION_EXPRESSION) {
+          if (!regular_memory->Read(value, &(*cur_regs)[reg], sizeof(AddressType))) {
+            last_error_ = DWARF_ERROR_MEMORY_INVALID;
+            return false;
+          }
+        } else {
+          (*cur_regs)[reg] = value;
+        }
+        break;
+      }
+      case DWARF_LOCATION_UNDEFINED:
+        if (reg == cie->return_address_register) {
+          return_address_undefined = true;
+        }
+      default:
+        break;
+    }
+  }
+
+  // Find the return address location.
+  if (return_address_undefined) {
+    cur_regs->set_pc(0);
+  } else {
+    cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
+  }
+  cur_regs->set_sp(cfa);
+  // Stop if the cfa and pc are the same.
+  return prev_cfa != cfa || prev_pc != cur_regs->pc();
+}
+
+template <typename AddressType>
+const DwarfCie* DwarfSectionImpl<AddressType>::GetCie(uint64_t offset) {
+  auto cie_entry = cie_entries_.find(offset);
+  if (cie_entry != cie_entries_.end()) {
+    return &cie_entry->second;
+  }
+  DwarfCie* cie = &cie_entries_[offset];
+  memory_.set_cur_offset(offset);
+  if (!FillInCie(cie)) {
+    // Erase the cached entry.
+    cie_entries_.erase(offset);
+    return nullptr;
+  }
+  return cie;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
+  uint32_t length32;
+  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  if (length32 == static_cast<uint32_t>(-1)) {
+    // 64 bit Cie
+    uint64_t length64;
+    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+
+    cie->cfa_instructions_end = memory_.cur_offset() + length64;
+    cie->fde_address_encoding = DW_EH_PE_sdata8;
+
+    uint64_t cie_id;
+    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    if (!IsCie64(cie_id)) {
+      // This is not a Cie, something has gone horribly wrong.
+      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+  } else {
+    // 32 bit Cie
+    cie->cfa_instructions_end = memory_.cur_offset() + length32;
+    cie->fde_address_encoding = DW_EH_PE_sdata4;
+
+    uint32_t cie_id;
+    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    if (!IsCie32(cie_id)) {
+      // This is not a Cie, something has gone horribly wrong.
+      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+  }
+
+  if (!memory_.ReadBytes(&cie->version, sizeof(cie->version))) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
+    // Unrecognized version.
+    last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
+    return false;
+  }
+
+  // Read the augmentation string.
+  char aug_value;
+  do {
+    if (!memory_.ReadBytes(&aug_value, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    cie->augmentation_string.push_back(aug_value);
+  } while (aug_value != '\0');
+
+  if (cie->version == 4) {
+    // Skip the Address Size field since we only use it for validation.
+    memory_.set_cur_offset(memory_.cur_offset() + 1);
+
+    // Segment Size
+    if (!memory_.ReadBytes(&cie->segment_size, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+  }
+
+  // Code Alignment Factor
+  if (!memory_.ReadULEB128(&cie->code_alignment_factor)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  // Data Alignment Factor
+  if (!memory_.ReadSLEB128(&cie->data_alignment_factor)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  if (cie->version == 1) {
+    // Return Address is a single byte.
+    uint8_t return_address_register;
+    if (!memory_.ReadBytes(&return_address_register, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    cie->return_address_register = return_address_register;
+  } else if (!memory_.ReadULEB128(&cie->return_address_register)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  if (cie->augmentation_string[0] != 'z') {
+    cie->cfa_instructions_offset = memory_.cur_offset();
+    return true;
+  }
+
+  uint64_t aug_length;
+  if (!memory_.ReadULEB128(&aug_length)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  cie->cfa_instructions_offset = memory_.cur_offset() + aug_length;
+
+  for (size_t i = 1; i < cie->augmentation_string.size(); i++) {
+    switch (cie->augmentation_string[i]) {
+      case 'L':
+        if (!memory_.ReadBytes(&cie->lsda_encoding, 1)) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+        break;
+      case 'P': {
+        uint8_t encoding;
+        if (!memory_.ReadBytes(&encoding, 1)) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+        if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+      } break;
+      case 'R':
+        if (!memory_.ReadBytes(&cie->fde_address_encoding, 1)) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+        break;
+    }
+  }
+  return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromOffset(uint64_t offset) {
+  auto fde_entry = fde_entries_.find(offset);
+  if (fde_entry != fde_entries_.end()) {
+    return &fde_entry->second;
+  }
+  DwarfFde* fde = &fde_entries_[offset];
+  memory_.set_cur_offset(offset);
+  if (!FillInFde(fde)) {
+    fde_entries_.erase(offset);
+    return nullptr;
+  }
+  return fde;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInFde(DwarfFde* fde) {
+  uint32_t length32;
+  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  if (length32 == static_cast<uint32_t>(-1)) {
+    // 64 bit Fde.
+    uint64_t length64;
+    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    fde->cfa_instructions_end = memory_.cur_offset() + length64;
+
+    uint64_t value64;
+    if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    if (IsCie64(value64)) {
+      // This is a Cie, this means something has gone wrong.
+      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+
+    // Get the Cie pointer, which is necessary to properly read the rest of
+    // of the Fde information.
+    fde->cie_offset = GetCieOffsetFromFde64(value64);
+  } else {
+    // 32 bit Fde.
+    fde->cfa_instructions_end = memory_.cur_offset() + length32;
+
+    uint32_t value32;
+    if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    if (IsCie32(value32)) {
+      // This is a Cie, this means something has gone wrong.
+      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+
+    // Get the Cie pointer, which is necessary to properly read the rest of
+    // of the Fde information.
+    fde->cie_offset = GetCieOffsetFromFde32(value32);
+  }
+  uint64_t cur_offset = memory_.cur_offset();
+
+  const DwarfCie* cie = GetCie(fde->cie_offset);
+  if (cie == nullptr) {
+    return false;
+  }
+  fde->cie = cie;
+
+  if (cie->segment_size != 0) {
+    // Skip over the segment selector for now.
+    cur_offset += cie->segment_size;
+  }
+  memory_.set_cur_offset(cur_offset);
+
+  if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_start)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  fde->pc_start = AdjustPcFromFde(fde->pc_start);
+
+  if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_end)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  fde->pc_end += fde->pc_start;
+  if (cie->augmentation_string.size() > 0 && cie->augmentation_string[0] == 'z') {
+    // Augmentation Size
+    uint64_t aug_length;
+    if (!memory_.ReadULEB128(&aug_length)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    uint64_t cur_offset = memory_.cur_offset();
+
+    if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+
+    // Set our position to after all of the augmentation data.
+    memory_.set_cur_offset(cur_offset + aug_length);
+  }
+  fde->cfa_instructions_offset = memory_.cur_offset();
+
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
+                                                       dwarf_loc_regs_t* loc_regs) {
+  DwarfCfa<AddressType> cfa(&memory_, fde);
+
+  // Look for the cached copy of the cie data.
+  auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
+  if (reg_entry == cie_loc_regs_.end()) {
+    if (!cfa.GetLocationInfo(pc, fde->cie->cfa_instructions_offset, fde->cie->cfa_instructions_end,
+                             loc_regs)) {
+      last_error_ = cfa.last_error();
+      return false;
+    }
+    cie_loc_regs_[fde->cie_offset] = *loc_regs;
+  }
+  cfa.set_cie_loc_regs(&cie_loc_regs_[fde->cie_offset]);
+  if (!cfa.GetLocationInfo(pc, fde->cfa_instructions_offset, fde->cfa_instructions_end, loc_regs)) {
+    last_error_ = cfa.last_error();
+    return false;
+  }
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, uint64_t load_bias,
+                                        const DwarfFde* fde) {
+  DwarfCfa<AddressType> cfa(&memory_, fde);
+
+  // Always print the cie information.
+  const DwarfCie* cie = fde->cie;
+  if (!cfa.Log(indent, pc, load_bias, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
+    last_error_ = cfa.last_error();
+    return false;
+  }
+  if (!cfa.Log(indent, pc, load_bias, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
+    last_error_ = cfa.last_error();
+    return false;
+  }
+  return true;
+}
+
+// Explicitly instantiate DwarfSectionImpl
+template class DwarfSectionImpl<uint32_t>;
+template class DwarfSectionImpl<uint64_t>;
diff --git a/libunwindstack/DwarfSection.h b/libunwindstack/DwarfSection.h
new file mode 100644
index 0000000..e617601
--- /dev/null
+++ b/libunwindstack/DwarfSection.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_SECTION_H
+#define _LIBUNWINDSTACK_DWARF_SECTION_H
+
+#include <stdint.h>
+
+#include <iterator>
+#include <unordered_map>
+
+#include "DwarfError.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfStructs.h"
+
+// Forward declarations.
+class Memory;
+class Regs;
+
+class DwarfSection {
+ public:
+  DwarfSection(Memory* memory) : memory_(memory) {}
+  virtual ~DwarfSection() = default;
+
+  class iterator : public std::iterator<std::bidirectional_iterator_tag, DwarfFde*> {
+   public:
+    iterator(DwarfSection* section, size_t index) : section_(section), index_(index) {}
+
+    iterator& operator++() {
+      index_++;
+      return *this;
+    }
+    iterator& operator++(int increment) {
+      index_ += increment;
+      return *this;
+    }
+    iterator& operator--() {
+      index_--;
+      return *this;
+    }
+    iterator& operator--(int decrement) {
+      index_ -= decrement;
+      return *this;
+    }
+
+    bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
+    bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
+
+    const DwarfFde* operator*() { return section_->GetFdeFromIndex(index_); }
+
+   private:
+    DwarfSection* section_ = nullptr;
+    size_t index_ = 0;
+  };
+
+  iterator begin() { return iterator(this, 0); }
+  iterator end() { return iterator(this, fde_count_); }
+
+  DwarfError last_error() { return last_error_; }
+
+  virtual bool Init(uint64_t offset, uint64_t size) = 0;
+
+  virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*) = 0;
+
+  virtual bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
+
+  virtual bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) = 0;
+
+  virtual const DwarfFde* GetFdeFromIndex(size_t index) = 0;
+
+  const DwarfFde* GetFdeFromPc(uint64_t pc);
+
+  virtual const DwarfFde* GetFdeFromOffset(uint64_t fde_offset) = 0;
+
+  virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
+
+  virtual bool IsCie32(uint32_t value32) = 0;
+
+  virtual bool IsCie64(uint64_t value64) = 0;
+
+  virtual uint64_t GetCieOffsetFromFde32(uint32_t pointer) = 0;
+
+  virtual uint64_t GetCieOffsetFromFde64(uint64_t pointer) = 0;
+
+  virtual uint64_t AdjustPcFromFde(uint64_t pc) = 0;
+
+  bool Step(uint64_t pc, Regs* regs, Memory* process_memory);
+
+ protected:
+  DwarfMemory memory_;
+  DwarfError last_error_ = DWARF_ERROR_NONE;
+
+  uint64_t fde_count_;
+  std::unordered_map<uint64_t, DwarfFde> fde_entries_;
+  std::unordered_map<uint64_t, DwarfCie> cie_entries_;
+  std::unordered_map<uint64_t, dwarf_loc_regs_t> cie_loc_regs_;
+};
+
+template <typename AddressType>
+class DwarfSectionImpl : public DwarfSection {
+ public:
+  DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
+  virtual ~DwarfSectionImpl() = default;
+
+  bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
+            Regs* regs) override;
+
+  const DwarfCie* GetCie(uint64_t offset);
+  bool FillInCie(DwarfCie* cie);
+
+  const DwarfFde* GetFdeFromOffset(uint64_t offset) override;
+  bool FillInFde(DwarfFde* fde);
+
+  bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
+
+  bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) override;
+
+ protected:
+  bool EvalExpression(const DwarfLocation& loc, uint8_t version, Memory* regular_memory,
+                      AddressType* value);
+};
+
+#endif  // _LIBUNWINDSTACK_DWARF_SECTION_H
diff --git a/libunwindstack/DwarfStructs.h b/libunwindstack/DwarfStructs.h
new file mode 100644
index 0000000..87182c9
--- /dev/null
+++ b/libunwindstack/DwarfStructs.h
@@ -0,0 +1,52 @@
+/*
+ * 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_STRUCTS_H
+#define _LIBUNWINDSTACK_DWARF_STRUCTS_H
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "DwarfEncoding.h"
+
+struct DwarfCie {
+  uint8_t version = 0;
+  uint8_t fde_address_encoding = DW_EH_PE_absptr;
+  uint8_t lsda_encoding = DW_EH_PE_omit;
+  uint8_t segment_size = 0;
+  std::vector<char> augmentation_string;
+  uint64_t personality_handler = 0;
+  uint64_t cfa_instructions_offset = 0;
+  uint64_t cfa_instructions_end = 0;
+  uint64_t code_alignment_factor = 0;
+  int64_t data_alignment_factor = 0;
+  uint64_t return_address_register = 0;
+};
+
+struct DwarfFde {
+  uint64_t cie_offset = 0;
+  uint64_t cfa_instructions_offset = 0;
+  uint64_t cfa_instructions_end = 0;
+  uint64_t pc_start = 0;
+  uint64_t pc_end = 0;
+  uint64_t lsda_address = 0;
+  const DwarfCie* cie = nullptr;
+};
+
+constexpr uint16_t CFA_REG = static_cast<uint16_t>(-1);
+
+#endif  // _LIBUNWINDSTACK_DWARF_STRUCTS_H
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index d59e9d8..087457c 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -42,7 +42,7 @@
   uint64_t offset = ehdr.e_phoff;
   for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
     PhdrType phdr;
-    if (!memory_->Read(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
+    if (!memory_->ReadField(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
       return false;
     }
 
@@ -54,20 +54,20 @@
     case PT_LOAD:
     {
       // Get the flags first, if this isn't an executable header, ignore it.
-      if (!memory_->Read(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
         return false;
       }
       if ((phdr.p_flags & PF_X) == 0) {
         continue;
       }
 
-      if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
         return false;
       }
-      if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
         return false;
       }
-      if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
         return false;
       }
       pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
@@ -79,22 +79,22 @@
     }
 
     case PT_GNU_EH_FRAME:
-      if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
         return false;
       }
       eh_frame_offset_ = phdr.p_offset;
-      if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
         return false;
       }
       eh_frame_size_ = phdr.p_memsz;
       break;
 
     case PT_DYNAMIC:
-      if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
         return false;
       }
       dynamic_offset_ = phdr.p_offset;
-      if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
         return false;
       }
       dynamic_size_ = phdr.p_memsz;
@@ -116,8 +116,8 @@
   ShdrType shdr;
   if (ehdr.e_shstrndx < ehdr.e_shnum) {
     uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
-    if (memory_->Read(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
-        && memory_->Read(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+    if (memory_->ReadField(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+        memory_->ReadField(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
       sec_offset = shdr.sh_offset;
       sec_size = shdr.sh_size;
     }
@@ -125,27 +125,27 @@
 
   // Skip the first header, it's always going to be NULL.
   for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
-    if (!memory_->Read(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
+    if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
       return false;
     }
 
     if (shdr.sh_type == SHT_PROGBITS) {
       // Look for the .debug_frame and .gnu_debugdata.
-      if (!memory_->Read(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
+      if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
         return false;
       }
       if (shdr.sh_name < sec_size) {
         std::string name;
         if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
           if (name == ".debug_frame") {
-            if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
-                && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+            if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+                memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
               debug_frame_offset_ = shdr.sh_offset;
               debug_frame_size_ = shdr.sh_size;
             }
           } else if (name == ".gnu_debugdata") {
-            if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
-                && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+            if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+                memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
               gnu_debugdata_offset_ = shdr.sh_offset;
               gnu_debugdata_size_ = shdr.sh_size;
             }
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index e157320..bab84cc 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -85,10 +85,10 @@
   }
 
   Elf32_Phdr phdr;
-  if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+  if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
     return true;
   }
-  if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+  if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
     return true;
   }
   // The load_bias_ should always be set by this time.
@@ -98,13 +98,15 @@
 }
 
 bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
-  return StepExidx(pc, regs, process_memory) ||
-      ElfInterface32::Step(pc, regs, process_memory);
+  // Dwarf unwind information is precise about whether a pc is covered or not,
+  // but arm unwind information only has ranges of pc. In order to avoid
+  // incorrectly doing a bad unwind using arm unwind information for a
+  // different function, always try and unwind with the dwarf information first.
+  return ElfInterface32::Step(pc, regs, process_memory) || StepExidx(pc, regs, process_memory);
 }
 
 bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) {
   RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
-  // First try arm, then try dwarf.
   uint64_t entry_offset;
   if (!FindEntry(pc, &entry_offset)) {
     return false;
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
new file mode 100644
index 0000000..051f700
--- /dev/null
+++ b/libunwindstack/MapInfo.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include "Elf.h"
+#include "MapInfo.h"
+#include "Maps.h"
+#include "Memory.h"
+
+Memory* MapInfo::CreateMemory(pid_t pid) {
+  if (end <= start) {
+    return nullptr;
+  }
+
+  elf_offset = 0;
+
+  // First try and use the file associated with the info.
+  if (!name.empty()) {
+    // Fail on device maps.
+    if (flags & MAPS_FLAGS_DEVICE_MAP) {
+      return nullptr;
+    }
+
+    std::unique_ptr<MemoryFileAtOffset> file_memory(new MemoryFileAtOffset);
+    uint64_t map_size;
+    if (offset != 0) {
+      // Only map in a piece of the file.
+      map_size = end - start;
+    } else {
+      map_size = UINT64_MAX;
+    }
+    if (file_memory->Init(name, offset, map_size)) {
+      // It's possible that a non-zero offset might not be pointing to
+      // valid elf data. Check if this is a valid elf, and if not assume
+      // that this was meant to incorporate the entire file.
+      if (offset != 0 && !Elf::IsValidElf(file_memory.get())) {
+        // Don't bother checking the validity that will happen on the elf init.
+        if (file_memory->Init(name, 0)) {
+          elf_offset = offset;
+          return file_memory.release();
+        }
+        // Fall through if the init fails.
+      } else {
+        return file_memory.release();
+      }
+    }
+  }
+
+  Memory* memory = nullptr;
+  if (pid == getpid()) {
+    memory = new MemoryLocal();
+  } else {
+    memory = new MemoryRemote(pid);
+  }
+  return new MemoryRange(memory, start, end);
+}
+
+Elf* MapInfo::GetElf(pid_t pid, bool) {
+  if (elf) {
+    return elf;
+  }
+
+  elf = new Elf(CreateMemory(pid));
+  elf->Init();
+  // 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/MapInfo.h b/libunwindstack/MapInfo.h
index 8342904..79a2ada 100644
--- a/libunwindstack/MapInfo.h
+++ b/libunwindstack/MapInfo.h
@@ -39,6 +39,7 @@
   uint64_t elf_offset;
 
   Memory* CreateMemory(pid_t pid);
+  Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
 };
 
 #endif  // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
new file mode 100644
index 0000000..b369c43
--- /dev/null
+++ b/libunwindstack/Maps.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "Maps.h"
+
+MapInfo* Maps::Find(uint64_t pc) {
+  if (maps_.empty()) {
+    return nullptr;
+  }
+  size_t first = 0;
+  size_t last = maps_.size();
+  while (first < last) {
+    size_t index = (first + last) / 2;
+    MapInfo* cur = &maps_[index];
+    if (pc >= cur->start && pc < cur->end) {
+      return cur;
+    } else if (pc < cur->start) {
+      last = index;
+    } else {
+      first = index + 1;
+    }
+  }
+  return nullptr;
+}
+
+bool Maps::ParseLine(const char* line, MapInfo* map_info) {
+  char permissions[5];
+  int name_pos;
+  // Linux /proc/<pid>/maps lines:
+  // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so
+  if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %" SCNx64 " %*x:%*x %*d %n", &map_info->start,
+             &map_info->end, permissions, &map_info->offset, &name_pos) != 4) {
+    return false;
+  }
+  map_info->flags = PROT_NONE;
+  if (permissions[0] == 'r') {
+    map_info->flags |= PROT_READ;
+  }
+  if (permissions[1] == 'w') {
+    map_info->flags |= PROT_WRITE;
+  }
+  if (permissions[2] == 'x') {
+    map_info->flags |= PROT_EXEC;
+  }
+
+  map_info->name = &line[name_pos];
+  size_t length = map_info->name.length() - 1;
+  if (map_info->name[length] == '\n') {
+    map_info->name.erase(length);
+  }
+  // Mark a device map in /dev/and not in /dev/ashmem/ specially.
+  if (!map_info->name.empty() && map_info->name.substr(0, 5) == "/dev/" &&
+      map_info->name.substr(5, 7) != "ashmem/") {
+    map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
+  }
+
+  return true;
+}
+
+bool Maps::Parse() {
+  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(GetMapsFile().c_str(), "re"), fclose);
+  if (!fp) {
+    return false;
+  }
+
+  bool valid = true;
+  char* line = nullptr;
+  size_t line_len;
+  while (getline(&line, &line_len, fp.get()) > 0) {
+    MapInfo map_info;
+    if (!ParseLine(line, &map_info)) {
+      valid = false;
+      break;
+    }
+
+    maps_.push_back(map_info);
+  }
+  free(line);
+
+  return valid;
+}
+
+Maps::~Maps() {
+  for (auto& map : maps_) {
+    delete map.elf;
+    map.elf = nullptr;
+  }
+}
+
+bool BufferMaps::Parse() {
+  const char* start_of_line = buffer_;
+  do {
+    std::string line;
+    const char* end_of_line = strchr(start_of_line, '\n');
+    if (end_of_line == nullptr) {
+      line = start_of_line;
+    } else {
+      end_of_line++;
+      line = std::string(start_of_line, end_of_line - start_of_line);
+    }
+
+    MapInfo map_info;
+    if (!ParseLine(line.c_str(), &map_info)) {
+      return false;
+    }
+    maps_.push_back(map_info);
+
+    start_of_line = end_of_line;
+  } while (start_of_line != nullptr && *start_of_line != '\0');
+  return true;
+}
+
+const std::string RemoteMaps::GetMapsFile() const {
+  return "/proc/" + std::to_string(pid_) + "/maps";
+}
+
+bool OfflineMaps::Parse() {
+  // Format of maps information:
+  //   <uint64_t> StartOffset
+  //   <uint64_t> EndOffset
+  //   <uint64_t> offset
+  //   <uint16_t> flags
+  //   <uint16_t> MapNameLength
+  //   <VariableLengthValue> MapName
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file_.c_str(), O_RDONLY)));
+  if (fd == -1) {
+    return false;
+  }
+
+  std::vector<char> name;
+  while (true) {
+    MapInfo map_info;
+    ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.start, sizeof(map_info.start)));
+    if (bytes == 0) {
+      break;
+    }
+    if (bytes == -1 || bytes != sizeof(map_info.start)) {
+      return false;
+    }
+    bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.end, sizeof(map_info.end)));
+    if (bytes == -1 || bytes != sizeof(map_info.end)) {
+      return false;
+    }
+    bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.offset, sizeof(map_info.offset)));
+    if (bytes == -1 || bytes != sizeof(map_info.offset)) {
+      return false;
+    }
+    bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.flags, sizeof(map_info.flags)));
+    if (bytes == -1 || bytes != sizeof(map_info.flags)) {
+      return false;
+    }
+    uint16_t len;
+    bytes = TEMP_FAILURE_RETRY(read(fd, &len, sizeof(len)));
+    if (bytes == -1 || bytes != sizeof(len)) {
+      return false;
+    }
+    if (len > 0) {
+      name.resize(len);
+      bytes = TEMP_FAILURE_RETRY(read(fd, name.data(), len));
+      if (bytes == -1 || bytes != len) {
+        return false;
+      }
+      map_info.name = std::string(name.data(), len);
+    }
+    maps_.push_back(map_info);
+  }
+  return true;
+}
diff --git a/libunwindstack/Maps.h b/libunwindstack/Maps.h
new file mode 100644
index 0000000..239b64a
--- /dev/null
+++ b/libunwindstack/Maps.h
@@ -0,0 +1,107 @@
+/*
+ * 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_MAPS_H
+#define _LIBUNWINDSTACK_MAPS_H
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "Elf.h"
+#include "MapInfo.h"
+
+// Special flag to indicate a map is in /dev/. However, a map in
+// /dev/ashmem/... does not set this flag.
+static constexpr int MAPS_FLAGS_DEVICE_MAP = 0x8000;
+
+class Maps {
+ public:
+  Maps() = default;
+  virtual ~Maps();
+
+  MapInfo* Find(uint64_t pc);
+
+  bool ParseLine(const char* line, MapInfo* map_info);
+
+  virtual bool Parse();
+
+  virtual const std::string GetMapsFile() const { return ""; }
+
+  typedef std::vector<MapInfo>::iterator iterator;
+  iterator begin() { return maps_.begin(); }
+  iterator end() { return maps_.end(); }
+
+  typedef std::vector<MapInfo>::const_iterator const_iterator;
+  const_iterator begin() const { return maps_.begin(); }
+  const_iterator end() const { return maps_.end(); }
+
+  size_t Total() { return maps_.size(); }
+
+ protected:
+  std::vector<MapInfo> maps_;
+};
+
+class RemoteMaps : public Maps {
+ public:
+  RemoteMaps(pid_t pid) : pid_(pid) {}
+  virtual ~RemoteMaps() = default;
+
+  virtual const std::string GetMapsFile() const override;
+
+ private:
+  pid_t pid_;
+};
+
+class LocalMaps : public RemoteMaps {
+ public:
+  LocalMaps() : RemoteMaps(getpid()) {}
+  virtual ~LocalMaps() = default;
+};
+
+class BufferMaps : public Maps {
+ public:
+  BufferMaps(const char* buffer) : buffer_(buffer) {}
+  virtual ~BufferMaps() = default;
+
+  bool Parse() override;
+
+ private:
+  const char* buffer_;
+};
+
+class FileMaps : public Maps {
+ public:
+  FileMaps(const std::string& file) : file_(file) {}
+  virtual ~FileMaps() = default;
+
+  const std::string GetMapsFile() const override { return file_; }
+
+ protected:
+  const std::string file_;
+};
+
+class OfflineMaps : public FileMaps {
+ public:
+  OfflineMaps(const std::string& file) : FileMaps(file) {}
+  virtual ~OfflineMaps() = default;
+
+  bool Parse() override;
+};
+
+#endif  // _LIBUNWINDSTACK_MAPS_H
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 1fcf842..9e46509 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -96,10 +96,16 @@
 
   offset_ = offset & (getpagesize() - 1);
   uint64_t aligned_offset = offset & ~(getpagesize() - 1);
+  if (aligned_offset > static_cast<uint64_t>(buf.st_size) ||
+      offset > static_cast<uint64_t>(buf.st_size)) {
+    return false;
+  }
+
   size_ = buf.st_size - aligned_offset;
-  if (size < (UINT64_MAX - offset_) && size + offset_ < size_) {
+  uint64_t max_size;
+  if (!__builtin_add_overflow(size, offset_, &max_size) && max_size < size_) {
     // Truncate the mapped size.
-    size_ = size + offset_;
+    size_ = max_size;
   }
   void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
   if (map == MAP_FAILED) {
@@ -113,14 +119,15 @@
 }
 
 bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
-  if (addr + size > size_) {
+  uint64_t max_size;
+  if (__builtin_add_overflow(addr, size, &max_size) || max_size > size_) {
     return false;
   }
   memcpy(dst, &data_[addr], size);
   return true;
 }
 
-static bool PtraceRead(pid_t pid, uint64_t addr, long* value) {
+bool MemoryRemote::PtraceRead(uint64_t addr, long* value) {
 #if !defined(__LP64__)
   // Cannot read an address greater than 32 bits.
   if (addr > UINT32_MAX) {
@@ -130,7 +137,7 @@
   // ptrace() returns -1 and sets errno when the operation fails.
   // To disambiguate -1 from a valid result, we clear errno beforehand.
   errno = 0;
-  *value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr);
+  *value = ptrace(PTRACE_PEEKTEXT, pid_, reinterpret_cast<void*>(addr), nullptr);
   if (*value == -1 && errno) {
     return false;
   }
@@ -138,11 +145,17 @@
 }
 
 bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) {
+  // Make sure that there is no overflow.
+  uint64_t max_size;
+  if (__builtin_add_overflow(addr, bytes, &max_size)) {
+    return false;
+  }
+
   size_t bytes_read = 0;
   long data;
   size_t align_bytes = addr & (sizeof(long) - 1);
   if (align_bytes != 0) {
-    if (!PtraceRead(pid_, addr & ~(sizeof(long) - 1), &data)) {
+    if (!PtraceRead(addr & ~(sizeof(long) - 1), &data)) {
       return false;
     }
     size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
@@ -154,7 +167,7 @@
   }
 
   for (size_t i = 0; i < bytes / sizeof(long); i++) {
-    if (!PtraceRead(pid_, addr, &data)) {
+    if (!PtraceRead(addr, &data)) {
       return false;
     }
     memcpy(dst, &data, sizeof(long));
@@ -165,7 +178,7 @@
 
   size_t left_over = bytes & (sizeof(long) - 1);
   if (left_over) {
-    if (!PtraceRead(pid_, addr, &data)) {
+    if (!PtraceRead(addr, &data)) {
       return false;
     }
     memcpy(dst, &data, left_over);
@@ -175,7 +188,13 @@
 }
 
 bool MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
-  // The process_vm_readv call does will not always work on remote
+  // Make sure that there is no overflow.
+  uint64_t max_size;
+  if (__builtin_add_overflow(addr, size, &max_size)) {
+    return false;
+  }
+
+  // The process_vm_readv call will not always work on remote
   // processes, so only use it for reads from the current pid.
   // Use this method to avoid crashes if an address is invalid since
   // unwind data could try to access any part of the address space.
@@ -208,9 +227,29 @@
 }
 
 bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
-  if (addr < start_ || addr + size > start_ + offset_ + size_) {
+  uint64_t max_size;
+  if (__builtin_add_overflow(addr, size, &max_size)) {
+    return false;
+  }
+
+  uint64_t real_size;
+  if (__builtin_add_overflow(start_, offset_, &real_size) ||
+      __builtin_add_overflow(real_size, size_, &real_size)) {
+    return false;
+  }
+
+  if (addr < start_ || max_size > real_size) {
     return false;
   }
   memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size);
   return true;
 }
+
+bool MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
+  uint64_t max_read;
+  if (__builtin_add_overflow(addr, size, &max_read) || max_read > length_) {
+    return false;
+  }
+  // The check above guarantees that addr + begin_ will not overflow.
+  return memory_->Read(addr + begin_, dst, size);
+}
diff --git a/libunwindstack/Memory.h b/libunwindstack/Memory.h
index c5316a1..f9f6d56 100644
--- a/libunwindstack/Memory.h
+++ b/libunwindstack/Memory.h
@@ -17,6 +17,7 @@
 #ifndef _LIBUNWINDSTACK_MEMORY_H
 #define _LIBUNWINDSTACK_MEMORY_H
 
+#include <assert.h>
 #include <stdint.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -33,9 +34,16 @@
 
   virtual bool Read(uint64_t addr, void* dst, size_t size) = 0;
 
-  inline bool Read(uint64_t addr, void* start, void* field, size_t size) {
-    return Read(addr + reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start),
-                field, size);
+  inline bool ReadField(uint64_t addr, void* start, void* field, size_t size) {
+    if (reinterpret_cast<uintptr_t>(field) < reinterpret_cast<uintptr_t>(start)) {
+      return false;
+    }
+    uint64_t offset = reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start);
+    if (__builtin_add_overflow(addr, offset, &offset)) {
+      return false;
+    }
+    // The read will check if offset + size overflows.
+    return Read(offset, field, size);
   }
 
   inline bool Read32(uint64_t addr, uint32_t* dst) {
@@ -103,6 +111,9 @@
 
   pid_t pid() { return pid_; }
 
+ protected:
+  virtual bool PtraceRead(uint64_t addr, long* value);
+
  private:
   pid_t pid_;
 };
@@ -118,15 +129,12 @@
 class MemoryRange : public Memory {
  public:
   MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
-      : memory_(memory), begin_(begin), length_(end - begin_) {}
+      : memory_(memory), begin_(begin), length_(end - begin) {
+    assert(end > begin);
+  }
   virtual ~MemoryRange() { delete memory_; }
 
-  inline bool Read(uint64_t addr, void* dst, size_t size) override {
-    if (addr + size <= length_) {
-      return memory_->Read(addr + begin_, dst, size);
-    }
-    return false;
-  }
+  bool Read(uint64_t addr, void* dst, size_t size) override;
 
  private:
   Memory* memory_;
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index adb6522..e7d10b2 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -30,7 +30,7 @@
 #include "User.h"
 
 template <typename AddressType>
-uint64_t RegsTmpl<AddressType>::GetRelPc(Elf* elf, const MapInfo* map_info) {
+uint64_t RegsImpl<AddressType>::GetRelPc(Elf* elf, const MapInfo* map_info) {
   uint64_t load_bias = 0;
   if (elf->valid()) {
     load_bias = elf->interface()->load_bias();
@@ -40,7 +40,7 @@
 }
 
 template <typename AddressType>
-bool RegsTmpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) {
+bool RegsImpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) {
   switch (return_loc_.type) {
   case LOCATION_REGISTER:
     assert(return_loc_.value < total_regs_);
@@ -59,9 +59,8 @@
   }
 }
 
-RegsArm::RegsArm() : RegsTmpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP,
-                                        Location(LOCATION_REGISTER, ARM_REG_LR)) {
-}
+RegsArm::RegsArm()
+    : RegsImpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
 
 uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
@@ -89,9 +88,8 @@
   return rel_pc - 4;
 }
 
-RegsArm64::RegsArm64() : RegsTmpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP,
-                                            Location(LOCATION_REGISTER, ARM64_REG_LR)) {
-}
+RegsArm64::RegsArm64()
+    : RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
 
 uint64_t RegsArm64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
@@ -104,9 +102,8 @@
   return rel_pc - 4;
 }
 
-RegsX86::RegsX86() : RegsTmpl<uint32_t>(X86_REG_LAST, X86_REG_SP,
-                                        Location(LOCATION_SP_OFFSET, -4)) {
-}
+RegsX86::RegsX86()
+    : RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
 
 uint64_t RegsX86::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
@@ -119,9 +116,8 @@
   return rel_pc - 1;
 }
 
-RegsX86_64::RegsX86_64() : RegsTmpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP,
-                                              Location(LOCATION_SP_OFFSET, -8)) {
-}
+RegsX86_64::RegsX86_64()
+    : RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
 
 uint64_t RegsX86_64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
diff --git a/libunwindstack/Regs.h b/libunwindstack/Regs.h
index 718fc85..8f5a721 100644
--- a/libunwindstack/Regs.h
+++ b/libunwindstack/Regs.h
@@ -66,11 +66,11 @@
 };
 
 template <typename AddressType>
-class RegsTmpl : public Regs {
+class RegsImpl : public Regs {
  public:
-  RegsTmpl(uint16_t total_regs, uint16_t sp_reg, Location return_loc)
+  RegsImpl(uint16_t total_regs, uint16_t sp_reg, Location return_loc)
       : Regs(total_regs, sp_reg, return_loc), regs_(total_regs) {}
-  virtual ~RegsTmpl() = default;
+  virtual ~RegsImpl() = default;
 
   uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) override;
 
@@ -92,7 +92,7 @@
   std::vector<AddressType> regs_;
 };
 
-class RegsArm : public RegsTmpl<uint32_t> {
+class RegsArm : public RegsImpl<uint32_t> {
  public:
   RegsArm();
   virtual ~RegsArm() = default;
@@ -100,7 +100,7 @@
   uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
 };
 
-class RegsArm64 : public RegsTmpl<uint64_t> {
+class RegsArm64 : public RegsImpl<uint64_t> {
  public:
   RegsArm64();
   virtual ~RegsArm64() = default;
@@ -108,7 +108,7 @@
   uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
 };
 
-class RegsX86 : public RegsTmpl<uint32_t> {
+class RegsX86 : public RegsImpl<uint32_t> {
  public:
   RegsX86();
   virtual ~RegsX86() = default;
@@ -116,7 +116,7 @@
   uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
 };
 
-class RegsX86_64 : public RegsTmpl<uint64_t> {
+class RegsX86_64 : public RegsImpl<uint64_t> {
  public:
   RegsX86_64();
   virtual ~RegsX86_64() = default;
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
new file mode 100644
index 0000000..86c1233
--- /dev/null
+++ b/libunwindstack/Symbols.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 <assert.h>
+#include <elf.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "Memory.h"
+#include "Symbols.h"
+
+Symbols::Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
+                 uint64_t str_size)
+    : cur_offset_(offset),
+      offset_(offset),
+      end_(offset + size),
+      entry_size_(entry_size),
+      str_offset_(str_offset),
+      str_end_(str_offset_ + str_size) {}
+
+const Symbols::Info* Symbols::GetInfoFromCache(uint64_t addr) {
+  // Binary search the table.
+  size_t first = 0;
+  size_t last = symbols_.size();
+  while (first < last) {
+    size_t current = first + (last - first) / 2;
+    const Info* info = &symbols_[current];
+    if (addr < info->start_offset) {
+      last = current;
+    } else if (addr < info->end_offset) {
+      return info;
+    } else {
+      first = current + 1;
+    }
+  }
+  return nullptr;
+}
+
+template <typename SymType>
+bool Symbols::GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
+                      uint64_t* func_offset) {
+  addr += load_bias;
+
+  if (symbols_.size() != 0) {
+    const Info* info = GetInfoFromCache(addr);
+    if (info) {
+      assert(addr >= info->start_offset && addr <= info->end_offset);
+      *func_offset = addr - info->start_offset;
+      return elf_memory->ReadString(info->str_offset, name, str_end_ - info->str_offset);
+    }
+  }
+
+  bool symbol_added = false;
+  bool return_value = false;
+  while (cur_offset_ + entry_size_ <= end_) {
+    SymType entry;
+    if (!elf_memory->Read(cur_offset_, &entry, sizeof(entry))) {
+      // Stop all processing, something looks like it is corrupted.
+      cur_offset_ = UINT64_MAX;
+      return false;
+    }
+    cur_offset_ += entry_size_;
+
+    if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_FUNC) {
+      // Treat st_value as virtual address.
+      uint64_t start_offset = entry.st_value;
+      if (entry.st_shndx != SHN_ABS) {
+        start_offset += load_bias;
+      }
+      uint64_t end_offset = start_offset + entry.st_size;
+
+      // Cache the value.
+      symbols_.emplace_back(start_offset, end_offset, str_offset_ + entry.st_name);
+      symbol_added = true;
+
+      if (addr >= start_offset && addr < end_offset) {
+        *func_offset = addr - start_offset;
+        uint64_t offset = str_offset_ + entry.st_name;
+        if (offset < str_end_) {
+          return_value = elf_memory->ReadString(offset, name, str_end_ - offset);
+        }
+        break;
+      }
+    }
+  }
+
+  if (symbol_added) {
+    std::sort(symbols_.begin(), symbols_.end(),
+              [](const Info& a, const Info& b) { return a.start_offset < b.start_offset; });
+  }
+  return return_value;
+}
+
+// Instantiate all of the needed template functions.
+template bool Symbols::GetName<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
+template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
new file mode 100644
index 0000000..3c0d033
--- /dev/null
+++ b/libunwindstack/Symbols.h
@@ -0,0 +1,64 @@
+/*
+ * 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_SYMBOLS_H
+#define _LIBUNWINDSTACK_SYMBOLS_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+// Forward declaration.
+class Memory;
+
+class Symbols {
+  struct Info {
+    Info(uint64_t start_offset, uint64_t end_offset, uint64_t str_offset)
+        : start_offset(start_offset), end_offset(end_offset), str_offset(str_offset) {}
+    uint64_t start_offset;
+    uint64_t end_offset;
+    uint64_t str_offset;
+  };
+
+ public:
+  Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
+          uint64_t str_size);
+  virtual ~Symbols() = default;
+
+  const Info* GetInfoFromCache(uint64_t addr);
+
+  template <typename SymType>
+  bool GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
+               uint64_t* func_offset);
+
+  void ClearCache() {
+    symbols_.clear();
+    cur_offset_ = offset_;
+  }
+
+ private:
+  uint64_t cur_offset_;
+  uint64_t offset_;
+  uint64_t end_;
+  uint64_t entry_size_;
+  uint64_t str_offset_;
+  uint64_t str_end_;
+
+  std::vector<Info> symbols_;
+};
+
+#endif  // _LIBUNWINDSTACK_SYMBOLS_H
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
new file mode 100644
index 0000000..967c2c2
--- /dev/null
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -0,0 +1,814 @@
+/*
+ * 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 <memory>
+#include <type_traits>
+#include <unordered_map>
+
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "DwarfCfa.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfStructs.h"
+#include "Log.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+template <typename TypeParam>
+class DwarfCfaLogTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+    memory_.Clear();
+
+    dmem_.reset(new DwarfMemory(&memory_));
+
+    cie_.cfa_instructions_offset = 0x1000;
+    cie_.cfa_instructions_end = 0x1030;
+    // These two values should be different to distinguish between
+    // operations that deal with code versus data.
+    cie_.code_alignment_factor = 4;
+    cie_.data_alignment_factor = 8;
+
+    fde_.cfa_instructions_offset = 0x2000;
+    fde_.cfa_instructions_end = 0x2030;
+    fde_.pc_start = 0x2000;
+    fde_.pc_end = 0x2000;
+    fde_.pc_end = 0x10000;
+    fde_.cie = &cie_;
+    cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
+  }
+
+  MemoryFake memory_;
+  std::unique_ptr<DwarfMemory> dmem_;
+  std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
+  DwarfCie cie_;
+  DwarfFde fde_;
+};
+TYPED_TEST_CASE_P(DwarfCfaLogTest);
+
+// NOTE: All class variable references have to be prefaced with this->.
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_illegal) {
+  for (uint8_t i = 0x17; i < 0x3f; i++) {
+    if (i == 0x2e || i == 0x2f) {
+      // Skip gnu extension ops.
+      continue;
+    }
+    this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
+
+    ResetLogs();
+    ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+    std::string expected = "4 unwind Illegal\n";
+    expected += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", i);
+    ASSERT_EQ(expected, GetFakeLogPrint());
+    ASSERT_EQ("", GetFakeLogBuf());
+  }
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_nop) {
+  this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x00});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+  std::string expected =
+      "4 unwind DW_CFA_nop\n"
+      "4 unwind Raw Data: 0x00\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_offset) {
+  this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x83, 0x04});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+  std::string expected =
+      "4 unwind DW_CFA_offset register(3) 4\n"
+      "4 unwind Raw Data: 0x83 0x04\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x83, 0x84, 0x01});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+  expected =
+      "4 unwind DW_CFA_offset register(3) 132\n"
+      "4 unwind Raw Data: 0x83 0x84 0x01\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended) {
+  this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x05, 0x03, 0x02});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+  std::string expected =
+      "4 unwind DW_CFA_offset_extended register(3) 2\n"
+      "4 unwind Raw Data: 0x05 0x03 0x02\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x05, 0x81, 0x01, 0x82, 0x12});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+  expected =
+      "4 unwind DW_CFA_offset_extended register(129) 2306\n"
+      "4 unwind Raw Data: 0x05 0x81 0x01 0x82 0x12\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended_sf) {
+  this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x11, 0x05, 0x10});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+  std::string expected =
+      "4 unwind DW_CFA_offset_extended_sf register(5) 16\n"
+      "4 unwind Raw Data: 0x11 0x05 0x10\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  // Check a negative value for the offset.
+  ResetLogs();
+  this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x11, 0x86, 0x01, 0xff, 0x7f});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+  expected =
+      "4 unwind DW_CFA_offset_extended_sf register(134) -1\n"
+      "4 unwind Raw Data: 0x11 0x86 0x01 0xff 0x7f\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_restore) {
+  this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0xc2});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+  std::string expected =
+      "4 unwind DW_CFA_restore register(2)\n"
+      "4 unwind Raw Data: 0xc2\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x82, 0x04, 0xc2});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3003));
+  expected =
+      "4 unwind DW_CFA_offset register(2) 4\n"
+      "4 unwind Raw Data: 0x82 0x04\n"
+      "4 unwind DW_CFA_restore register(2)\n"
+      "4 unwind Raw Data: 0xc2\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_restore_extended) {
+  this->memory_.SetMemory(0x4000, std::vector<uint8_t>{0x06, 0x08});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4000, 0x4002));
+  std::string expected =
+      "4 unwind DW_CFA_restore_extended register(8)\n"
+      "4 unwind Raw Data: 0x06 0x08\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5007));
+  expected =
+      "4 unwind DW_CFA_offset_extended register(258) 4\n"
+      "4 unwind Raw Data: 0x05 0x82 0x02 0x04\n"
+      "4 unwind DW_CFA_restore_extended register(258)\n"
+      "4 unwind Raw Data: 0x06 0x82 0x02\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_set_loc) {
+  uint8_t buffer[1 + sizeof(TypeParam)];
+  buffer[0] = 0x1;
+  TypeParam address;
+  std::string raw_data("Raw Data: 0x01 ");
+  std::string address_str;
+  if (std::is_same<TypeParam, uint32_t>::value) {
+    address = 0x81234578U;
+    address_str = "0x81234578";
+    raw_data += "0x78 0x45 0x23 0x81";
+  } else {
+    address = 0x8123456712345678ULL;
+    address_str = "0x8123456712345678";
+    raw_data += "0x78 0x56 0x34 0x12 0x67 0x45 0x23 0x81";
+  }
+  memcpy(&buffer[1], &address, sizeof(address));
+
+  this->memory_.SetMemory(0x50, buffer, sizeof(buffer));
+  ResetLogs();
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+  std::string expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
+  expected += "4 unwind " + raw_data + "\n";
+  expected += "4 unwind \n";
+  expected += "4 unwind PC " + address_str + "\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  // Check for a set going back.
+  ResetLogs();
+  this->fde_.pc_start = address + 0x10;
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+  expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
+  expected += "4 unwind " + raw_data + "\n";
+  expected += "4 unwind \n";
+  expected += "4 unwind PC " + address_str + "\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc) {
+  this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x44});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x201));
+  std::string expected =
+      "4 unwind DW_CFA_advance_loc 4\n"
+      "4 unwind Raw Data: 0x44\n"
+      "4 unwind \n"
+      "4 unwind PC 0x2010\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x200, 0x201));
+  expected =
+      "4 unwind DW_CFA_advance_loc 4\n"
+      "4 unwind Raw Data: 0x44\n"
+      "4 unwind \n"
+      "4 unwind PC 0x2110\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc1) {
+  this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x02, 0x04});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x202));
+  std::string expected =
+      "4 unwind DW_CFA_advance_loc1 4\n"
+      "4 unwind Raw Data: 0x02 0x04\n"
+      "4 unwind \n"
+      "4 unwind PC 0x2004\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x10, 0x200, 0x202));
+  expected =
+      "4 unwind DW_CFA_advance_loc1 4\n"
+      "4 unwind Raw Data: 0x02 0x04\n"
+      "4 unwind \n"
+      "4 unwind PC 0x2014\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc2) {
+  this->memory_.SetMemory(0x600, std::vector<uint8_t>{0x03, 0x04, 0x03});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x600, 0x603));
+  std::string expected =
+      "4 unwind DW_CFA_advance_loc2 772\n"
+      "4 unwind Raw Data: 0x03 0x04 0x03\n"
+      "4 unwind \n"
+      "4 unwind PC 0x2304\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1000, 0x600, 0x603));
+  expected =
+      "4 unwind DW_CFA_advance_loc2 772\n"
+      "4 unwind Raw Data: 0x03 0x04 0x03\n"
+      "4 unwind \n"
+      "4 unwind PC 0x3304\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc4) {
+  this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x04, 0x04, 0x03, 0x02, 0x01});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x505));
+  std::string expected =
+      "4 unwind DW_CFA_advance_loc4 16909060\n"
+      "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
+      "4 unwind \n"
+      "4 unwind PC 0x1022304\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x500, 0x505));
+  expected =
+      "4 unwind DW_CFA_advance_loc4 16909060\n"
+      "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
+      "4 unwind \n"
+      "4 unwind PC 0x1024304\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_undefined) {
+  this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x07, 0x09});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa02));
+  std::string expected =
+      "4 unwind DW_CFA_undefined register(9)\n"
+      "4 unwind Raw Data: 0x07 0x09\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  dwarf_loc_regs_t cie_loc_regs;
+  this->memory_.SetMemory(0x1a00, std::vector<uint8_t>{0x07, 0x81, 0x01});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1a00, 0x1a03));
+  expected =
+      "4 unwind DW_CFA_undefined register(129)\n"
+      "4 unwind Raw Data: 0x07 0x81 0x01\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_same) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x08, 0x7f});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  std::string expected =
+      "4 unwind DW_CFA_same_value register(127)\n"
+      "4 unwind Raw Data: 0x08 0x7f\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x08, 0xff, 0x01});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+  expected =
+      "4 unwind DW_CFA_same_value register(255)\n"
+      "4 unwind Raw Data: 0x08 0xff 0x01\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_register) {
+  this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x303));
+  std::string expected =
+      "4 unwind DW_CFA_register register(2) register(1)\n"
+      "4 unwind Raw Data: 0x09 0x02 0x01\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x09, 0xff, 0x01, 0xff, 0x03});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4305));
+  expected =
+      "4 unwind DW_CFA_register register(255) register(511)\n"
+      "4 unwind Raw Data: 0x09 0xff 0x01 0xff 0x03\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_state) {
+  this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x0a});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x301));
+
+  std::string expected =
+      "4 unwind DW_CFA_remember_state\n"
+      "4 unwind Raw Data: 0x0a\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x0b});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4301));
+
+  expected =
+      "4 unwind DW_CFA_restore_state\n"
+      "4 unwind Raw Data: 0x0b\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_state_cfa_offset_restore) {
+  this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x0a, 0x0e, 0x40, 0x0b});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3004));
+
+  std::string expected =
+      "4 unwind DW_CFA_remember_state\n"
+      "4 unwind Raw Data: 0x0a\n"
+      "4 unwind DW_CFA_def_cfa_offset 64\n"
+      "4 unwind Raw Data: 0x0e 0x40\n"
+      "4 unwind DW_CFA_restore_state\n"
+      "4 unwind Raw Data: 0x0b\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0c, 0x7f, 0x74});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+  std::string expected =
+      "4 unwind DW_CFA_def_cfa register(127) 116\n"
+      "4 unwind Raw Data: 0x0c 0x7f 0x74\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0c, 0xff, 0x02, 0xf4, 0x04});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+
+  expected =
+      "4 unwind DW_CFA_def_cfa register(383) 628\n"
+      "4 unwind Raw Data: 0x0c 0xff 0x02 0xf4 0x04\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_sf) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x12, 0x30, 0x25});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+  std::string expected =
+      "4 unwind DW_CFA_def_cfa_sf register(48) 37\n"
+      "4 unwind Raw Data: 0x12 0x30 0x25\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  // Test a negative value.
+  ResetLogs();
+  this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x12, 0xa3, 0x01, 0xfa, 0x7f});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+
+  expected =
+      "4 unwind DW_CFA_def_cfa_sf register(163) -6\n"
+      "4 unwind Raw Data: 0x12 0xa3 0x01 0xfa 0x7f\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_register) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0d, 0x72});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+  std::string expected =
+      "4 unwind DW_CFA_def_cfa_register register(114)\n"
+      "4 unwind Raw Data: 0x0d 0x72\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0d, 0xf9, 0x20});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+
+  expected =
+      "4 unwind DW_CFA_def_cfa_register register(4217)\n"
+      "4 unwind Raw Data: 0x0d 0xf9 0x20\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0e, 0x59});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+  std::string expected =
+      "4 unwind DW_CFA_def_cfa_offset 89\n"
+      "4 unwind Raw Data: 0x0e 0x59\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+  expected =
+      "4 unwind DW_CFA_def_cfa_offset 89\n"
+      "4 unwind Raw Data: 0x0e 0x59\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0e, 0xd4, 0x0a});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+
+  expected =
+      "4 unwind DW_CFA_def_cfa_offset 1364\n"
+      "4 unwind Raw Data: 0x0e 0xd4 0x0a\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset_sf) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x13, 0x23});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+  std::string expected =
+      "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
+      "4 unwind Raw Data: 0x13 0x23\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+  expected =
+      "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
+      "4 unwind Raw Data: 0x13 0x23\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  // Negative offset.
+  ResetLogs();
+  this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x13, 0xf6, 0x7f});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+
+  expected =
+      "4 unwind DW_CFA_def_cfa_offset_sf -10\n"
+      "4 unwind Raw Data: 0x13 0xf6 0x7f\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_expression) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0f, 0x04, 0x01, 0x02, 0x04, 0x05});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x106));
+
+  std::string expected =
+      "4 unwind DW_CFA_def_cfa_expression 4\n"
+      "4 unwind Raw Data: 0x0f 0x04 0x01 0x02 0x04 0x05\n"
+      "4 unwind   Illegal\n"
+      "4 unwind   Raw Data: 0x01\n"
+      "4 unwind   Illegal\n"
+      "4 unwind   Raw Data: 0x02\n"
+      "4 unwind   Illegal\n"
+      "4 unwind   Raw Data: 0x04\n"
+      "4 unwind   Illegal\n"
+      "4 unwind   Raw Data: 0x05\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  std::vector<uint8_t> ops{0x0f, 0x81, 0x01};
+  expected = "4 unwind Raw Data: 0x0f 0x81 0x01";
+  std::string op_string;
+  for (uint8_t i = 3; i < 132; i++) {
+    ops.push_back(0x05);
+    op_string +=
+        "4 unwind   Illegal\n"
+        "4 unwind   Raw Data: 0x05\n";
+    expected += " 0x05";
+    if (((i + 1) % 10) == 0) {
+      expected += "\n4 unwind Raw Data:";
+    }
+  }
+  expected += '\n';
+  this->memory_.SetMemory(0x200, ops);
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x284));
+
+  expected = "4 unwind DW_CFA_def_cfa_expression 129\n" + expected;
+  ASSERT_EQ(expected + op_string, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_expression) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x10, 0x04, 0x02, 0xc0, 0xc1});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+
+  std::string expected =
+      "4 unwind DW_CFA_expression register(4) 2\n"
+      "4 unwind Raw Data: 0x10 0x04 0x02 0xc0 0xc1\n"
+      "4 unwind   Illegal\n"
+      "4 unwind   Raw Data: 0xc0\n"
+      "4 unwind   Illegal\n"
+      "4 unwind   Raw Data: 0xc1\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  std::vector<uint8_t> ops{0x10, 0xff, 0x01, 0x82, 0x01};
+  expected = "4 unwind Raw Data: 0x10 0xff 0x01 0x82 0x01";
+  std::string op_string;
+  for (uint8_t i = 5; i < 135; i++) {
+    ops.push_back(0xa0 + (i - 5) % 96);
+    op_string += "4 unwind   Illegal\n";
+    op_string += android::base::StringPrintf("4 unwind   Raw Data: 0x%02x\n", ops.back());
+    expected += android::base::StringPrintf(" 0x%02x", ops.back());
+    if (((i + 1) % 10) == 0) {
+      expected += "\n4 unwind Raw Data:";
+    }
+  }
+  expected = "4 unwind DW_CFA_expression register(255) 130\n" + expected + "\n";
+
+  this->memory_.SetMemory(0x200, ops);
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x287));
+
+  ASSERT_EQ(expected + op_string, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x14, 0x45, 0x54});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+  std::string expected =
+      "4 unwind DW_CFA_val_offset register(69) 84\n"
+      "4 unwind Raw Data: 0x14 0x45 0x54\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x400, std::vector<uint8_t>{0x14, 0xa2, 0x02, 0xb4, 0x05});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x400, 0x405));
+
+  expected =
+      "4 unwind DW_CFA_val_offset register(290) 692\n"
+      "4 unwind Raw Data: 0x14 0xa2 0x02 0xb4 0x05\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset_sf) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x15, 0x56, 0x12});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+  std::string expected =
+      "4 unwind DW_CFA_val_offset_sf register(86) 18\n"
+      "4 unwind Raw Data: 0x15 0x56 0x12\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  // Negative value.
+  ResetLogs();
+  this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x15, 0xff, 0x01, 0xc0, 0x7f});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa05));
+
+  expected =
+      "4 unwind DW_CFA_val_offset_sf register(255) -64\n"
+      "4 unwind Raw Data: 0x15 0xff 0x01 0xc0 0x7f\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_val_expression) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x16, 0x05, 0x02, 0xb0, 0xb1});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+
+  std::string expected =
+      "4 unwind DW_CFA_val_expression register(5) 2\n"
+      "4 unwind Raw Data: 0x16 0x05 0x02 0xb0 0xb1\n"
+      "4 unwind   Illegal\n"
+      "4 unwind   Raw Data: 0xb0\n"
+      "4 unwind   Illegal\n"
+      "4 unwind   Raw Data: 0xb1\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  std::vector<uint8_t> ops{0x16, 0x83, 0x10, 0xa8, 0x01};
+  expected = "4 unwind Raw Data: 0x16 0x83 0x10 0xa8 0x01";
+  std::string op_string;
+  for (uint8_t i = 0; i < 168; i++) {
+    ops.push_back(0xa0 + (i % 96));
+    op_string += "4 unwind   Illegal\n";
+    op_string += android::base::StringPrintf("4 unwind   Raw Data: 0x%02x\n", ops.back());
+    expected += android::base::StringPrintf(" 0x%02x", ops.back());
+    if (((i + 6) % 10) == 0) {
+      expected += "\n4 unwind Raw Data:";
+    }
+  }
+  expected = "4 unwind DW_CFA_val_expression register(2051) 168\n" + expected + "\n";
+
+  this->memory_.SetMemory(0xa00, ops);
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xaad));
+
+  ASSERT_EQ(expected + op_string, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_args_size) {
+  this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2e, 0x04});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+
+  std::string expected =
+      "4 unwind DW_CFA_GNU_args_size 4\n"
+      "4 unwind Raw Data: 0x2e 0x04\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x2e, 0xa4, 0x80, 0x04});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5004));
+
+  expected =
+      "4 unwind DW_CFA_GNU_args_size 65572\n"
+      "4 unwind Raw Data: 0x2e 0xa4 0x80 0x04\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_negative_offset_extended) {
+  this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x2f, 0x08, 0x10});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+
+  std::string expected =
+      "4 unwind DW_CFA_GNU_negative_offset_extended register(8) 16\n"
+      "4 unwind Raw Data: 0x2f 0x08 0x10\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x2f, 0x81, 0x02, 0xff, 0x01});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+
+  expected =
+      "4 unwind DW_CFA_GNU_negative_offset_extended register(257) 255\n"
+      "4 unwind Raw Data: 0x2f 0x81 0x02 0xff 0x01\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_register_override) {
+  this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01, 0x09, 0x02, 0x04});
+
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x306));
+
+  std::string expected =
+      "4 unwind DW_CFA_register register(2) register(1)\n"
+      "4 unwind Raw Data: 0x09 0x02 0x01\n"
+      "4 unwind DW_CFA_register register(2) register(4)\n"
+      "4 unwind Raw Data: 0x09 0x02 0x04\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+                           cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+                           cfa_advance_loc, cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4,
+                           cfa_undefined, cfa_same, cfa_register, cfa_state,
+                           cfa_state_cfa_offset_restore, cfa_def_cfa, cfa_def_cfa_sf,
+                           cfa_def_cfa_register, cfa_def_cfa_offset, cfa_def_cfa_offset_sf,
+                           cfa_def_cfa_expression, cfa_expression, cfa_val_offset,
+                           cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
+                           cfa_gnu_negative_offset_extended, cfa_register_override);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
new file mode 100644
index 0000000..687af7d
--- /dev/null
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -0,0 +1,959 @@
+/*
+ * 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 <memory>
+#include <unordered_map>
+
+#include <gtest/gtest.h>
+
+#include "DwarfCfa.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfStructs.h"
+#include "Log.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+template <typename TypeParam>
+class DwarfCfaTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+    memory_.Clear();
+
+    dmem_.reset(new DwarfMemory(&memory_));
+
+    cie_.cfa_instructions_offset = 0x1000;
+    cie_.cfa_instructions_end = 0x1030;
+    // These two values should be different to distinguish between
+    // operations that deal with code versus data.
+    cie_.code_alignment_factor = 4;
+    cie_.data_alignment_factor = 8;
+
+    fde_.cfa_instructions_offset = 0x2000;
+    fde_.cfa_instructions_end = 0x2030;
+    fde_.pc_start = 0x2000;
+    fde_.cie = &cie_;
+
+    cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
+  }
+
+  MemoryFake memory_;
+  std::unique_ptr<DwarfMemory> dmem_;
+  std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
+  DwarfCie cie_;
+  DwarfFde fde_;
+};
+TYPED_TEST_CASE_P(DwarfCfaTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfCfaTest, cfa_illegal) {
+  for (uint8_t i = 0x17; i < 0x3f; i++) {
+    if (i == 0x2e || i == 0x2f) {
+      // Skip gnu extension ops.
+      continue;
+    }
+    this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
+    dwarf_loc_regs_t loc_regs;
+
+    ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->cfa_->last_error());
+    ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+
+    ASSERT_EQ("", GetFakeLogPrint());
+    ASSERT_EQ("", GetFakeLogBuf());
+  }
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_nop) {
+  this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x00});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+  ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+  ASSERT_EQ(0U, loc_regs.size());
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+// This test needs to be examined.
+TYPED_TEST_P(DwarfCfaTest, cfa_offset) {
+  this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x83, 0x04});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2002, &loc_regs));
+  ASSERT_EQ(0x2002U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  auto location = loc_regs.find(3);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+  ASSERT_EQ(32U, location->second.values[0]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x83, 0x84, 0x01});
+  loc_regs.clear();
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2100, 0x2103, &loc_regs));
+  ASSERT_EQ(0x2103U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  location = loc_regs.find(3);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+  ASSERT_EQ(1056U, location->second.values[0]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_offset_extended) {
+  this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x05, 0x03, 0x02});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs));
+  ASSERT_EQ(0x503U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  auto location = loc_regs.find(3);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+  ASSERT_EQ(2U, location->second.values[0]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  loc_regs.clear();
+  this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x05, 0x81, 0x01, 0x82, 0x12});
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs));
+  ASSERT_EQ(0x1505U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  location = loc_regs.find(129);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+  ASSERT_EQ(2306U, location->second.values[0]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_offset_extended_sf) {
+  this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x11, 0x05, 0x10});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs));
+  ASSERT_EQ(0x503U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  auto location = loc_regs.find(5);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+  ASSERT_EQ(0x80U, location->second.values[0]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  // Check a negative value for the offset.
+  ResetLogs();
+  loc_regs.clear();
+  this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x11, 0x86, 0x01, 0xff, 0x7f});
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs));
+  ASSERT_EQ(0x1505U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  location = loc_regs.find(134);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+  ASSERT_EQ(static_cast<uint64_t>(-8), location->second.values[0]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_restore) {
+  this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0xc2});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+  ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+  ASSERT_EQ(0U, loc_regs.size());
+
+  ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  dwarf_loc_regs_t cie_loc_regs;
+  cie_loc_regs[2] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+  this->cfa_->set_cie_loc_regs(&cie_loc_regs);
+  this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x82, 0x04, 0xc2});
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x3000, 0x3003, &loc_regs));
+  ASSERT_EQ(0x3003U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  auto location = loc_regs.find(2);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_restore_extended) {
+  this->memory_.SetMemory(0x4000, std::vector<uint8_t>{0x06, 0x08});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4000, 0x4002, &loc_regs));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+  ASSERT_EQ(0x4002U, this->dmem_->cur_offset());
+  ASSERT_EQ(0U, loc_regs.size());
+
+  ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  loc_regs.clear();
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02});
+  dwarf_loc_regs_t cie_loc_regs;
+  cie_loc_regs[258] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+  this->cfa_->set_cie_loc_regs(&cie_loc_regs);
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x5000, 0x5007, &loc_regs));
+  ASSERT_EQ(0x5007U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  auto location = loc_regs.find(258);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_set_loc) {
+  uint8_t buffer[1 + sizeof(TypeParam)];
+  buffer[0] = 0x1;
+  TypeParam address;
+  std::string raw_data("Raw Data: 0x01 ");
+  std::string address_str;
+  if (sizeof(TypeParam) == 4) {
+    address = 0x81234578U;
+    address_str = "0x81234578";
+    raw_data += "0x78 0x45 0x23 0x81";
+  } else {
+    address = 0x8123456712345678ULL;
+    address_str = "0x8123456712345678";
+    raw_data += "0x78 0x56 0x34 0x12 0x67 0x45 0x23 0x81";
+  }
+  memcpy(&buffer[1], &address, sizeof(address));
+
+  this->memory_.SetMemory(0x50, buffer, sizeof(buffer));
+  ResetLogs();
+  dwarf_loc_regs_t loc_regs;
+  ASSERT_TRUE(
+      this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam), &loc_regs));
+  ASSERT_EQ(0x51 + sizeof(TypeParam), this->dmem_->cur_offset());
+  ASSERT_EQ(address, this->cfa_->cur_pc());
+  ASSERT_EQ(0U, loc_regs.size());
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  // Check for a set going back.
+  ResetLogs();
+  loc_regs.clear();
+  this->fde_.pc_start = address + 0x10;
+  ASSERT_TRUE(
+      this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam), &loc_regs));
+  ASSERT_EQ(0x51 + sizeof(TypeParam), this->dmem_->cur_offset());
+  ASSERT_EQ(address, this->cfa_->cur_pc());
+  ASSERT_EQ(0U, loc_regs.size());
+
+  std::string cur_address_str(address_str);
+  cur_address_str[cur_address_str.size() - 2] = '8';
+  std::string expected = "4 unwind Warning: PC is moving backwards: old " + cur_address_str +
+                         " new " + address_str + "\n";
+  ASSERT_EQ(expected, GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc1) {
+  this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x02, 0x04});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x202, &loc_regs));
+  ASSERT_EQ(0x202U, this->dmem_->cur_offset());
+  ASSERT_EQ(this->fde_.pc_start + 0x10, this->cfa_->cur_pc());
+  ASSERT_EQ(0U, loc_regs.size());
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc2) {
+  this->memory_.SetMemory(0x600, std::vector<uint8_t>{0x03, 0x04, 0x03});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x600, 0x603, &loc_regs));
+  ASSERT_EQ(0x603U, this->dmem_->cur_offset());
+  ASSERT_EQ(this->fde_.pc_start + 0xc10U, this->cfa_->cur_pc());
+  ASSERT_EQ(0U, loc_regs.size());
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc4) {
+  this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x04, 0x04, 0x03, 0x02, 0x01});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x505, &loc_regs));
+  ASSERT_EQ(0x505U, this->dmem_->cur_offset());
+  ASSERT_EQ(this->fde_.pc_start + 0x4080c10, this->cfa_->cur_pc());
+  ASSERT_EQ(0U, loc_regs.size());
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_undefined) {
+  this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x07, 0x09});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xa02, &loc_regs));
+  ASSERT_EQ(0xa02U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  auto location = loc_regs.find(9);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location->second.type);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  loc_regs.clear();
+  this->memory_.SetMemory(0x1a00, std::vector<uint8_t>{0x07, 0x81, 0x01});
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1a00, 0x1a03, &loc_regs));
+  ASSERT_EQ(0x1a03U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  location = loc_regs.find(129);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location->second.type);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_same) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x08, 0x7f});
+  dwarf_loc_regs_t loc_regs;
+
+  loc_regs[127] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+  ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+  ASSERT_EQ(0U, loc_regs.size());
+  ASSERT_EQ(0U, loc_regs.count(127));
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  loc_regs.clear();
+  this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x08, 0xff, 0x01});
+
+  loc_regs[255] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2100, 0x2103, &loc_regs));
+  ASSERT_EQ(0x2103U, this->dmem_->cur_offset());
+  ASSERT_EQ(0U, loc_regs.size());
+  ASSERT_EQ(0U, loc_regs.count(255));
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_register) {
+  this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x303, &loc_regs));
+  ASSERT_EQ(0x303U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  auto location = loc_regs.find(2);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+  ASSERT_EQ(1U, location->second.values[0]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  loc_regs.clear();
+  this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x09, 0xff, 0x01, 0xff, 0x03});
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4300, 0x4305, &loc_regs));
+  ASSERT_EQ(0x4305U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  location = loc_regs.find(255);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+  ASSERT_EQ(511U, location->second.values[0]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_state) {
+  this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x0a});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x301, &loc_regs));
+  ASSERT_EQ(0x301U, this->dmem_->cur_offset());
+  ASSERT_EQ(0U, loc_regs.size());
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x0b});
+
+  loc_regs.clear();
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4300, 0x4301, &loc_regs));
+  ASSERT_EQ(0x4301U, this->dmem_->cur_offset());
+  ASSERT_EQ(0U, loc_regs.size());
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x85, 0x02, 0x0a, 0x86, 0x04, 0x0b});
+
+  loc_regs.clear();
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2005, &loc_regs));
+  ASSERT_EQ(0x2005U, this->dmem_->cur_offset());
+  ASSERT_EQ(2U, loc_regs.size());
+  ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+  ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+
+  loc_regs.clear();
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2006, &loc_regs));
+  ASSERT_EQ(0x2006U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+
+  ResetLogs();
+  this->memory_.SetMemory(
+      0x6000, std::vector<uint8_t>{0x0a, 0x85, 0x02, 0x0a, 0x86, 0x04, 0x0a, 0x87, 0x01, 0x0a, 0x89,
+                                   0x05, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b});
+
+  loc_regs.clear();
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600c, &loc_regs));
+  ASSERT_EQ(0x600cU, this->dmem_->cur_offset());
+  ASSERT_EQ(4U, loc_regs.size());
+  ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+  ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+  ASSERT_NE(loc_regs.end(), loc_regs.find(7));
+  ASSERT_NE(loc_regs.end(), loc_regs.find(9));
+
+  loc_regs.clear();
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600d, &loc_regs));
+  ASSERT_EQ(0x600dU, this->dmem_->cur_offset());
+  ASSERT_EQ(3U, loc_regs.size());
+  ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+  ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+  ASSERT_NE(loc_regs.end(), loc_regs.find(7));
+
+  loc_regs.clear();
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600e, &loc_regs));
+  ASSERT_EQ(0x600eU, this->dmem_->cur_offset());
+  ASSERT_EQ(2U, loc_regs.size());
+  ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+  ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+
+  loc_regs.clear();
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600f, &loc_regs));
+  ASSERT_EQ(0x600fU, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+
+  loc_regs.clear();
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x6010, &loc_regs));
+  ASSERT_EQ(0x6010U, this->dmem_->cur_offset());
+  ASSERT_EQ(0U, loc_regs.size());
+
+  loc_regs.clear();
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x6011, &loc_regs));
+  ASSERT_EQ(0x6011U, this->dmem_->cur_offset());
+  ASSERT_EQ(0U, loc_regs.size());
+}
+
+// This test verifies that the cfa offset is saved and restored properly.
+// Even though the spec is not clear about whether the offset is also
+// restored, the gcc unwinder does, and libunwind does too.
+TYPED_TEST_P(DwarfCfaTest, cfa_state_cfa_offset_restore) {
+  this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x0a, 0x0e, 0x40, 0x0b});
+  dwarf_loc_regs_t loc_regs;
+  loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {5, 100}};
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x3000, 0x3004, &loc_regs));
+  ASSERT_EQ(0x3004U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+  ASSERT_EQ(5U, loc_regs[CFA_REG].values[0]);
+  ASSERT_EQ(100U, loc_regs[CFA_REG].values[1]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0c, 0x7f, 0x74});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+  ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+  ASSERT_EQ(0x7fU, loc_regs[CFA_REG].values[0]);
+  ASSERT_EQ(0x74U, loc_regs[CFA_REG].values[1]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  loc_regs.clear();
+  this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0c, 0xff, 0x02, 0xf4, 0x04});
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x205, &loc_regs));
+  ASSERT_EQ(0x205U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+  ASSERT_EQ(0x17fU, loc_regs[CFA_REG].values[0]);
+  ASSERT_EQ(0x274U, loc_regs[CFA_REG].values[1]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_sf) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x12, 0x30, 0x25});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+  ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+  ASSERT_EQ(0x30U, loc_regs[CFA_REG].values[0]);
+  ASSERT_EQ(0x128U, loc_regs[CFA_REG].values[1]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  // Test a negative value.
+  ResetLogs();
+  loc_regs.clear();
+  this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x12, 0xa3, 0x01, 0xfa, 0x7f});
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x205, &loc_regs));
+  ASSERT_EQ(0x205U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+  ASSERT_EQ(0xa3U, loc_regs[CFA_REG].values[0]);
+  ASSERT_EQ(static_cast<uint64_t>(-48), loc_regs[CFA_REG].values[1]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_register) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0d, 0x72});
+  dwarf_loc_regs_t loc_regs;
+
+  // This fails because the cfa is not defined as a register.
+  ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+  ASSERT_EQ(0U, loc_regs.size());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+
+  ASSERT_EQ("4 unwind Attempt to set new register, but cfa is not already set to a register.\n",
+            GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  loc_regs.clear();
+  loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3, 20}};
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+  ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+  ASSERT_EQ(0x72U, loc_regs[CFA_REG].values[0]);
+  ASSERT_EQ(20U, loc_regs[CFA_REG].values[1]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0d, 0xf9, 0x20});
+  loc_regs.clear();
+  loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3, 60}};
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs));
+  ASSERT_EQ(0x203U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+  ASSERT_EQ(0x1079U, loc_regs[CFA_REG].values[0]);
+  ASSERT_EQ(60U, loc_regs[CFA_REG].values[1]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_offset) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0e, 0x59});
+  dwarf_loc_regs_t loc_regs;
+
+  // This fails because the cfa is not defined as a register.
+  ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+  ASSERT_EQ(0U, loc_regs.size());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+
+  ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
+            GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  loc_regs.clear();
+  loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+  ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+  ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+  ASSERT_EQ(0x59U, loc_regs[CFA_REG].values[1]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0e, 0xd4, 0x0a});
+  loc_regs.clear();
+  loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs));
+  ASSERT_EQ(0x203U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+  ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+  ASSERT_EQ(0x554U, loc_regs[CFA_REG].values[1]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_offset_sf) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x13, 0x23});
+  dwarf_loc_regs_t loc_regs;
+
+  // This fails because the cfa is not defined as a register.
+  ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+
+  ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
+            GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  loc_regs.clear();
+  loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+  ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+  ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+  ASSERT_EQ(0x118U, loc_regs[CFA_REG].values[1]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  // Negative offset.
+  ResetLogs();
+  this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x13, 0xf6, 0x7f});
+  loc_regs.clear();
+  loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs));
+  ASSERT_EQ(0x203U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+  ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+  ASSERT_EQ(static_cast<TypeParam>(-80), static_cast<TypeParam>(loc_regs[CFA_REG].values[1]));
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_expression) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0f, 0x04, 0x01, 0x02, 0x03, 0x04});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x106, &loc_regs));
+  ASSERT_EQ(0x106U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  std::vector<uint8_t> ops{0x0f, 0x81, 0x01};
+  for (uint8_t i = 3; i < 132; i++) {
+    ops.push_back(i - 1);
+  }
+  this->memory_.SetMemory(0x200, ops);
+  loc_regs.clear();
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x284, &loc_regs));
+  ASSERT_EQ(0x284U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_expression) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x10, 0x04, 0x02, 0x40, 0x20});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x105, &loc_regs));
+  ASSERT_EQ(0x105U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  auto location = loc_regs.find(4);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_EXPRESSION, location->second.type);
+  ASSERT_EQ(2U, location->second.values[0]);
+  ASSERT_EQ(0x105U, location->second.values[1]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  std::vector<uint8_t> ops{0x10, 0xff, 0x01, 0x82, 0x01};
+  for (uint8_t i = 5; i < 135; i++) {
+    ops.push_back(i - 4);
+  }
+
+  this->memory_.SetMemory(0x200, ops);
+  loc_regs.clear();
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x287, &loc_regs));
+  ASSERT_EQ(0x287U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  location = loc_regs.find(255);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_EXPRESSION, location->second.type);
+  ASSERT_EQ(130U, location->second.values[0]);
+  ASSERT_EQ(0x287U, location->second.values[1]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_val_offset) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x14, 0x45, 0x54});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+  ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  auto location = loc_regs.find(69);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+  ASSERT_EQ(0x2a0U, location->second.values[0]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  loc_regs.clear();
+  this->memory_.SetMemory(0x400, std::vector<uint8_t>{0x14, 0xa2, 0x02, 0xb4, 0x05});
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x400, 0x405, &loc_regs));
+  ASSERT_EQ(0x405U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  location = loc_regs.find(290);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+  ASSERT_EQ(0x15a0U, location->second.values[0]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_val_offset_sf) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x15, 0x56, 0x12});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+  ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  auto location = loc_regs.find(86);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+  ASSERT_EQ(0x90U, location->second.values[0]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  // Negative value.
+  ResetLogs();
+  loc_regs.clear();
+  this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x15, 0xff, 0x01, 0xc0, 0x7f});
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xa05, &loc_regs));
+  ASSERT_EQ(0xa05U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  location = loc_regs.find(255);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+  ASSERT_EQ(static_cast<uint64_t>(-512), location->second.values[0]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_val_expression) {
+  this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x16, 0x05, 0x02, 0x10, 0x20});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x105, &loc_regs));
+  ASSERT_EQ(0x105U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  auto location = loc_regs.find(5);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, location->second.type);
+  ASSERT_EQ(2U, location->second.values[0]);
+  ASSERT_EQ(0x105U, location->second.values[1]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  std::vector<uint8_t> ops{0x16, 0x83, 0x10, 0xa8, 0x01};
+  for (uint8_t i = 0; i < 168; i++) {
+    ops.push_back(i);
+  }
+
+  this->memory_.SetMemory(0xa00, ops);
+  loc_regs.clear();
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xaad, &loc_regs));
+  ASSERT_EQ(0xaadU, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  location = loc_regs.find(2051);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, location->second.type);
+  ASSERT_EQ(168U, location->second.values[0]);
+  ASSERT_EQ(0xaadU, location->second.values[1]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_gnu_args_size) {
+  this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2e, 0x04});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2002, &loc_regs));
+  ASSERT_EQ(0x2002U, this->dmem_->cur_offset());
+  ASSERT_EQ(0U, loc_regs.size());
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  loc_regs.clear();
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x2e, 0xa4, 0x80, 0x04});
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x5000, 0x5004, &loc_regs));
+  ASSERT_EQ(0x5004U, this->dmem_->cur_offset());
+  ASSERT_EQ(0U, loc_regs.size());
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_gnu_negative_offset_extended) {
+  this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x2f, 0x08, 0x10});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs));
+  ASSERT_EQ(0x503U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  auto location = loc_regs.find(8);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+  ASSERT_EQ(static_cast<uint64_t>(-16), location->second.values[0]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+
+  ResetLogs();
+  loc_regs.clear();
+  this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x2f, 0x81, 0x02, 0xff, 0x01});
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs));
+  ASSERT_EQ(0x1505U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  location = loc_regs.find(257);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+  ASSERT_EQ(static_cast<uint64_t>(-255), location->second.values[0]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_register_override) {
+  this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01, 0x09, 0x02, 0x04});
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x306, &loc_regs));
+  ASSERT_EQ(0x306U, this->dmem_->cur_offset());
+  ASSERT_EQ(1U, loc_regs.size());
+  auto location = loc_regs.find(2);
+  ASSERT_NE(loc_regs.end(), location);
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+  ASSERT_EQ(4U, location->second.values[0]);
+
+  ASSERT_EQ("", GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+                           cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+                           cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, cfa_undefined,
+                           cfa_same, cfa_register, cfa_state, cfa_state_cfa_offset_restore,
+                           cfa_def_cfa, cfa_def_cfa_sf, cfa_def_cfa_register, cfa_def_cfa_offset,
+                           cfa_def_cfa_offset_sf, cfa_def_cfa_expression, cfa_expression,
+                           cfa_val_offset, cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
+                           cfa_gnu_negative_offset_extended, cfa_register_override);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaTest, DwarfCfaTestTypes);
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
new file mode 100644
index 0000000..4877f36
--- /dev/null
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -0,0 +1,472 @@
+/*
+ * 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 <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "DwarfMemory.h"
+
+#include "MemoryFake.h"
+
+class DwarfMemoryTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_.Clear();
+    dwarf_mem_.reset(new DwarfMemory(&memory_));
+  }
+
+  template <typename AddressType>
+  void GetEncodedSizeTest(uint8_t value, size_t expected);
+  template <typename AddressType>
+  void ReadEncodedValue_omit();
+  template <typename AddressType>
+  void ReadEncodedValue_leb128();
+  template <typename AddressType>
+  void ReadEncodedValue_data1();
+  template <typename AddressType>
+  void ReadEncodedValue_data2();
+  template <typename AddressType>
+  void ReadEncodedValue_data4();
+  template <typename AddressType>
+  void ReadEncodedValue_data8();
+  template <typename AddressType>
+  void ReadEncodedValue_non_zero_adjust();
+  template <typename AddressType>
+  void ReadEncodedValue_overflow();
+
+  MemoryFake memory_;
+  std::unique_ptr<DwarfMemory> dwarf_mem_;
+};
+
+TEST_F(DwarfMemoryTest, ReadBytes) {
+  memory_.SetMemory(0, std::vector<uint8_t>{0x10, 0x18, 0xff, 0xfe});
+
+  uint8_t byte;
+  ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+  ASSERT_EQ(0x10U, byte);
+  ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+  ASSERT_EQ(0x18U, byte);
+  ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+  ASSERT_EQ(0xffU, byte);
+  ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+  ASSERT_EQ(0xfeU, byte);
+  ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+
+  dwarf_mem_->set_cur_offset(2);
+  ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+  ASSERT_EQ(0xffU, byte);
+  ASSERT_EQ(3U, dwarf_mem_->cur_offset());
+}
+
+TEST_F(DwarfMemoryTest, ReadSigned_check) {
+  uint64_t value;
+
+  // Signed 8 byte reads.
+  memory_.SetData8(0, static_cast<uint8_t>(-10));
+  memory_.SetData8(1, 200);
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int8_t>(&value));
+  ASSERT_EQ(static_cast<int8_t>(-10), static_cast<int8_t>(value));
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int8_t>(&value));
+  ASSERT_EQ(static_cast<int8_t>(200), static_cast<int8_t>(value));
+
+  // Signed 16 byte reads.
+  memory_.SetData16(0x10, static_cast<uint16_t>(-1000));
+  memory_.SetData16(0x12, 50100);
+  dwarf_mem_->set_cur_offset(0x10);
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int16_t>(&value));
+  ASSERT_EQ(static_cast<int16_t>(-1000), static_cast<int16_t>(value));
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int16_t>(&value));
+  ASSERT_EQ(static_cast<int16_t>(50100), static_cast<int16_t>(value));
+
+  // Signed 32 byte reads.
+  memory_.SetData32(0x100, static_cast<uint32_t>(-1000000000));
+  memory_.SetData32(0x104, 3000000000);
+  dwarf_mem_->set_cur_offset(0x100);
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int32_t>(&value));
+  ASSERT_EQ(static_cast<int32_t>(-1000000000), static_cast<int32_t>(value));
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int32_t>(&value));
+  ASSERT_EQ(static_cast<int32_t>(3000000000), static_cast<int32_t>(value));
+
+  // Signed 64 byte reads.
+  memory_.SetData64(0x200, static_cast<uint64_t>(-2000000000000LL));
+  memory_.SetData64(0x208, 5000000000000LL);
+  dwarf_mem_->set_cur_offset(0x200);
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int64_t>(&value));
+  ASSERT_EQ(static_cast<int64_t>(-2000000000000), static_cast<int64_t>(value));
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int64_t>(&value));
+  ASSERT_EQ(static_cast<int64_t>(5000000000000), static_cast<int64_t>(value));
+}
+
+TEST_F(DwarfMemoryTest, ReadULEB128) {
+  memory_.SetMemory(0, std::vector<uint8_t>{0x01, 0x80, 0x24, 0xff, 0xc3, 0xff, 0x7f});
+
+  uint64_t value;
+  ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+  ASSERT_EQ(1U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(1U, value);
+
+  ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+  ASSERT_EQ(3U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0x1200U, value);
+
+  ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+  ASSERT_EQ(7U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0xfffe1ffU, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadSLEB128) {
+  memory_.SetMemory(0, std::vector<uint8_t>{0x06, 0x40, 0x82, 0x34, 0x89, 0x64, 0xf9, 0xc3, 0x8f,
+                                            0x2f, 0xbf, 0xc3, 0xf7, 0x5f});
+
+  int64_t value;
+  ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+  ASSERT_EQ(1U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(6U, value);
+
+  ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+  ASSERT_EQ(2U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0xffffffffffffffc0ULL, static_cast<uint64_t>(value));
+
+  ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+  ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0x1a02U, value);
+
+  ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+  ASSERT_EQ(6U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0xfffffffffffff209ULL, static_cast<uint64_t>(value));
+
+  ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+  ASSERT_EQ(10U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0x5e3e1f9U, value);
+
+  ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+  ASSERT_EQ(14U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0xfffffffffbfde1bfULL, static_cast<uint64_t>(value));
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::GetEncodedSizeTest(uint8_t value, size_t expected) {
+  for (size_t i = 0; i < 16; i++) {
+    uint8_t encoding = (i << 4) | value;
+    ASSERT_EQ(expected, dwarf_mem_->GetEncodedSize<AddressType>(encoding))
+        << "encoding 0x" << std::hex << static_cast<uint32_t>(encoding) << " test value 0x"
+        << static_cast<size_t>(value);
+  }
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_absptr_uint32_t) {
+  GetEncodedSizeTest<uint32_t>(0, sizeof(uint32_t));
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_absptr_uint64_t) {
+  GetEncodedSizeTest<uint64_t>(0, sizeof(uint64_t));
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data1) {
+  // udata1
+  GetEncodedSizeTest<uint32_t>(0x0d, 1);
+  GetEncodedSizeTest<uint64_t>(0x0d, 1);
+
+  // sdata1
+  GetEncodedSizeTest<uint32_t>(0x0e, 1);
+  GetEncodedSizeTest<uint64_t>(0x0e, 1);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data2) {
+  // udata2
+  GetEncodedSizeTest<uint32_t>(0x02, 2);
+  GetEncodedSizeTest<uint64_t>(0x02, 2);
+
+  // sdata2
+  GetEncodedSizeTest<uint32_t>(0x0a, 2);
+  GetEncodedSizeTest<uint64_t>(0x0a, 2);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data4) {
+  // udata4
+  GetEncodedSizeTest<uint32_t>(0x03, 4);
+  GetEncodedSizeTest<uint64_t>(0x03, 4);
+
+  // sdata4
+  GetEncodedSizeTest<uint32_t>(0x0b, 4);
+  GetEncodedSizeTest<uint64_t>(0x0b, 4);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data8) {
+  // udata8
+  GetEncodedSizeTest<uint32_t>(0x04, 8);
+  GetEncodedSizeTest<uint64_t>(0x04, 8);
+
+  // sdata8
+  GetEncodedSizeTest<uint32_t>(0x0c, 8);
+  GetEncodedSizeTest<uint64_t>(0x0c, 8);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_unknown) {
+  GetEncodedSizeTest<uint32_t>(0x01, 0);
+  GetEncodedSizeTest<uint64_t>(0x01, 0);
+
+  GetEncodedSizeTest<uint32_t>(0x09, 0);
+  GetEncodedSizeTest<uint64_t>(0x09, 0);
+
+  GetEncodedSizeTest<uint32_t>(0x0f, 0);
+  GetEncodedSizeTest<uint64_t>(0x0f, 0);
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_omit() {
+  uint64_t value = 123;
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0xff, &value));
+  ASSERT_EQ(0U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) { ReadEncodedValue_omit<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) { ReadEncodedValue_omit<uint64_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint32_t) {
+  uint64_t value = 100;
+  ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x00, &value));
+
+  memory_.SetData32(0, 0x12345678);
+
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x00, &value));
+  ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0x12345678U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint64_t) {
+  uint64_t value = 100;
+  ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x00, &value));
+
+  memory_.SetData64(0, 0x12345678f1f2f3f4ULL);
+
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x00, &value));
+  ASSERT_EQ(8U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0x12345678f1f2f3f4ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_aligned_uint32_t) {
+  uint64_t value = 100;
+  dwarf_mem_->set_cur_offset(1);
+  ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x50, &value));
+
+  memory_.SetData32(4, 0x12345678);
+
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x50, &value));
+  ASSERT_EQ(8U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0x12345678U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_aligned_uint64_t) {
+  uint64_t value = 100;
+  dwarf_mem_->set_cur_offset(1);
+  ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x50, &value));
+
+  memory_.SetData64(8, 0x12345678f1f2f3f4ULL);
+
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x50, &value));
+  ASSERT_EQ(16U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0x12345678f1f2f3f4ULL, value);
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_leb128() {
+  memory_.SetMemory(0, std::vector<uint8_t>{0x80, 0x42});
+
+  uint64_t value = 100;
+  // uleb128
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x01, &value));
+  ASSERT_EQ(0x2100U, value);
+
+  dwarf_mem_->set_cur_offset(0);
+  // sleb128
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x09, &value));
+  ASSERT_EQ(0xffffffffffffe100ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) { ReadEncodedValue_leb128<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) { ReadEncodedValue_leb128<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data1() {
+  memory_.SetData8(0, 0xe0);
+
+  uint64_t value = 0;
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0d, &value));
+  ASSERT_EQ(0xe0U, value);
+
+  dwarf_mem_->set_cur_offset(0);
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0e, &value));
+  ASSERT_EQ(0xffffffffffffffe0ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) { ReadEncodedValue_data1<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) { ReadEncodedValue_data1<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data2() {
+  memory_.SetData16(0, 0xe000);
+
+  uint64_t value = 0;
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x02, &value));
+  ASSERT_EQ(0xe000U, value);
+
+  dwarf_mem_->set_cur_offset(0);
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0a, &value));
+  ASSERT_EQ(0xffffffffffffe000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) { ReadEncodedValue_data2<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) { ReadEncodedValue_data2<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data4() {
+  memory_.SetData32(0, 0xe0000000);
+
+  uint64_t value = 0;
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x03, &value));
+  ASSERT_EQ(0xe0000000U, value);
+
+  dwarf_mem_->set_cur_offset(0);
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0b, &value));
+  ASSERT_EQ(0xffffffffe0000000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) { ReadEncodedValue_data4<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) { ReadEncodedValue_data4<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data8() {
+  memory_.SetData64(0, 0xe000000000000000ULL);
+
+  uint64_t value = 0;
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x04, &value));
+  ASSERT_EQ(0xe000000000000000ULL, value);
+
+  dwarf_mem_->set_cur_offset(0);
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0c, &value));
+  ASSERT_EQ(0xe000000000000000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) { ReadEncodedValue_data8<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) { ReadEncodedValue_data8<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_non_zero_adjust() {
+  memory_.SetData64(0, 0xe000000000000000ULL);
+
+  uint64_t value = 0;
+  dwarf_mem_->set_pc_offset(0x2000);
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x14, &value));
+  ASSERT_EQ(0xe000000000002000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_non_zero_adjust_uint32_t) {
+  ReadEncodedValue_non_zero_adjust<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_non_zero_adjust_uint64_t) {
+  ReadEncodedValue_non_zero_adjust<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_overflow() {
+  memory_.SetData64(0, 0);
+
+  uint64_t value = 0;
+  dwarf_mem_->set_cur_offset(UINT64_MAX);
+  ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<AddressType>(0x50, &value));
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_overflow_uint32_t) {
+  ReadEncodedValue_overflow<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_overflow_uint64_t) {
+  ReadEncodedValue_overflow<uint64_t>();
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_absptr) {
+  uint64_t value = 0x1234;
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x00, &value));
+  ASSERT_EQ(0x1234U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_pcrel) {
+  uint64_t value = 0x1234;
+  ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+
+  dwarf_mem_->set_pc_offset(0x2000);
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+  ASSERT_EQ(0x3234U, value);
+
+  dwarf_mem_->set_pc_offset(static_cast<uint64_t>(-4));
+  value = 0x1234;
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+  ASSERT_EQ(0x1230U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_textrel) {
+  uint64_t value = 0x8234;
+  ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+
+  dwarf_mem_->set_text_offset(0x1000);
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+  ASSERT_EQ(0x9234U, value);
+
+  dwarf_mem_->set_text_offset(static_cast<uint64_t>(-16));
+  value = 0x8234;
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+  ASSERT_EQ(0x8224U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_datarel) {
+  uint64_t value = 0xb234;
+  ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+
+  dwarf_mem_->set_data_offset(0x1200);
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+  ASSERT_EQ(0xc434U, value);
+
+  dwarf_mem_->set_data_offset(static_cast<uint64_t>(-256));
+  value = 0xb234;
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+  ASSERT_EQ(0xb134U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_funcrel) {
+  uint64_t value = 0x15234;
+  ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+
+  dwarf_mem_->set_func_offset(0x60000);
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+  ASSERT_EQ(0x75234U, value);
+
+  dwarf_mem_->set_func_offset(static_cast<uint64_t>(-4096));
+  value = 0x15234;
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+  ASSERT_EQ(0x14234U, value);
+}
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
new file mode 100644
index 0000000..d18aad0
--- /dev/null
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "DwarfError.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "Log.h"
+#include "Regs.h"
+
+#include "MemoryFake.h"
+
+template <typename TypeParam>
+class DwarfOpLogTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    op_memory_.Clear();
+    regular_memory_.Clear();
+    mem_.reset(new DwarfMemory(&op_memory_));
+    op_.reset(new DwarfOp<TypeParam>(mem_.get(), &regular_memory_));
+  }
+
+  MemoryFake op_memory_;
+  MemoryFake regular_memory_;
+
+  std::unique_ptr<DwarfMemory> mem_;
+  std::unique_ptr<DwarfOp<TypeParam>> op_;
+};
+TYPED_TEST_CASE_P(DwarfOpLogTest);
+
+TYPED_TEST_P(DwarfOpLogTest, multiple_ops) {
+  // Multi operation opcodes.
+  std::vector<uint8_t> opcode_buffer = {
+      0x0a, 0x20, 0x10, 0x08, 0x03, 0x12, 0x27,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  std::vector<std::string> lines;
+  this->op_->GetLogInfo(0, opcode_buffer.size(), &lines);
+  std::vector<std::string> expected{
+      "DW_OP_const2u 4128", "Raw Data: 0x0a 0x20 0x10", "DW_OP_const1u 3", "Raw Data: 0x08 0x03",
+      "DW_OP_dup",          "Raw Data: 0x12",           "DW_OP_xor",       "Raw Data: 0x27"};
+  ASSERT_EQ(expected, lines);
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfOpLogTest, multiple_ops);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
new file mode 100644
index 0000000..20c488a
--- /dev/null
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -0,0 +1,1581 @@
+/*
+ * 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 <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "DwarfError.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "Log.h"
+
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+template <typename TypeParam>
+class DwarfOpTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    op_memory_.Clear();
+    regular_memory_.Clear();
+    mem_.reset(new DwarfMemory(&op_memory_));
+    op_.reset(new DwarfOp<TypeParam>(mem_.get(), &regular_memory_));
+  }
+
+  MemoryFake op_memory_;
+  MemoryFake regular_memory_;
+
+  std::unique_ptr<DwarfMemory> mem_;
+  std::unique_ptr<DwarfOp<TypeParam>> op_;
+};
+TYPED_TEST_CASE_P(DwarfOpTest);
+
+TYPED_TEST_P(DwarfOpTest, decode) {
+  // Memory error.
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+
+  // No error.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
+  this->mem_->set_cur_offset(0);
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error());
+  ASSERT_EQ(0x96U, this->op_->cur_op());
+  ASSERT_EQ(1U, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, eval) {
+  // Memory error.
+  ASSERT_FALSE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+
+  // Register set.
+  // Do this first, to verify that subsequent calls reset the value.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x50});
+  ASSERT_TRUE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->is_register());
+  ASSERT_EQ(1U, this->mem_->cur_offset());
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  // Multi operation opcodes.
+  std::vector<uint8_t> opcode_buffer = {
+      0x08, 0x04, 0x08, 0x03, 0x08, 0x02, 0x08, 0x01,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_TRUE(this->op_->Eval(0, 8, DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error());
+  ASSERT_FALSE(this->op_->is_register());
+  ASSERT_EQ(8U, this->mem_->cur_offset());
+  ASSERT_EQ(4U, this->op_->StackSize());
+  ASSERT_EQ(1U, this->op_->StackAt(0));
+  ASSERT_EQ(2U, this->op_->StackAt(1));
+  ASSERT_EQ(3U, this->op_->StackAt(2));
+  ASSERT_EQ(4U, this->op_->StackAt(3));
+
+  // Infinite loop.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x2f, 0xfd, 0xff});
+  ASSERT_FALSE(this->op_->Eval(0, 4, DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_TOO_MANY_ITERATIONS, this->op_->last_error());
+  ASSERT_FALSE(this->op_->is_register());
+  ASSERT_EQ(0U, this->op_->StackSize());
+}
+
+TYPED_TEST_P(DwarfOpTest, illegal_opcode) {
+  // Fill the buffer with all of the illegal opcodes.
+  std::vector<uint8_t> opcode_buffer = {0x00, 0x01, 0x02, 0x04, 0x05, 0x07};
+  for (size_t opcode = 0xa0; opcode < 256; opcode++) {
+    opcode_buffer.push_back(opcode);
+  }
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  for (size_t i = 0; i < opcode_buffer.size(); i++) {
+    ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+    ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, illegal_in_version3) {
+  std::vector<uint8_t> opcode_buffer = {0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d};
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  for (size_t i = 0; i < opcode_buffer.size(); i++) {
+    ASSERT_FALSE(this->op_->Decode(2));
+    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+    ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, illegal_in_version4) {
+  std::vector<uint8_t> opcode_buffer = {0x9e, 0x9f};
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  for (size_t i = 0; i < opcode_buffer.size(); i++) {
+    ASSERT_FALSE(this->op_->Decode(3));
+    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+    ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, not_implemented) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Push values so that any not implemented ops will return the right error.
+      0x08, 0x03, 0x08, 0x02, 0x08, 0x01,
+      // xderef
+      0x18,
+      // fbreg
+      0x91, 0x01,
+      // piece
+      0x93, 0x01,
+      // xderef_size
+      0x95, 0x01,
+      // push_object_address
+      0x97,
+      // call2
+      0x98, 0x01, 0x02,
+      // call4
+      0x99, 0x01, 0x02, 0x03, 0x04,
+      // call_ref
+      0x9a,
+      // form_tls_address
+      0x9b,
+      // call_frame_cfa
+      0x9c,
+      // bit_piece
+      0x9d, 0x01, 0x01,
+      // implicit_value
+      0x9e, 0x01,
+      // stack_value
+      0x9f,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  // Push the stack values.
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+
+  while (this->mem_->cur_offset() < opcode_buffer.size()) {
+    ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+    ASSERT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->op_->last_error());
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_addr) {
+  std::vector<uint8_t> opcode_buffer = {0x03, 0x12, 0x23, 0x34, 0x45};
+  if (sizeof(TypeParam) == 8) {
+    opcode_buffer.push_back(0x56);
+    opcode_buffer.push_back(0x67);
+    opcode_buffer.push_back(0x78);
+    opcode_buffer.push_back(0x89);
+  }
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x03, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x45342312U, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0x8978675645342312UL, this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_deref) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Try a dereference with nothing on the stack.
+      0x06,
+      // Add an address, then dereference.
+      0x0a, 0x10, 0x20, 0x06,
+      // Now do another dereference that should fail in memory.
+      0x06,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+  TypeParam value = 0x12345678;
+  this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x06, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(value, this->op_->StackAt(0));
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_deref_size) {
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x94});
+  TypeParam value = 0x12345678;
+  this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  // Read all byte sizes up to the sizeof the type.
+  for (size_t i = 1; i < sizeof(TypeParam); i++) {
+    this->op_memory_.SetMemory(
+        0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, static_cast<uint8_t>(i)});
+    ASSERT_TRUE(this->op_->Eval(0, 5, DWARF_VERSION_MAX)) << "Failed at size " << i;
+    ASSERT_EQ(1U, this->op_->StackSize()) << "Failed at size " << i;
+    ASSERT_EQ(0x94, this->op_->cur_op()) << "Failed at size " << i;
+    TypeParam expected_value = 0;
+    memcpy(&expected_value, &value, i);
+    ASSERT_EQ(expected_value, this->op_->StackAt(0)) << "Failed at size " << i;
+  }
+
+  // Zero byte read.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, 0x00});
+  ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+
+  // Read too many bytes.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, sizeof(TypeParam) + 1});
+  ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+
+  // Force bad memory read.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x40, 0x94, 0x01});
+  ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, const_unsigned) {
+  std::vector<uint8_t> opcode_buffer = {
+      // const1u
+      0x08, 0x12, 0x08, 0xff,
+      // const2u
+      0x0a, 0x45, 0x12, 0x0a, 0x00, 0xff,
+      // const4u
+      0x0c, 0x12, 0x23, 0x34, 0x45, 0x0c, 0x03, 0x02, 0x01, 0xff,
+      // const8u
+      0x0e, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x0e, 0x87, 0x98, 0xa9, 0xba, 0xcb,
+      0xdc, 0xed, 0xfe,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  // const1u
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x08, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x12U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x08, this->op_->cur_op());
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_EQ(0xffU, this->op_->StackAt(0));
+
+  // const2u
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x0a, this->op_->cur_op());
+  ASSERT_EQ(3U, this->op_->StackSize());
+  ASSERT_EQ(0x1245U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x0a, this->op_->cur_op());
+  ASSERT_EQ(4U, this->op_->StackSize());
+  ASSERT_EQ(0xff00U, this->op_->StackAt(0));
+
+  // const4u
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x0c, this->op_->cur_op());
+  ASSERT_EQ(5U, this->op_->StackSize());
+  ASSERT_EQ(0x45342312U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x0c, this->op_->cur_op());
+  ASSERT_EQ(6U, this->op_->StackSize());
+  ASSERT_EQ(0xff010203U, this->op_->StackAt(0));
+
+  // const8u
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x0e, this->op_->cur_op());
+  ASSERT_EQ(7U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x05060708U, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0x0102030405060708ULL, this->op_->StackAt(0));
+  }
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x0e, this->op_->cur_op());
+  ASSERT_EQ(8U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0xbaa99887UL, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0xfeeddccbbaa99887ULL, this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_signed) {
+  std::vector<uint8_t> opcode_buffer = {
+      // const1s
+      0x09, 0x12, 0x09, 0xff,
+      // const2s
+      0x0b, 0x21, 0x32, 0x0b, 0x08, 0xff,
+      // const4s
+      0x0d, 0x45, 0x34, 0x23, 0x12, 0x0d, 0x01, 0x02, 0x03, 0xff,
+      // const8s
+      0x0f, 0x89, 0x78, 0x67, 0x56, 0x45, 0x34, 0x23, 0x12, 0x0f, 0x04, 0x03, 0x02, 0x01, 0xef,
+      0xef, 0xef, 0xff,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  // const1s
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x09, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x12U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x09, this->op_->cur_op());
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
+
+  // const2s
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x0b, this->op_->cur_op());
+  ASSERT_EQ(3U, this->op_->StackSize());
+  ASSERT_EQ(0x3221U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x0b, this->op_->cur_op());
+  ASSERT_EQ(4U, this->op_->StackSize());
+  ASSERT_EQ(static_cast<TypeParam>(-248), this->op_->StackAt(0));
+
+  // const4s
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x0d, this->op_->cur_op());
+  ASSERT_EQ(5U, this->op_->StackSize());
+  ASSERT_EQ(0x12233445U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x0d, this->op_->cur_op());
+  ASSERT_EQ(6U, this->op_->StackSize());
+  ASSERT_EQ(static_cast<TypeParam>(-16580095), this->op_->StackAt(0));
+
+  // const8s
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x0f, this->op_->cur_op());
+  ASSERT_EQ(7U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x56677889ULL, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0x1223344556677889ULL, this->op_->StackAt(0));
+  }
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x0f, this->op_->cur_op());
+  ASSERT_EQ(8U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x01020304U, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(static_cast<TypeParam>(-4521264810949884LL), this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_uleb) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Single byte ULEB128
+      0x10, 0x22, 0x10, 0x7f,
+      // Multi byte ULEB128
+      0x10, 0xa2, 0x22, 0x10, 0xa2, 0x74, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+      0x09, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x79,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  // Single byte ULEB128
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x10, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x22U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x10, this->op_->cur_op());
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_EQ(0x7fU, this->op_->StackAt(0));
+
+  // Multi byte ULEB128
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x10, this->op_->cur_op());
+  ASSERT_EQ(3U, this->op_->StackSize());
+  ASSERT_EQ(0x1122U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x10, this->op_->cur_op());
+  ASSERT_EQ(4U, this->op_->StackSize());
+  ASSERT_EQ(0x3a22U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x10, this->op_->cur_op());
+  ASSERT_EQ(5U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
+  }
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x10, this->op_->cur_op());
+  ASSERT_EQ(6U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0x79101c305080c101ULL, this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_sleb) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Single byte SLEB128
+      0x11, 0x22, 0x11, 0x7f,
+      // Multi byte SLEB128
+      0x11, 0xa2, 0x22, 0x11, 0xa2, 0x74, 0x11, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+      0x09, 0x11,
+  };
+  if (sizeof(TypeParam) == 4) {
+    opcode_buffer.push_back(0xb8);
+    opcode_buffer.push_back(0xd3);
+    opcode_buffer.push_back(0x63);
+  } else {
+    opcode_buffer.push_back(0x81);
+    opcode_buffer.push_back(0x82);
+    opcode_buffer.push_back(0x83);
+    opcode_buffer.push_back(0x84);
+    opcode_buffer.push_back(0x85);
+    opcode_buffer.push_back(0x86);
+    opcode_buffer.push_back(0x87);
+    opcode_buffer.push_back(0x88);
+    opcode_buffer.push_back(0x79);
+  }
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  // Single byte SLEB128
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x11, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x22U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x11, this->op_->cur_op());
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
+
+  // Multi byte SLEB128
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x11, this->op_->cur_op());
+  ASSERT_EQ(3U, this->op_->StackSize());
+  ASSERT_EQ(0x1122U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x11, this->op_->cur_op());
+  ASSERT_EQ(4U, this->op_->StackSize());
+  ASSERT_EQ(static_cast<TypeParam>(-1502), this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x11, this->op_->cur_op());
+  ASSERT_EQ(5U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
+  }
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x11, this->op_->cur_op());
+  ASSERT_EQ(6U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(static_cast<TypeParam>(-464456), this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(static_cast<TypeParam>(-499868564803501823LL), this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_dup) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Should fail since nothing is on the stack.
+      0x12,
+      // Push on a value and dup.
+      0x08, 0x15, 0x12,
+      // Do it again.
+      0x08, 0x23, 0x12,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x12, this->op_->cur_op());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x12, this->op_->cur_op());
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_EQ(0x15U, this->op_->StackAt(0));
+  ASSERT_EQ(0x15U, this->op_->StackAt(1));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(3U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x12, this->op_->cur_op());
+  ASSERT_EQ(4U, this->op_->StackSize());
+  ASSERT_EQ(0x23U, this->op_->StackAt(0));
+  ASSERT_EQ(0x23U, this->op_->StackAt(1));
+  ASSERT_EQ(0x15U, this->op_->StackAt(2));
+  ASSERT_EQ(0x15U, this->op_->StackAt(3));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_drop) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Push a couple of values.
+      0x08, 0x10, 0x08, 0x20,
+      // Drop the values.
+      0x13, 0x13,
+      // Attempt to drop empty stack.
+      0x13,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x13, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x13, this->op_->cur_op());
+  ASSERT_EQ(0U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x13, this->op_->cur_op());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_over) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Push a couple of values.
+      0x08, 0x1a, 0x08, 0xed,
+      // Copy a value.
+      0x14,
+      // Remove all but one element.
+      0x13, 0x13,
+      // Provoke a failure with this opcode.
+      0x14,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x14, this->op_->cur_op());
+  ASSERT_EQ(3U, this->op_->StackSize());
+  ASSERT_EQ(0x1aU, this->op_->StackAt(0));
+  ASSERT_EQ(0xedU, this->op_->StackAt(1));
+  ASSERT_EQ(0x1aU, this->op_->StackAt(2));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x14, this->op_->cur_op());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_pick) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Push a few values.
+      0x08, 0x1a, 0x08, 0xed, 0x08, 0x34,
+      // Copy the value at offset 2.
+      0x15, 0x01,
+      // Copy the last value in the stack.
+      0x15, 0x03,
+      // Choose an invalid index.
+      0x15, 0x10,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(3U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x15, this->op_->cur_op());
+  ASSERT_EQ(4U, this->op_->StackSize());
+  ASSERT_EQ(0xedU, this->op_->StackAt(0));
+  ASSERT_EQ(0x34U, this->op_->StackAt(1));
+  ASSERT_EQ(0xedU, this->op_->StackAt(2));
+  ASSERT_EQ(0x1aU, this->op_->StackAt(3));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x15, this->op_->cur_op());
+  ASSERT_EQ(5U, this->op_->StackSize());
+  ASSERT_EQ(0x1aU, this->op_->StackAt(0));
+  ASSERT_EQ(0xedU, this->op_->StackAt(1));
+  ASSERT_EQ(0x34U, this->op_->StackAt(2));
+  ASSERT_EQ(0xedU, this->op_->StackAt(3));
+  ASSERT_EQ(0x1aU, this->op_->StackAt(4));
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x15, this->op_->cur_op());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_swap) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Push a couple of values.
+      0x08, 0x26, 0x08, 0xab,
+      // Swap values.
+      0x16,
+      // Pop a value to cause a failure.
+      0x13, 0x16,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_EQ(0xabU, this->op_->StackAt(0));
+  ASSERT_EQ(0x26U, this->op_->StackAt(1));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x16, this->op_->cur_op());
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_EQ(0x26U, this->op_->StackAt(0));
+  ASSERT_EQ(0xabU, this->op_->StackAt(1));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x16, this->op_->cur_op());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_rot) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Rotate that should cause a failure.
+      0x17, 0x08, 0x10,
+      // Only 1 value on stack, should fail.
+      0x17, 0x08, 0x20,
+      // Only 2 values on stack, should fail.
+      0x17, 0x08, 0x30,
+      // Should rotate properly.
+      0x17,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(3U, this->op_->StackSize());
+  ASSERT_EQ(0x30U, this->op_->StackAt(0));
+  ASSERT_EQ(0x20U, this->op_->StackAt(1));
+  ASSERT_EQ(0x10U, this->op_->StackAt(2));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x17, this->op_->cur_op());
+  ASSERT_EQ(3U, this->op_->StackSize());
+  ASSERT_EQ(0x20U, this->op_->StackAt(0));
+  ASSERT_EQ(0x10U, this->op_->StackAt(1));
+  ASSERT_EQ(0x30U, this->op_->StackAt(2));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_abs) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Abs that should fail.
+      0x19,
+      // A value that is already positive.
+      0x08, 0x10, 0x19,
+      // A value that is negative.
+      0x11, 0x7f, 0x19,
+      // A value that is large and negative.
+      0x11, 0x81, 0x80, 0x80, 0x80,
+  };
+  if (sizeof(TypeParam) == 4) {
+    opcode_buffer.push_back(0x08);
+  } else {
+    opcode_buffer.push_back(0x80);
+    opcode_buffer.push_back(0x80);
+    opcode_buffer.push_back(0x01);
+  }
+  opcode_buffer.push_back(0x19);
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x19, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x19, this->op_->cur_op());
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_EQ(0x1U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(3U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x19, this->op_->cur_op());
+  ASSERT_EQ(3U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(2147483647U, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(4398046511105UL, this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_and) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x1b,
+      // Push a single value.
+      0x08, 0x20,
+      // One element stack, and op will fail.
+      0x1b,
+      // Push another value.
+      0x08, 0x02, 0x1b,
+      // Push on two negative values.
+      0x11, 0x7c, 0x11, 0x7f, 0x1b,
+      // Push one negative, one positive.
+      0x11, 0x10, 0x11, 0x7c, 0x1b,
+      // Divide by zero.
+      0x11, 0x10, 0x11, 0x00, 0x1b,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  // Two positive values.
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x1b, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+  // Two negative values.
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(3U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x1b, this->op_->cur_op());
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_EQ(0x04U, this->op_->StackAt(0));
+
+  // One negative value, one positive value.
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(3U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(4U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x1b, this->op_->cur_op());
+  ASSERT_EQ(3U, this->op_->StackSize());
+  ASSERT_EQ(static_cast<TypeParam>(-4), this->op_->StackAt(0));
+
+  // Divide by zero.
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(4U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(5U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_div) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x1a,
+      // Push a single value.
+      0x08, 0x48,
+      // One element stack, and op will fail.
+      0x1a,
+      // Push another value.
+      0x08, 0xf0, 0x1a,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x1a, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x40U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_minus) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x1c,
+      // Push a single value.
+      0x08, 0x48,
+      // One element stack, and op will fail.
+      0x1c,
+      // Push another value.
+      0x08, 0x04, 0x1c,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x1c, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x44U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_mod) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x1d,
+      // Push a single value.
+      0x08, 0x47,
+      // One element stack, and op will fail.
+      0x1d,
+      // Push another value.
+      0x08, 0x04, 0x1d,
+      // Try a mod of zero.
+      0x08, 0x01, 0x08, 0x00, 0x1d,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x1d, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x03U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(3U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_mul) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x1e,
+      // Push a single value.
+      0x08, 0x48,
+      // One element stack, and op will fail.
+      0x1e,
+      // Push another value.
+      0x08, 0x04, 0x1e,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x1e, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x120U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_neg) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x1f,
+      // Push a single value.
+      0x08, 0x48, 0x1f,
+      // Push a negative value.
+      0x11, 0x7f, 0x1f,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x1f, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(static_cast<TypeParam>(-72), this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x1f, this->op_->cur_op());
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_EQ(0x01U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_not) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x20,
+      // Push a single value.
+      0x08, 0x4, 0x20,
+      // Push a negative value.
+      0x11, 0x7c, 0x20,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x20, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(static_cast<TypeParam>(-5), this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x20, this->op_->cur_op());
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_EQ(0x03U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_or) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x21,
+      // Push a single value.
+      0x08, 0x48,
+      // One element stack, and op will fail.
+      0x21,
+      // Push another value.
+      0x08, 0xf4, 0x21,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x21, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0xfcU, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_plus) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x22,
+      // Push a single value.
+      0x08, 0xff,
+      // One element stack, and op will fail.
+      0x22,
+      // Push another value.
+      0x08, 0xf2, 0x22,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x22, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x1f1U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_plus_uconst) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x23,
+      // Push a single value.
+      0x08, 0x50, 0x23, 0x80, 0x51,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x23, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x28d0U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shl) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x24,
+      // Push a single value.
+      0x08, 0x67,
+      // One element stack, and op will fail.
+      0x24,
+      // Push another value.
+      0x08, 0x03, 0x24,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x24, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x338U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shr) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x25,
+      // Push a single value.
+      0x11, 0x70,
+      // One element stack, and op will fail.
+      0x25,
+      // Push another value.
+      0x08, 0x03, 0x25,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x25, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x1ffffffeU, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0x1ffffffffffffffeULL, this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shra) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x26,
+      // Push a single value.
+      0x11, 0x70,
+      // One element stack, and op will fail.
+      0x26,
+      // Push another value.
+      0x08, 0x03, 0x26,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x26, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(static_cast<TypeParam>(-2), this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_xor) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x27,
+      // Push a single value.
+      0x08, 0x11,
+      // One element stack, and op will fail.
+      0x27,
+      // Push another value.
+      0x08, 0x41, 0x27,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x27, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x50U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_bra) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x28,
+      // Push on a non-zero value with a positive branch.
+      0x08, 0x11, 0x28, 0x02, 0x01,
+      // Push on a zero value with a positive branch.
+      0x08, 0x00, 0x28, 0x05, 0x00,
+      // Push on a non-zero value with a negative branch.
+      0x08, 0x11, 0x28, 0xfc, 0xff,
+      // Push on a zero value with a negative branch.
+      0x08, 0x00, 0x28, 0xf0, 0xff,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  // Push on a non-zero value with a positive branch.
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  uint64_t offset = this->mem_->cur_offset() + 3;
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x28, this->op_->cur_op());
+  ASSERT_EQ(0U, this->op_->StackSize());
+  ASSERT_EQ(offset + 0x102, this->mem_->cur_offset());
+
+  // Push on a zero value with a positive branch.
+  this->mem_->set_cur_offset(offset);
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  offset = this->mem_->cur_offset() + 3;
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x28, this->op_->cur_op());
+  ASSERT_EQ(0U, this->op_->StackSize());
+  ASSERT_EQ(offset - 5, this->mem_->cur_offset());
+
+  // Push on a non-zero value with a negative branch.
+  this->mem_->set_cur_offset(offset);
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  offset = this->mem_->cur_offset() + 3;
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x28, this->op_->cur_op());
+  ASSERT_EQ(0U, this->op_->StackSize());
+  ASSERT_EQ(offset - 4, this->mem_->cur_offset());
+
+  // Push on a zero value with a negative branch.
+  this->mem_->set_cur_offset(offset);
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  offset = this->mem_->cur_offset() + 3;
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x28, this->op_->cur_op());
+  ASSERT_EQ(0U, this->op_->StackSize());
+  ASSERT_EQ(offset + 16, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, compare_opcode_stack_error) {
+  // All of the ops require two stack elements. Loop through all of these
+  // ops with potential errors.
+  std::vector<uint8_t> opcode_buffer = {
+      0xff,  // Place holder for compare op.
+      0x08, 0x11,
+      0xff,  // Place holder for compare op.
+  };
+
+  for (uint8_t opcode = 0x29; opcode <= 0x2e; opcode++) {
+    opcode_buffer[0] = opcode;
+    opcode_buffer[3] = opcode;
+    this->op_memory_.SetMemory(0, opcode_buffer);
+
+    ASSERT_FALSE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
+    ASSERT_EQ(opcode, this->op_->cur_op());
+    ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+    ASSERT_FALSE(this->op_->Eval(1, 4, DWARF_VERSION_MAX));
+    ASSERT_EQ(opcode, this->op_->cur_op());
+    ASSERT_EQ(1U, this->op_->StackSize());
+    ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, compare_opcodes) {
+  // Have three different checks for each compare op:
+  //   - Both values the same.
+  //   - The first value larger than the second.
+  //   - The second value larger than the first.
+  std::vector<uint8_t> opcode_buffer = {
+      // Values the same.
+      0x08, 0x11, 0x08, 0x11,
+      0xff,  // Placeholder.
+      // First value larger.
+      0x08, 0x12, 0x08, 0x10,
+      0xff,  // Placeholder.
+      // Second value larger.
+      0x08, 0x10, 0x08, 0x12,
+      0xff,  // Placeholder.
+  };
+
+  // Opcode followed by the expected values on the stack.
+  std::vector<uint8_t> expected = {
+      0x29, 1, 0, 0,  // eq
+      0x2a, 1, 1, 0,  // ge
+      0x2b, 0, 1, 0,  // gt
+      0x2c, 1, 0, 1,  // le
+      0x2d, 0, 0, 1,  // lt
+      0x2e, 0, 1, 1,  // ne
+  };
+  for (size_t i = 0; i < expected.size(); i += 4) {
+    opcode_buffer[4] = expected[i];
+    opcode_buffer[9] = expected[i];
+    opcode_buffer[14] = expected[i];
+    this->op_memory_.SetMemory(0, opcode_buffer);
+
+    ASSERT_TRUE(this->op_->Eval(0, 15, DWARF_VERSION_MAX))
+        << "Op: 0x" << std::hex << static_cast<uint32_t>(expected[i]) << " failed";
+
+    ASSERT_EQ(3U, this->op_->StackSize());
+    ASSERT_EQ(expected[i + 1], this->op_->StackAt(2));
+    ASSERT_EQ(expected[i + 2], this->op_->StackAt(1));
+    ASSERT_EQ(expected[i + 3], this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_skip) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Positive value.
+      0x2f, 0x10, 0x20,
+      // Negative value.
+      0x2f, 0xfd, 0xff,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  uint64_t offset = this->mem_->cur_offset() + 3;
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x2f, this->op_->cur_op());
+  ASSERT_EQ(0U, this->op_->StackSize());
+  ASSERT_EQ(offset + 0x2010, this->mem_->cur_offset());
+
+  this->mem_->set_cur_offset(offset);
+  offset = this->mem_->cur_offset() + 3;
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x2f, this->op_->cur_op());
+  ASSERT_EQ(0U, this->op_->StackSize());
+  ASSERT_EQ(offset - 3, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_lit) {
+  std::vector<uint8_t> opcode_buffer;
+
+  // Verify every lit opcode.
+  for (uint8_t op = 0x30; op <= 0x4f; op++) {
+    opcode_buffer.push_back(op);
+  }
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  for (size_t i = 0; i < opcode_buffer.size(); i++) {
+    uint32_t op = opcode_buffer[i];
+    ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op;
+    ASSERT_EQ(op, this->op_->cur_op());
+    ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+    ASSERT_EQ(op - 0x30U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_reg) {
+  std::vector<uint8_t> opcode_buffer;
+
+  // Verify every reg opcode.
+  for (uint8_t op = 0x50; op <= 0x6f; op++) {
+    opcode_buffer.push_back(op);
+  }
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  for (size_t i = 0; i < opcode_buffer.size(); i++) {
+    uint32_t op = opcode_buffer[i];
+    ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op;
+    ASSERT_EQ(op, this->op_->cur_op());
+    ASSERT_TRUE(this->op_->is_register()) << "Failed op: 0x" << std::hex << op;
+    ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+    ASSERT_EQ(op - 0x50U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_regx) {
+  std::vector<uint8_t> opcode_buffer = {
+      0x90, 0x02, 0x90, 0x80, 0x15,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+  ASSERT_EQ(0x90, this->op_->cur_op());
+  ASSERT_TRUE(this->op_->is_register());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x02U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Eval(2, 5, DWARF_VERSION_MAX));
+  ASSERT_EQ(0x90, this->op_->cur_op());
+  ASSERT_TRUE(this->op_->is_register());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0xa80U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_breg) {
+  std::vector<uint8_t> opcode_buffer;
+
+  // Verify every reg opcode.
+  for (uint8_t op = 0x70; op <= 0x8f; op++) {
+    // Positive value added to register.
+    opcode_buffer.push_back(op);
+    opcode_buffer.push_back(0x12);
+    // Negative value added to register.
+    opcode_buffer.push_back(op);
+    opcode_buffer.push_back(0x7e);
+  }
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  RegsFake<TypeParam> regs(32, 10);
+  for (size_t i = 0; i < 32; i++) {
+    regs[i] = i + 10;
+  }
+  this->op_->set_regs(&regs);
+
+  uint64_t offset = 0;
+  for (uint32_t op = 0x70; op <= 0x8f; op++) {
+    // Positive value added to register.
+    ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x"
+                                                                        << std::hex << op;
+    ASSERT_EQ(op, this->op_->cur_op());
+    ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+    ASSERT_EQ(op - 0x70 + 10 + 0x12, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+    offset += 2;
+
+    // Negative value added to register.
+    ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x"
+                                                                        << std::hex << op;
+    ASSERT_EQ(op, this->op_->cur_op());
+    ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+    ASSERT_EQ(op - 0x70 + 10 - 2, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+    offset += 2;
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_breg_invalid_register) {
+  std::vector<uint8_t> opcode_buffer = {
+      0x7f, 0x12, 0x80, 0x12,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  RegsFake<TypeParam> regs(16, 10);
+  for (size_t i = 0; i < 16; i++) {
+    regs[i] = i + 10;
+  }
+  this->op_->set_regs(&regs);
+
+  // Should pass since this references the last regsister.
+  ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+  ASSERT_EQ(0x7fU, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x2bU, this->op_->StackAt(0));
+
+  // Should fail since this references a non-existent register.
+  ASSERT_FALSE(this->op_->Eval(2, 4, DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_bregx) {
+  std::vector<uint8_t> opcode_buffer = {// Positive value added to register.
+                                        0x92, 0x05, 0x20,
+                                        // Negative value added to register.
+                                        0x92, 0x06, 0x80, 0x7e,
+                                        // Illegal register.
+                                        0x92, 0x80, 0x15, 0x80, 0x02};
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  RegsFake<TypeParam> regs(10, 10);
+  regs[5] = 0x45;
+  regs[6] = 0x190;
+  this->op_->set_regs(&regs);
+
+  ASSERT_TRUE(this->op_->Eval(0, 3, DWARF_VERSION_MAX));
+  ASSERT_EQ(0x92, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x65U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Eval(3, 7, DWARF_VERSION_MAX));
+  ASSERT_EQ(0x92, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x90U, this->op_->StackAt(0));
+
+  ASSERT_FALSE(this->op_->Eval(7, 12, DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_nop) {
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x96, this->op_->cur_op());
+  ASSERT_EQ(0U, this->op_->StackSize());
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfOpTest, decode, eval, illegal_opcode, illegal_in_version3,
+                           illegal_in_version4, not_implemented, op_addr, op_deref, op_deref_size,
+                           const_unsigned, const_signed, const_uleb, const_sleb, op_dup, op_drop,
+                           op_over, op_pick, op_swap, op_rot, op_abs, op_and, op_div, op_minus,
+                           op_mod, op_mul, op_neg, op_not, op_or, op_plus, op_plus_uconst, op_shl,
+                           op_shr, op_shra, op_xor, op_bra, compare_opcode_stack_error,
+                           compare_opcodes, op_skip, op_lit, op_reg, op_regx, op_breg,
+                           op_breg_invalid_register, op_bregx, op_nop);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes);
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
new file mode 100644
index 0000000..71b114a
--- /dev/null
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -0,0 +1,832 @@
+/*
+ * 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 "DwarfSection.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+template <typename TypeParam>
+class MockDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
+ public:
+  MockDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
+  virtual ~MockDwarfSectionImpl() = default;
+
+  MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+
+  MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
+
+  MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
+
+  MOCK_METHOD1(IsCie32, bool(uint32_t));
+
+  MOCK_METHOD1(IsCie64, bool(uint64_t));
+
+  MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
+
+  MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
+
+  MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
+
+  void TestSetCachedCieEntry(uint64_t offset, const DwarfCie& cie) {
+    this->cie_entries_[offset] = cie;
+  }
+  void TestClearCachedCieEntry() { this->cie_entries_.clear(); }
+
+  void TestSetCachedFdeEntry(uint64_t offset, const DwarfFde& fde) {
+    this->fde_entries_[offset] = fde;
+  }
+  void TestClearCachedFdeEntry() { this->fde_entries_.clear(); }
+
+  void TestSetCachedCieLocRegs(uint64_t offset, const dwarf_loc_regs_t& loc_regs) {
+    this->cie_loc_regs_[offset] = loc_regs;
+  }
+  void TestClearCachedCieLocRegs() { this->cie_loc_regs_.clear(); }
+
+  void TestClearError() { this->last_error_ = DWARF_ERROR_NONE; }
+};
+
+template <typename TypeParam>
+class DwarfSectionImplTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_.Clear();
+    section_ = new MockDwarfSectionImpl<TypeParam>(&memory_);
+    ResetLogs();
+  }
+
+  void TearDown() override { delete section_; }
+
+  MemoryFake memory_;
+  MockDwarfSectionImpl<TypeParam>* section_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfSectionImplTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x96, 0x96, 0x96});
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+  TypeParam cfa_value = 0x12345;
+  this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x12345U, regs.sp());
+  EXPECT_EQ(0x20U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x80000000U, regs.sp());
+  EXPECT_EQ(0x20U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x50, 0x96, 0x96});
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) {
+  DwarfCie cie{.return_address_register = 60};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_CFA_NOT_DEFINED, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+
+  this->section_->TestClearError();
+  loc_regs.erase(CFA_REG);
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_INVALID, {0, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+
+  this->section_->TestClearError();
+  loc_regs.erase(CFA_REG);
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_OFFSET, {0, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+
+  this->section_->TestClearError();
+  loc_regs.erase(CFA_REG);
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {9, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x20U, regs.pc());
+  EXPECT_EQ(0x2000U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_from_value) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[6] = 0x4000;
+  regs[9] = 0x3000;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {6, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x20U, regs.pc());
+  EXPECT_EQ(0x4000U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 0}};
+  loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {10, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  if (sizeof(TypeParam) == sizeof(uint64_t)) {
+    this->memory_.SetData64(0x2150, 0x12345678abcdef00ULL);
+  } else {
+    this->memory_.SetData32(0x2150, 0x12345678);
+  }
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[3] = 0x234;
+  regs[5] = 0x10;
+  regs[8] = 0x2100;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[1] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0x100, 0}};
+  loc_regs[2] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x50, 0}};
+  loc_regs[3] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x10U, regs.pc());
+  EXPECT_EQ(0x2100U, regs.sp());
+  EXPECT_EQ(0x2200U, regs[1]);
+  EXPECT_EQ(0x234U, regs[3]);
+  if (sizeof(TypeParam) == sizeof(uint64_t)) {
+    EXPECT_EQ(0x12345678abcdef00ULL, regs[2]);
+  } else {
+    EXPECT_EQ(0x12345678U, regs[2]);
+  }
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[5] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0U, regs.pc());
+  EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x20U, regs.pc());
+  EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  // This should not result in any errors.
+  loc_regs[20] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x20U, regs.pc());
+  EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[8] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+  TypeParam cfa_value = 0x12345;
+  this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[5] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x3000U, regs.sp());
+  EXPECT_EQ(0x12345U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[8] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[5] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x3000U, regs.sp());
+  EXPECT_EQ(0x80000000U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_same_cfa_same_pc) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x100;
+  regs[8] = 0x2000;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x2000U, regs.sp());
+  EXPECT_EQ(0x100U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_fail_should_not_cache) {
+  ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_32_version_check) {
+  this->memory_.SetData32(0x5000, 0x100);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 0x1);
+  this->memory_.SetData8(0x5009, '\0');
+  this->memory_.SetData8(0x500a, 4);
+  this->memory_.SetData8(0x500b, 8);
+  this->memory_.SetData8(0x500c, 0x20);
+
+  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  const DwarfCie* cie = this->section_->GetCie(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ(1U, cie->augmentation_string.size());
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x500dU, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, cie->return_address_register);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->section_->last_error());
+
+  this->section_->TestClearCachedCieEntry();
+  // Set version to 0, 2, 5 and verify we fail.
+  this->memory_.SetData8(0x5008, 0x0);
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+
+  this->memory_.SetData8(0x5008, 0x2);
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+
+  this->memory_.SetData8(0x5008, 0x5);
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_negative_data_alignment_factor) {
+  this->memory_.SetData32(0x5000, 0x100);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 0x1);
+  this->memory_.SetData8(0x5009, '\0');
+  this->memory_.SetData8(0x500a, 4);
+  this->memory_.SetMemory(0x500b, std::vector<uint8_t>{0xfc, 0xff, 0xff, 0xff, 0x7f});
+  this->memory_.SetData8(0x5010, 0x20);
+
+  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  const DwarfCie* cie = this->section_->GetCie(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ(1U, cie->augmentation_string.size());
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x5011U, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(-4, cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_64_no_augment) {
+  this->memory_.SetData32(0x8000, 0xffffffff);
+  this->memory_.SetData64(0x8004, 0x200);
+  this->memory_.SetData64(0x800c, 0xffffffff);
+  this->memory_.SetData8(0x8014, 0x1);
+  this->memory_.SetData8(0x8015, '\0');
+  this->memory_.SetData8(0x8016, 4);
+  this->memory_.SetData8(0x8017, 8);
+  this->memory_.SetData8(0x8018, 0x20);
+
+  EXPECT_CALL(*this->section_, IsCie64(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  const DwarfCie* cie = this->section_->GetCie(0x8000);
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata8, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ(1U, cie->augmentation_string.size());
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x8019U, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x820cU, cie->cfa_instructions_end);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_augment) {
+  this->memory_.SetData32(0x5000, 0x100);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 0x1);
+  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
+  this->memory_.SetData8(0x500e, 4);
+  this->memory_.SetData8(0x500f, 8);
+  this->memory_.SetData8(0x5010, 0x10);
+  // Augment length.
+  this->memory_.SetData8(0x5011, 0xf);
+  // L data.
+  this->memory_.SetData8(0x5012, DW_EH_PE_textrel | DW_EH_PE_udata2);
+  // P data.
+  this->memory_.SetData8(0x5013, DW_EH_PE_udata4);
+  this->memory_.SetData32(0x5014, 0x12345678);
+  // R data.
+  this->memory_.SetData8(0x5018, DW_EH_PE_udata2);
+
+  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  const DwarfCie* cie = this->section_->GetCie(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_udata2, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_textrel | DW_EH_PE_udata2, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ(5U, cie->augmentation_string.size());
+  EXPECT_EQ('z', cie->augmentation_string[0]);
+  EXPECT_EQ('L', cie->augmentation_string[1]);
+  EXPECT_EQ('P', cie->augmentation_string[2]);
+  EXPECT_EQ('R', cie->augmentation_string[3]);
+  EXPECT_EQ('\0', cie->augmentation_string[4]);
+  EXPECT_EQ(0x12345678U, cie->personality_handler);
+  EXPECT_EQ(0x5021U, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(0x10U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_3) {
+  this->memory_.SetData32(0x5000, 0x100);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 0x3);
+  this->memory_.SetData8(0x5009, '\0');
+  this->memory_.SetData8(0x500a, 4);
+  this->memory_.SetData8(0x500b, 8);
+  this->memory_.SetMemory(0x500c, std::vector<uint8_t>{0x81, 0x03});
+
+  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  const DwarfCie* cie = this->section_->GetCie(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(3U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ(1U, cie->augmentation_string.size());
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x500eU, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(0x181U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_4) {
+  this->memory_.SetData32(0x5000, 0x100);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 0x4);
+  this->memory_.SetData8(0x5009, '\0');
+  this->memory_.SetData8(0x500a, 4);
+  this->memory_.SetData8(0x500b, 0x13);
+  this->memory_.SetData8(0x500c, 4);
+  this->memory_.SetData8(0x500d, 8);
+  this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x81, 0x03});
+
+  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  const DwarfCie* cie = this->section_->GetCie(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(4U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0x13U, cie->segment_size);
+  EXPECT_EQ(1U, cie->augmentation_string.size());
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x5010U, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(0x181U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_fail_should_not_cache) {
+  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment) {
+  this->memory_.SetData32(0x4000, 0x20);
+  this->memory_.SetData32(0x4004, 0x8000);
+  this->memory_.SetData32(0x4008, 0x5000);
+  this->memory_.SetData32(0x400c, 0x100);
+
+  EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
+  EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
+  DwarfCie cie{};
+  cie.fde_address_encoding = DW_EH_PE_udata4;
+  this->section_->TestSetCachedCieEntry(0x8000, cie);
+  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(0x4010U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x4024U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x5000U, fde->pc_start);
+  EXPECT_EQ(0x5100U, fde->pc_end);
+  EXPECT_EQ(0x8000U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment_non_zero_segment_size) {
+  this->memory_.SetData32(0x4000, 0x30);
+  this->memory_.SetData32(0x4004, 0x8000);
+  this->memory_.SetData32(0x4018, 0x5000);
+  this->memory_.SetData32(0x401c, 0x100);
+
+  EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
+  EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
+  DwarfCie cie{};
+  cie.fde_address_encoding = DW_EH_PE_udata4;
+  cie.segment_size = 0x10;
+  this->section_->TestSetCachedCieEntry(0x8000, cie);
+  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(0x4020U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x4034U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x5000U, fde->pc_start);
+  EXPECT_EQ(0x5100U, fde->pc_end);
+  EXPECT_EQ(0x8000U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_augment) {
+  this->memory_.SetData32(0x4000, 0x100);
+  this->memory_.SetData32(0x4004, 0x8000);
+  this->memory_.SetData32(0x4008, 0x5000);
+  this->memory_.SetData32(0x400c, 0x100);
+  this->memory_.SetMemory(0x4010, std::vector<uint8_t>{0x82, 0x01});
+  this->memory_.SetData16(0x4012, 0x1234);
+
+  EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
+  EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
+  DwarfCie cie{};
+  cie.fde_address_encoding = DW_EH_PE_udata4;
+  cie.augmentation_string.push_back('z');
+  cie.lsda_encoding = DW_EH_PE_udata2;
+  this->section_->TestSetCachedCieEntry(0x8000, cie);
+  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(0x4094U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x4104U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x5000U, fde->pc_start);
+  EXPECT_EQ(0x5100U, fde->pc_end);
+  EXPECT_EQ(0x8000U, fde->cie_offset);
+  EXPECT_EQ(0x1234U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_64_no_augment) {
+  this->memory_.SetData32(0x4000, 0xffffffff);
+  this->memory_.SetData64(0x4004, 0x100);
+  this->memory_.SetData64(0x400c, 0x12345678);
+  this->memory_.SetData32(0x4014, 0x5000);
+  this->memory_.SetData32(0x4018, 0x100);
+
+  EXPECT_CALL(*this->section_, IsCie64(0x12345678)).WillOnce(::testing::Return(false));
+  EXPECT_CALL(*this->section_, GetCieOffsetFromFde64(0x12345678))
+      .WillOnce(::testing::Return(0x12345678));
+  DwarfCie cie{};
+  cie.fde_address_encoding = DW_EH_PE_udata4;
+  this->section_->TestSetCachedCieEntry(0x12345678, cie);
+  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(0x401cU, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x410cU, fde->cfa_instructions_end);
+  EXPECT_EQ(0x5000U, fde->pc_start);
+  EXPECT_EQ(0x5100U, fde->pc_end);
+  EXPECT_EQ(0x12345678U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_cached) {
+  DwarfCie cie{};
+  cie.fde_address_encoding = DW_EH_PE_udata4;
+  cie.augmentation_string.push_back('z');
+  cie.lsda_encoding = DW_EH_PE_udata2;
+
+  DwarfFde fde_cached{};
+  fde_cached.cfa_instructions_offset = 0x1000;
+  fde_cached.cfa_instructions_end = 0x1100;
+  fde_cached.pc_start = 0x9000;
+  fde_cached.pc_end = 0x9400;
+  fde_cached.cie_offset = 0x30000;
+  fde_cached.cie = &cie;
+  this->section_->TestSetCachedFdeEntry(0x6000, fde_cached);
+
+  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x6000);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_EQ(&cie, fde->cie);
+  EXPECT_EQ(0x1000U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x1100U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x9000U, fde->pc_start);
+  EXPECT_EQ(0x9400U, fde->pc_end);
+  EXPECT_EQ(0x30000U, fde->cie_offset);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_not_cached) {
+  DwarfCie cie{};
+  cie.cfa_instructions_offset = 0x3000;
+  cie.cfa_instructions_end = 0x3002;
+  DwarfFde fde{};
+  fde.cie = &cie;
+  fde.cie_offset = 0x8000;
+  fde.cfa_instructions_offset = 0x6000;
+  fde.cfa_instructions_end = 0x6002;
+
+  this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x09, 0x02, 0x01});
+  this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
+
+  dwarf_loc_regs_t loc_regs;
+  ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+  ASSERT_EQ(2U, loc_regs.size());
+
+  auto entry = loc_regs.find(2);
+  ASSERT_NE(entry, loc_regs.end());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+  ASSERT_EQ(1U, entry->second.values[0]);
+
+  entry = loc_regs.find(4);
+  ASSERT_NE(entry, loc_regs.end());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+  ASSERT_EQ(3U, entry->second.values[0]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_cached) {
+  DwarfCie cie{};
+  cie.cfa_instructions_offset = 0x3000;
+  cie.cfa_instructions_end = 0x3002;
+  DwarfFde fde{};
+  fde.cie = &cie;
+  fde.cie_offset = 0x8000;
+  fde.cfa_instructions_offset = 0x6000;
+  fde.cfa_instructions_end = 0x6002;
+
+  dwarf_loc_regs_t cie_loc_regs{{6, {DWARF_LOCATION_REGISTER, {4, 0}}}};
+  this->section_->TestSetCachedCieLocRegs(0x8000, cie_loc_regs);
+  this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
+
+  dwarf_loc_regs_t loc_regs;
+  ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+  ASSERT_EQ(2U, loc_regs.size());
+
+  auto entry = loc_regs.find(6);
+  ASSERT_NE(entry, loc_regs.end());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+  ASSERT_EQ(4U, entry->second.values[0]);
+
+  entry = loc_regs.find(4);
+  ASSERT_NE(entry, loc_regs.end());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+  ASSERT_EQ(3U, entry->second.values[0]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Log) {
+  DwarfCie cie{};
+  cie.cfa_instructions_offset = 0x5000;
+  cie.cfa_instructions_end = 0x5001;
+  DwarfFde fde{};
+  fde.cie = &cie;
+  fde.cfa_instructions_offset = 0x6000;
+  fde.cfa_instructions_end = 0x6001;
+
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x00});
+  this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0xc2});
+  ASSERT_TRUE(this->section_->Log(2, 0x1000, 0x1000, &fde));
+
+  ASSERT_EQ(
+      "4 unwind     DW_CFA_nop\n"
+      "4 unwind     Raw Data: 0x00\n"
+      "4 unwind     DW_CFA_restore register(2)\n"
+      "4 unwind     Raw Data: 0xc2\n",
+      GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+REGISTER_TYPED_TEST_CASE_P(
+    DwarfSectionImplTest, Eval_cfa_expr_eval_fail, Eval_cfa_expr_no_stack,
+    Eval_cfa_expr_is_register, Eval_cfa_expr, Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa,
+    Eval_cfa_bad, Eval_cfa_register_prev, Eval_cfa_register_from_value, Eval_double_indirection,
+    Eval_invalid_register, Eval_different_reg_locations, Eval_return_address_undefined,
+    Eval_return_address, Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+    Eval_same_cfa_same_pc, GetCie_fail_should_not_cache, GetCie_32_version_check,
+    GetCie_negative_data_alignment_factor, GetCie_64_no_augment, GetCie_augment, GetCie_version_3,
+    GetCie_version_4, GetFdeFromOffset_fail_should_not_cache, GetFdeFromOffset_32_no_augment,
+    GetFdeFromOffset_32_no_augment_non_zero_segment_size, GetFdeFromOffset_32_augment,
+    GetFdeFromOffset_64_no_augment, GetFdeFromOffset_cached, GetCfaLocationInfo_cie_not_cached,
+    GetCfaLocationInfo_cie_cached, Log);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
new file mode 100644
index 0000000..20f0e2a
--- /dev/null
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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 "DwarfSection.h"
+
+#include "MemoryFake.h"
+
+class MockDwarfSection : public DwarfSection {
+ public:
+  MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
+  virtual ~MockDwarfSection() = default;
+
+  MOCK_METHOD4(Log, bool(uint8_t, uint64_t, uint64_t, const DwarfFde*));
+
+  MOCK_METHOD4(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*));
+
+  MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
+
+  MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+
+  MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
+
+  MOCK_METHOD1(GetFdeFromOffset, const DwarfFde*(uint64_t));
+
+  MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
+
+  MOCK_METHOD1(IsCie32, bool(uint32_t));
+
+  MOCK_METHOD1(IsCie64, bool(uint64_t));
+
+  MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
+
+  MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
+
+  MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
+};
+
+class DwarfSectionTest : public ::testing::Test {
+ protected:
+  MemoryFake memory_;
+};
+
+TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_from_pc) {
+  MockDwarfSection mock_section(&memory_);
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(false));
+
+  // Verify nullptr when GetFdeOffsetFromPc fails.
+  ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
+}
+
+TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_fde_pc_end) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfFde fde{};
+  fde.pc_end = 0x500;
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+  // Verify nullptr when GetFdeOffsetFromPc fails.
+  ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
+}
+
+TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_pass) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfFde fde{};
+  fde.pc_end = 0x2000;
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+  // Verify nullptr when GetFdeOffsetFromPc fails.
+  ASSERT_EQ(&fde, mock_section.GetFdeFromPc(0x1000));
+}
+
+TEST_F(DwarfSectionTest, Step_fail_fde) {
+  MockDwarfSection mock_section(&memory_);
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(false));
+
+  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+}
+
+TEST_F(DwarfSectionTest, Step_fail_cie_null) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfFde fde{};
+  fde.pc_end = 0x2000;
+  fde.cie = nullptr;
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+}
+
+TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfCie cie{};
+  DwarfFde fde{};
+  fde.pc_end = 0x2000;
+  fde.cie = &cie;
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+      .WillOnce(::testing::Return(false));
+
+  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+}
+
+TEST_F(DwarfSectionTest, Step_pass) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfCie cie{};
+  DwarfFde fde{};
+  fde.pc_end = 0x2000;
+  fde.cie = &cie;
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+      .WillOnce(::testing::Return(true));
+
+  MemoryFake process;
+  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr))
+      .WillOnce(::testing::Return(true));
+
+  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process));
+}
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
new file mode 100644
index 0000000..c846ad7
--- /dev/null
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Elf.h"
+#include "MapInfo.h"
+#include "Memory.h"
+
+class MapInfoTest : public ::testing::Test {
+ protected:
+  static void SetUpTestCase() {
+    std::vector<uint8_t> buffer(1024);
+    memcpy(buffer.data(), ELFMAG, SELFMAG);
+    for (size_t i = SELFMAG; i < buffer.size(); i++) {
+      buffer[i] = i / 256 + 1;
+    }
+    ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+    for (size_t i = 0; i < 0x100; i++) {
+      buffer[i] = i / 256 + 1;
+    }
+    memcpy(&buffer[0x100], ELFMAG, SELFMAG);
+    for (size_t i = 0x100 + SELFMAG; i < buffer.size(); i++) {
+      buffer[i] = i / 256 + 1;
+    }
+    ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
+  }
+
+  static TemporaryFile elf_;
+
+  static TemporaryFile elf_at_100_;
+};
+TemporaryFile MapInfoTest::elf_;
+TemporaryFile MapInfoTest::elf_at_100_;
+
+TEST_F(MapInfoTest, end_le_start) {
+  MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
+
+  std::unique_ptr<Memory> memory;
+  memory.reset(info.CreateMemory(getpid()));
+  ASSERT_TRUE(memory.get() == nullptr);
+
+  info.end = 0xff;
+  memory.reset(info.CreateMemory(getpid()));
+  ASSERT_TRUE(memory.get() == nullptr);
+
+  // Make sure this test is valid.
+  info.end = 0x101;
+  memory.reset(info.CreateMemory(getpid()));
+  ASSERT_FALSE(info.CreateMemory(getpid()) == nullptr);
+}
+
+// Verify that if the offset is non-zero but there is no elf at the offset,
+// that the full file is used.
+TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_full_file) {
+  MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path};
+
+  std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
+  ASSERT_TRUE(memory.get() != nullptr);
+  ASSERT_EQ(0x100U, info.elf_offset);
+
+  // Read the entire file.
+  std::vector<uint8_t> buffer(1024);
+  ASSERT_TRUE(memory->Read(0, buffer.data(), 1024));
+  ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
+  for (size_t i = SELFMAG; i < buffer.size(); i++) {
+    ASSERT_EQ(i / 256 + 1, buffer[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_FALSE(memory->Read(1024, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used.
+TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_partial_file) {
+  MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path};
+
+  std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
+  ASSERT_TRUE(memory.get() != nullptr);
+  ASSERT_EQ(0U, info.elf_offset);
+
+  // Read the valid part of the file.
+  std::vector<uint8_t> buffer(0x100);
+  ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100));
+  ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
+  for (size_t i = SELFMAG; i < buffer.size(); i++) {
+    ASSERT_EQ(2, buffer[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_FALSE(memory->Read(0x100, buffer.data(), 1));
+}
+
+// Verify that device file names will never result in Memory object creation.
+TEST_F(MapInfoTest, create_memory_check_device_maps) {
+  // Set up some memory so that a valid local memory object would
+  // be returned if the file mapping fails, but the device check is incorrect.
+  std::vector<uint8_t> buffer(1024);
+  MapInfo info;
+  info.start = reinterpret_cast<uint64_t>(buffer.data());
+  info.end = info.start + buffer.size();
+  info.offset = 0;
+  std::unique_ptr<Memory> memory;
+
+  info.flags = 0x8000;
+  info.name = "/dev/something";
+  memory.reset(info.CreateMemory(getpid()));
+  ASSERT_TRUE(memory.get() == nullptr);
+}
+
+TEST_F(MapInfoTest, create_memory_local_memory) {
+  // Set up some memory for a valid local memory object.
+  std::vector<uint8_t> buffer(1024);
+  for (size_t i = 0; i < buffer.size(); i++) {
+    buffer[i] = i % 256;
+  }
+
+  MapInfo info;
+  info.start = reinterpret_cast<uint64_t>(buffer.data());
+  info.end = info.start + buffer.size();
+  info.offset = 0;
+
+  std::unique_ptr<Memory> memory;
+  memory.reset(info.CreateMemory(getpid()));
+  ASSERT_TRUE(memory.get() != nullptr);
+
+  std::vector<uint8_t> read_buffer(1024);
+  ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
+  for (size_t i = 0; i < read_buffer.size(); i++) {
+    ASSERT_EQ(i % 256, read_buffer[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_FALSE(memory->Read(read_buffer.size(), read_buffer.data(), 1));
+}
+
+TEST_F(MapInfoTest, create_memory_remote_memory) {
+  std::vector<uint8_t> buffer(1024);
+  memset(buffer.data(), 0xa, buffer.size());
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true)
+      ;
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+
+  ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) != -1);
+  uint64_t iterations = 0;
+  siginfo_t si;
+  while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
+    usleep(30);
+    iterations++;
+    ASSERT_LT(iterations, 500000000ULL);
+  }
+
+  MapInfo info;
+  info.start = reinterpret_cast<uint64_t>(buffer.data());
+  info.end = info.start + buffer.size();
+  info.offset = 0;
+
+  std::unique_ptr<Memory> memory;
+  memory.reset(info.CreateMemory(pid));
+  ASSERT_TRUE(memory.get() != nullptr);
+  // Set the local memory to a different value to guarantee we are reading
+  // from the remote process.
+  memset(buffer.data(), 0x1, buffer.size());
+  std::vector<uint8_t> read_buffer(1024);
+  ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
+  for (size_t i = 0; i < read_buffer.size(); i++) {
+    ASSERT_EQ(0xaU, read_buffer[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+  kill(pid, SIGKILL);
+}
+
+TEST_F(MapInfoTest, get_elf) {
+  // Create a map to use as initialization data.
+  void* map = mmap(nullptr, 1024, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  ASSERT_NE(MAP_FAILED, map);
+
+  uint64_t start = reinterpret_cast<uint64_t>(map);
+  MapInfo info{.start = start, .end = start + 1024, .offset = 0, .name = ""};
+
+  // The map contains garbage, but this should still produce an elf object.
+  Elf* elf = info.GetElf(getpid(), false);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_FALSE(elf->valid());
+
+  ASSERT_EQ(0, munmap(map, 1024));
+}
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
new file mode 100644
index 0000000..7eb9bae
--- /dev/null
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -0,0 +1,237 @@
+/*
+ * 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 <sys/mman.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Maps.h"
+
+TEST(MapsTest, parse_permissions) {
+  BufferMaps maps(
+      "1000-2000 ---- 00000000 00:00 0\n"
+      "2000-3000 r--- 00000000 00:00 0\n"
+      "3000-4000 -w-- 00000000 00:00 0\n"
+      "4000-5000 --x- 00000000 00:00 0\n"
+      "5000-6000 rwx- 00000000 00:00 0\n");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(5U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ(PROT_NONE, it->flags);
+  ASSERT_EQ(0x1000U, it->start);
+  ASSERT_EQ(0x2000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_READ, it->flags);
+  ASSERT_EQ(0x2000U, it->start);
+  ASSERT_EQ(0x3000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_WRITE, it->flags);
+  ASSERT_EQ(0x3000U, it->start);
+  ASSERT_EQ(0x4000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_EXEC, it->flags);
+  ASSERT_EQ(0x4000U, it->start);
+  ASSERT_EQ(0x5000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, it->flags);
+  ASSERT_EQ(0x5000U, it->start);
+  ASSERT_EQ(0x6000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(it, maps.end());
+}
+
+TEST(MapsTest, parse_name) {
+  BufferMaps maps(
+      "720b29b000-720b29e000 rw-p 00000000 00:00 0\n"
+      "720b29e000-720b29f000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+      "720b29f000-720b2a0000 rw-p 00000000 00:00 0");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(3U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ("", it->name);
+  ASSERT_EQ(0x720b29b000U, it->start);
+  ASSERT_EQ(0x720b29e000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ++it;
+  ASSERT_EQ("/system/lib/fake.so", it->name);
+  ASSERT_EQ(0x720b29e000U, it->start);
+  ASSERT_EQ(0x720b29f000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ++it;
+  ASSERT_EQ("", it->name);
+  ASSERT_EQ(0x720b29f000U, it->start);
+  ASSERT_EQ(0x720b2a0000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ++it;
+  ASSERT_EQ(it, maps.end());
+}
+
+TEST(MapsTest, parse_offset) {
+  BufferMaps maps(
+      "a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+      "e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(2U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(0xa000U, it->start);
+  ASSERT_EQ(0xe000U, it->end);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ASSERT_EQ("/system/lib/fake.so", it->name);
+  ++it;
+  ASSERT_EQ(0xa12345U, it->offset);
+  ASSERT_EQ(0xe000U, it->start);
+  ASSERT_EQ(0xf000U, it->end);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ASSERT_EQ("/system/lib/fake.so", it->name);
+  ++it;
+  ASSERT_EQ(maps.end(), it);
+}
+
+TEST(MapsTest, device) {
+  BufferMaps maps(
+      "a000-e000 rw-p 00000000 00:00 0 /dev/\n"
+      "f000-f100 rw-p 00000000 00:00 0 /dev/does_not_exist\n"
+      "f100-f200 rw-p 00000000 00:00 0 /dev/ashmem/does_not_exist\n"
+      "f200-f300 rw-p 00000000 00:00 0 /devsomething/does_not_exist\n");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(4U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_TRUE(it->flags & 0x8000);
+  ASSERT_EQ("/dev/", it->name);
+  ++it;
+  ASSERT_TRUE(it->flags & 0x8000);
+  ASSERT_EQ("/dev/does_not_exist", it->name);
+  ++it;
+  ASSERT_FALSE(it->flags & 0x8000);
+  ASSERT_EQ("/dev/ashmem/does_not_exist", it->name);
+  ++it;
+  ASSERT_FALSE(it->flags & 0x8000);
+  ASSERT_EQ("/devsomething/does_not_exist", it->name);
+}
+
+TEST(MapsTest, file_smoke) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_TRUE(
+      android::base::WriteStringToFile("720b29b000-720b29e000 r-xp a0000000 00:00 0   /fake.so\n"
+                                       "720b2b0000-720b2e0000 r-xp b0000000 00:00 0   /fake2.so\n"
+                                       "720b2e0000-720b2f0000 r-xp c0000000 00:00 0   /fake3.so\n",
+                                       tf.path, 0660, getuid(), getgid()));
+
+  FileMaps maps(tf.path);
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(3U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ(0x720b29b000U, it->start);
+  ASSERT_EQ(0x720b29e000U, it->end);
+  ASSERT_EQ(0xa0000000U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+  ASSERT_EQ("/fake.so", it->name);
+  ++it;
+  ASSERT_EQ(0x720b2b0000U, it->start);
+  ASSERT_EQ(0x720b2e0000U, it->end);
+  ASSERT_EQ(0xb0000000U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+  ASSERT_EQ("/fake2.so", it->name);
+  ++it;
+  ASSERT_EQ(0x720b2e0000U, it->start);
+  ASSERT_EQ(0x720b2f0000U, it->end);
+  ASSERT_EQ(0xc0000000U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+  ASSERT_EQ("/fake3.so", it->name);
+  ++it;
+  ASSERT_EQ(it, maps.end());
+}
+
+TEST(MapsTest, find) {
+  BufferMaps maps(
+      "1000-2000 r--p 00000010 00:00 0 /system/lib/fake1.so\n"
+      "3000-4000 -w-p 00000020 00:00 0 /system/lib/fake2.so\n"
+      "6000-8000 --xp 00000030 00:00 0 /system/lib/fake3.so\n"
+      "a000-b000 rw-p 00000040 00:00 0 /system/lib/fake4.so\n"
+      "e000-f000 rwxp 00000050 00:00 0 /system/lib/fake5.so\n");
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(5U, maps.Total());
+
+  ASSERT_TRUE(maps.Find(0x500) == nullptr);
+  ASSERT_TRUE(maps.Find(0x2000) == nullptr);
+  ASSERT_TRUE(maps.Find(0x5010) == nullptr);
+  ASSERT_TRUE(maps.Find(0x9a00) == nullptr);
+  ASSERT_TRUE(maps.Find(0xf000) == nullptr);
+  ASSERT_TRUE(maps.Find(0xf010) == nullptr);
+
+  MapInfo* info = maps.Find(0x1000);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0x1000U, info->start);
+  ASSERT_EQ(0x2000U, info->end);
+  ASSERT_EQ(0x10U, info->offset);
+  ASSERT_EQ(PROT_READ, info->flags);
+  ASSERT_EQ("/system/lib/fake1.so", info->name);
+
+  info = maps.Find(0x3020);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0x3000U, info->start);
+  ASSERT_EQ(0x4000U, info->end);
+  ASSERT_EQ(0x20U, info->offset);
+  ASSERT_EQ(PROT_WRITE, info->flags);
+  ASSERT_EQ("/system/lib/fake2.so", info->name);
+
+  info = maps.Find(0x6020);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0x6000U, info->start);
+  ASSERT_EQ(0x8000U, info->end);
+  ASSERT_EQ(0x30U, info->offset);
+  ASSERT_EQ(PROT_EXEC, info->flags);
+  ASSERT_EQ("/system/lib/fake3.so", info->name);
+
+  info = maps.Find(0xafff);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0xa000U, info->start);
+  ASSERT_EQ(0xb000U, info->end);
+  ASSERT_EQ(0x40U, info->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, info->flags);
+  ASSERT_EQ("/system/lib/fake4.so", info->name);
+
+  info = maps.Find(0xe500);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0xe000U, info->start);
+  ASSERT_EQ(0xf000U, info->end);
+  ASSERT_EQ(0x50U, info->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
+  ASSERT_EQ("/system/lib/fake5.so", info->name);
+}
diff --git a/libunwindstack/tests/MemoryBuffer.cpp b/libunwindstack/tests/MemoryBuffer.cpp
new file mode 100644
index 0000000..af3d6b9
--- /dev/null
+++ b/libunwindstack/tests/MemoryBuffer.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 <vector>
+
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+
+class MemoryBufferTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+    memory_.reset(new MemoryBuffer);
+  }
+  std::unique_ptr<MemoryBuffer> memory_;
+};
+
+TEST_F(MemoryBufferTest, empty) {
+  ASSERT_EQ(0U, memory_->Size());
+  std::vector<uint8_t> buffer(1024);
+  ASSERT_FALSE(memory_->Read(0, buffer.data(), 1));
+  ASSERT_EQ(nullptr, memory_->GetPtr(0));
+  ASSERT_EQ(nullptr, memory_->GetPtr(1));
+}
+
+TEST_F(MemoryBufferTest, write_read) {
+  memory_->Resize(256);
+  ASSERT_EQ(256U, memory_->Size());
+  ASSERT_TRUE(memory_->GetPtr(0) != nullptr);
+  ASSERT_TRUE(memory_->GetPtr(1) != nullptr);
+  ASSERT_TRUE(memory_->GetPtr(255) != nullptr);
+  ASSERT_TRUE(memory_->GetPtr(256) == nullptr);
+
+  uint8_t* data = memory_->GetPtr(0);
+  for (size_t i = 0; i < memory_->Size(); i++) {
+    data[i] = i;
+  }
+
+  std::vector<uint8_t> buffer(memory_->Size());
+  ASSERT_TRUE(memory_->Read(0, buffer.data(), buffer.size()));
+  for (size_t i = 0; i < buffer.size(); i++) {
+    ASSERT_EQ(i, buffer[i]) << "Failed at byte " << i;
+  }
+}
+
+TEST_F(MemoryBufferTest, read_failures) {
+  memory_->Resize(100);
+  std::vector<uint8_t> buffer(200);
+  ASSERT_FALSE(memory_->Read(0, buffer.data(), 101));
+  ASSERT_FALSE(memory_->Read(100, buffer.data(), 1));
+  ASSERT_FALSE(memory_->Read(101, buffer.data(), 2));
+  ASSERT_FALSE(memory_->Read(99, buffer.data(), 2));
+  ASSERT_TRUE(memory_->Read(99, buffer.data(), 1));
+}
+
+TEST_F(MemoryBufferTest, read_failure_overflow) {
+  memory_->Resize(100);
+  std::vector<uint8_t> buffer(200);
+
+  ASSERT_FALSE(memory_->Read(UINT64_MAX - 100, buffer.data(), 200));
+}
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index e05736b..70ef30a 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -75,4 +75,16 @@
   }
 };
 
+class MemoryFakeRemote : public MemoryRemote {
+ public:
+  MemoryFakeRemote() : MemoryRemote(0) {}
+  virtual ~MemoryFakeRemote() = default;
+
+ protected:
+  bool PtraceRead(uint64_t, long* value) override {
+    *value = 0;
+    return true;
+  }
+};
+
 #endif  // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
index 870ca19..aa7a23a 100644
--- a/libunwindstack/tests/MemoryFileTest.cpp
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#include <string>
+#include <vector>
+
 #include <android-base/test_utils.h>
 #include <android-base/file.h>
 #include <gtest/gtest.h>
@@ -39,7 +42,7 @@
   TemporaryFile* tf_ = nullptr;
 };
 
-TEST_F(MemoryFileTest, offset_0) {
+TEST_F(MemoryFileTest, init_offset_0) {
   WriteTestData();
 
   ASSERT_TRUE(memory_.Init(tf_->path, 0));
@@ -49,7 +52,7 @@
   ASSERT_STREQ("0123456789", buffer.data());
 }
 
-TEST_F(MemoryFileTest, offset_non_zero) {
+TEST_F(MemoryFileTest, init_offset_non_zero) {
   WriteTestData();
 
   ASSERT_TRUE(memory_.Init(tf_->path, 10));
@@ -59,7 +62,7 @@
   ASSERT_STREQ("abcdefghij", buffer.data());
 }
 
-TEST_F(MemoryFileTest, offset_non_zero_larger_than_pagesize) {
+TEST_F(MemoryFileTest, init_offset_non_zero_larger_than_pagesize) {
   size_t pagesize = getpagesize();
   std::string large_string;
   for (size_t i = 0; i < pagesize; i++) {
@@ -75,7 +78,7 @@
   ASSERT_STREQ("abcdefgh", buffer.data());
 }
 
-TEST_F(MemoryFileTest, offset_pagesize_aligned) {
+TEST_F(MemoryFileTest, init_offset_pagesize_aligned) {
   size_t pagesize = getpagesize();
   std::string data;
   for (size_t i = 0; i < 2 * pagesize; i++) {
@@ -96,7 +99,7 @@
   ASSERT_STREQ(expected_str.c_str(), buffer.data());
 }
 
-TEST_F(MemoryFileTest, offset_pagesize_aligned_plus_extra) {
+TEST_F(MemoryFileTest, init_offset_pagesize_aligned_plus_extra) {
   size_t pagesize = getpagesize();
   std::string data;
   for (size_t i = 0; i < 2 * pagesize; i++) {
@@ -117,6 +120,23 @@
   ASSERT_STREQ(expected_str.c_str(), buffer.data());
 }
 
+TEST_F(MemoryFileTest, init_offset_greater_than_filesize) {
+  size_t pagesize = getpagesize();
+  std::string data;
+  uint64_t file_size = 2 * pagesize + pagesize / 2;
+  for (size_t i = 0; i < file_size; i++) {
+    data += static_cast<char>((i / pagesize) + '0');
+  }
+  ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+
+  // Check offset > file size fails and aligned_offset > file size.
+  ASSERT_FALSE(memory_.Init(tf_->path, file_size + 2 * pagesize));
+  // Check offset == filesize fails.
+  ASSERT_FALSE(memory_.Init(tf_->path, file_size));
+  // Check aligned_offset < filesize, but offset > filesize fails.
+  ASSERT_FALSE(memory_.Init(tf_->path, 2 * pagesize + pagesize / 2 + pagesize / 4));
+}
+
 TEST_F(MemoryFileTest, read_error) {
   std::string data;
   for (size_t i = 0; i < 5000; i++) {
@@ -137,32 +157,9 @@
   ASSERT_TRUE(memory_.Read(4990, buffer.data(), 10));
   ASSERT_FALSE(memory_.Read(4999, buffer.data(), 2));
   ASSERT_TRUE(memory_.Read(4999, buffer.data(), 1));
-}
 
-TEST_F(MemoryFileTest, read_string) {
-  std::string value("name_in_file");
-  ASSERT_TRUE(android::base::WriteFully(tf_->fd, value.c_str(), value.size() + 1));
-
-  std::string name;
-  ASSERT_TRUE(memory_.Init(tf_->path, 0));
-  ASSERT_TRUE(memory_.ReadString(0, &name));
-  ASSERT_EQ("name_in_file", name);
-  ASSERT_TRUE(memory_.ReadString(5, &name));
-  ASSERT_EQ("in_file", name);
-}
-
-TEST_F(MemoryFileTest, read_string_error) {
-  std::vector<uint8_t> buffer = { 0x23, 0x32, 0x45 };
-  ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size()));
-
-  std::string name;
-  ASSERT_TRUE(memory_.Init(tf_->path, 0));
-
-  // Read from a non-existant address.
-  ASSERT_FALSE(memory_.ReadString(100, &name));
-
-  // This should fail because there is no terminating \0
-  ASSERT_FALSE(memory_.ReadString(0, &name));
+  // Check that overflow fails properly.
+  ASSERT_FALSE(memory_.Read(UINT64_MAX - 100, buffer.data(), 200));
 }
 
 TEST_F(MemoryFileTest, read_past_file_within_mapping) {
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
index 0ba5f1c..ab999da 100644
--- a/libunwindstack/tests/MemoryLocalTest.cpp
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -47,25 +47,6 @@
   }
 }
 
-TEST(MemoryLocalTest, read_string) {
-  std::string name("string_in_memory");
-
-  MemoryLocal local;
-
-  std::vector<uint8_t> dst(1024);
-  std::string dst_name;
-  ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(name.c_str()), &dst_name));
-  ASSERT_EQ("string_in_memory", dst_name);
-
-  ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name));
-  ASSERT_EQ("in_memory", dst_name);
-
-  ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 10));
-  ASSERT_EQ("in_memory", dst_name);
-
-  ASSERT_FALSE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 9));
-}
-
 TEST(MemoryLocalTest, read_illegal) {
   MemoryLocal local;
 
@@ -73,3 +54,13 @@
   ASSERT_FALSE(local.Read(0, dst.data(), 1));
   ASSERT_FALSE(local.Read(0, dst.data(), 100));
 }
+
+TEST(MemoryLocalTest, read_overflow) {
+  MemoryLocal local;
+
+  // On 32 bit this test doesn't necessarily cause an overflow. The 64 bit
+  // version will always go through the overflow check.
+  std::vector<uint8_t> dst(100);
+  uint64_t value;
+  ASSERT_FALSE(local.Read(reinterpret_cast<uint64_t>(&value), dst.data(), SIZE_MAX));
+}
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index d636ec4..ee5ba01 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -17,6 +17,7 @@
 #include <stdint.h>
 #include <string.h>
 
+#include <memory>
 #include <vector>
 
 #include <gtest/gtest.h>
@@ -65,35 +66,14 @@
   ASSERT_FALSE(range.Read(1020, dst.data(), 5));
   ASSERT_FALSE(range.Read(1024, dst.data(), 1));
   ASSERT_FALSE(range.Read(1024, dst.data(), 1024));
+
+  // Verify that reading up to the end works.
+  ASSERT_TRUE(range.Read(1020, dst.data(), 4));
 }
 
-TEST_F(MemoryRangeTest, read_string_past_end) {
-  std::string name("0123456789");
-  memory_->SetMemory(0, name);
+TEST_F(MemoryRangeTest, read_overflow) {
+  std::vector<uint8_t> buffer(100);
 
-  // Verify a read past the range fails.
-  MemoryRange range(memory_, 0, 5);
-  std::string dst_name;
-  ASSERT_FALSE(range.ReadString(0, &dst_name));
-}
-
-TEST_F(MemoryRangeTest, read_string_to_end) {
-  std::string name("0123456789");
-  memory_->SetMemory(30, name);
-
-  // Verify the range going to the end of the string works.
-  MemoryRange range(memory_, 30, 30 + name.size() + 1);
-  std::string dst_name;
-  ASSERT_TRUE(range.ReadString(0, &dst_name));
-  ASSERT_EQ("0123456789", dst_name);
-}
-
-TEST_F(MemoryRangeTest, read_string_fencepost) {
-  std::string name("0123456789");
-  memory_->SetMemory(10, name);
-
-  // Verify the range set to one byte less than the end of the string fails.
-  MemoryRange range(memory_, 10, 10 + name.size());
-  std::string dst_name;
-  ASSERT_FALSE(range.ReadString(0, &dst_name));
+  std::unique_ptr<MemoryRange> overflow(new MemoryRange(new MemoryFakeAlwaysReadZero, 100, 200));
+  ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100));
 }
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index 7664c3e..e48edf7 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -33,6 +33,8 @@
 
 #include "Memory.h"
 
+#include "MemoryFake.h"
+
 class MemoryRemoteTest : public ::testing::Test {
  protected:
   static uint64_t NanoTime() {
@@ -121,6 +123,9 @@
   ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 1, dst.data(), 1));
   ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 4, dst.data(), 8));
 
+  // Check overflow condition is caught properly.
+  ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200));
+
   ASSERT_EQ(0, munmap(src, pagesize));
 
   ASSERT_TRUE(Detach(pid));
@@ -128,6 +133,14 @@
   kill(pid, SIGKILL);
 }
 
+TEST_F(MemoryRemoteTest, read_overflow) {
+  MemoryFakeRemote remote;
+
+  // Check overflow condition is caught properly.
+  std::vector<uint8_t> dst(200);
+  ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200));
+}
+
 TEST_F(MemoryRemoteTest, read_illegal) {
   pid_t pid;
   if ((pid = fork()) == 0) {
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
new file mode 100644
index 0000000..51b5d7d
--- /dev/null
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "MemoryFake.h"
+
+TEST(MemoryTest, read32) {
+  MemoryFakeAlwaysReadZero memory;
+
+  uint32_t data = 0xffffffff;
+  ASSERT_TRUE(memory.Read32(0, &data));
+  ASSERT_EQ(0U, data);
+}
+
+TEST(MemoryTest, read64) {
+  MemoryFakeAlwaysReadZero memory;
+
+  uint64_t data = 0xffffffffffffffffULL;
+  ASSERT_TRUE(memory.Read64(0, &data));
+  ASSERT_EQ(0U, data);
+}
+
+struct FakeStruct {
+  int one;
+  bool two;
+  uint32_t three;
+  uint64_t four;
+};
+
+TEST(MemoryTest, read_field) {
+  MemoryFakeAlwaysReadZero memory;
+
+  FakeStruct data;
+  memset(&data, 0xff, sizeof(data));
+  ASSERT_TRUE(memory.ReadField(0, &data, &data.one, sizeof(data.one)));
+  ASSERT_EQ(0, data.one);
+
+  memset(&data, 0xff, sizeof(data));
+  ASSERT_TRUE(memory.ReadField(0, &data, &data.two, sizeof(data.two)));
+  ASSERT_FALSE(data.two);
+
+  memset(&data, 0xff, sizeof(data));
+  ASSERT_TRUE(memory.ReadField(0, &data, &data.three, sizeof(data.three)));
+  ASSERT_EQ(0U, data.three);
+
+  memset(&data, 0xff, sizeof(data));
+  ASSERT_TRUE(memory.ReadField(0, &data, &data.four, sizeof(data.four)));
+  ASSERT_EQ(0U, data.four);
+}
+
+TEST(MemoryTest, read_field_fails) {
+  MemoryFakeAlwaysReadZero memory;
+
+  FakeStruct data;
+  memset(&data, 0xff, sizeof(data));
+
+  ASSERT_FALSE(memory.ReadField(UINT64_MAX, &data, &data.three, sizeof(data.three)));
+
+  // Field and start reversed, should fail.
+  ASSERT_FALSE(memory.ReadField(100, &data.two, &data, sizeof(data.two)));
+  ASSERT_FALSE(memory.ReadField(0, &data.two, &data, sizeof(data.two)));
+}
+
+TEST(MemoryTest, read_string) {
+  std::string name("string_in_memory");
+
+  MemoryFake memory;
+
+  memory.SetMemory(100, name.c_str(), name.size() + 1);
+
+  std::string dst_name;
+  ASSERT_TRUE(memory.ReadString(100, &dst_name));
+  ASSERT_EQ("string_in_memory", dst_name);
+
+  ASSERT_TRUE(memory.ReadString(107, &dst_name));
+  ASSERT_EQ("in_memory", dst_name);
+
+  // Set size greater than string.
+  ASSERT_TRUE(memory.ReadString(107, &dst_name, 10));
+  ASSERT_EQ("in_memory", dst_name);
+
+  ASSERT_FALSE(memory.ReadString(107, &dst_name, 9));
+}
+
+TEST(MemoryTest, read_string_error) {
+  std::string name("short");
+
+  MemoryFake memory;
+
+  // Save everything except the terminating '\0'.
+  memory.SetMemory(0, name.c_str(), name.size());
+
+  std::string dst_name;
+  // Read from a non-existant address.
+  ASSERT_FALSE(memory.ReadString(100, &dst_name));
+
+  // This should fail because there is no terminating '\0'.
+  ASSERT_FALSE(memory.ReadString(0, &dst_name));
+
+  // This should pass because there is a terminating '\0'.
+  memory.SetData8(name.size(), '\0');
+  ASSERT_TRUE(memory.ReadString(0, &dst_name));
+  ASSERT_EQ("short", dst_name);
+}
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
new file mode 100644
index 0000000..ff030c8
--- /dev/null
+++ b/libunwindstack/tests/RegsFake.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
+
+#include <stdint.h>
+
+#include "Regs.h"
+
+template <typename TypeParam>
+class RegsFake : public RegsImpl<TypeParam> {
+ public:
+  RegsFake(uint16_t total_regs, uint16_t sp_reg)
+      : RegsImpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+  virtual ~RegsFake() = default;
+
+  uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; }
+  uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+  bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
+};
+
+#endif  // _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 0dac278..3056373 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -48,13 +48,13 @@
 };
 
 template <typename TypeParam>
-class RegsTestTmpl : public RegsTmpl<TypeParam> {
+class RegsTestImpl : public RegsImpl<TypeParam> {
  public:
-  RegsTestTmpl(uint16_t total_regs, uint16_t regs_sp)
-      : RegsTmpl<TypeParam>(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
-  RegsTestTmpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc)
-      : RegsTmpl<TypeParam>(total_regs, regs_sp, return_loc) {}
-  virtual ~RegsTestTmpl() = default;
+  RegsTestImpl(uint16_t total_regs, uint16_t regs_sp)
+      : RegsImpl<TypeParam>(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+  RegsTestImpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc)
+      : RegsImpl<TypeParam>(total_regs, regs_sp, return_loc) {}
+  virtual ~RegsTestImpl() = default;
 
   uint64_t GetAdjustedPc(uint64_t, Elf*) { return 0; }
 };
@@ -80,7 +80,7 @@
 };
 
 TEST_F(RegsTest, regs32) {
-  RegsTestTmpl<uint32_t> regs32(50, 10);
+  RegsTestImpl<uint32_t> regs32(50, 10);
   ASSERT_EQ(50U, regs32.total_regs());
   ASSERT_EQ(10U, regs32.sp_reg());
 
@@ -103,7 +103,7 @@
 }
 
 TEST_F(RegsTest, regs64) {
-  RegsTestTmpl<uint64_t> regs64(30, 12);
+  RegsTestImpl<uint64_t> regs64(30, 12);
   ASSERT_EQ(30U, regs64.total_regs());
   ASSERT_EQ(12U, regs64.sp_reg());
 
@@ -127,7 +127,7 @@
 
 template <typename AddressType>
 void RegsTest::regs_rel_pc() {
-  RegsTestTmpl<AddressType> regs(30, 12);
+  RegsTestImpl<AddressType> regs(30, 12);
 
   elf_interface_->set_load_bias(0);
   MapInfo map_info{.start = 0x1000, .end = 0x2000};
@@ -147,7 +147,7 @@
 
 template <typename AddressType>
 void RegsTest::regs_return_address_register() {
-  RegsTestTmpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
+  RegsTestImpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
 
   regs[5] = 0x12345;
   uint64_t value;
@@ -164,7 +164,7 @@
 }
 
 TEST_F(RegsTest, regs32_return_address_sp_offset) {
-  RegsTestTmpl<uint32_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2));
+  RegsTestImpl<uint32_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2));
 
   regs.set_sp(0x2002);
   memory_->SetData32(0x2000, 0x12345678);
@@ -174,7 +174,7 @@
 }
 
 TEST_F(RegsTest, regs64_return_address_sp_offset) {
-  RegsTestTmpl<uint64_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8));
+  RegsTestImpl<uint64_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8));
 
   regs.set_sp(0x2008);
   memory_->SetData64(0x2000, 0x12345678aabbccddULL);
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
new file mode 100644
index 0000000..a0a21e6
--- /dev/null
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+#include "MemoryFake.h"
+#include "Symbols.h"
+
+template <typename TypeParam>
+class SymbolsTest : public ::testing::Test {
+ protected:
+  void SetUp() override { memory_.Clear(); }
+
+  void InitSym(TypeParam* sym, uint32_t st_value, uint32_t st_size, uint32_t st_name) {
+    memset(sym, 0, sizeof(*sym));
+    sym->st_info = STT_FUNC;
+    sym->st_value = st_value;
+    sym->st_size = st_size;
+    sym->st_name = st_name;
+    sym->st_shndx = SHN_COMMON;
+  }
+
+  MemoryFake memory_;
+};
+TYPED_TEST_CASE_P(SymbolsTest);
+
+TYPED_TEST_P(SymbolsTest, function_bounds_check) {
+  Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+  TypeParam sym;
+  this->InitSym(&sym, 0x5000, 0x10, 0x40);
+  uint64_t offset = 0x1000;
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+  std::string fake_name("fake_function");
+  this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+  std::string name;
+  uint64_t func_offset;
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("fake_function", name);
+  ASSERT_EQ(0U, func_offset);
+
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x500f, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("fake_function", name);
+  ASSERT_EQ(0xfU, func_offset);
+
+  // Check one before and one after the function.
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x4fff, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5010, 0, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, no_symbol) {
+  Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+  TypeParam sym;
+  this->InitSym(&sym, 0x5000, 0x10, 0x40);
+  uint64_t offset = 0x1000;
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+  std::string fake_name("fake_function");
+  this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+  // First verify that we can get the name.
+  std::string name;
+  uint64_t func_offset;
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("fake_function", name);
+  ASSERT_EQ(0U, func_offset);
+
+  // Now modify the info field so it's no longer a function.
+  sym.st_info = 0;
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  // Clear the cache to force the symbol data to be re-read.
+  symbols.ClearCache();
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+
+  // Set the function back, and set the shndx to UNDEF.
+  sym.st_info = STT_FUNC;
+  sym.st_shndx = SHN_UNDEF;
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  // Clear the cache to force the symbol data to be re-read.
+  symbols.ClearCache();
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, multiple_entries) {
+  Symbols symbols(0x1000, sizeof(TypeParam) * 3, sizeof(TypeParam), 0x2000, 0x500);
+
+  TypeParam sym;
+  uint64_t offset = 0x1000;
+  std::string fake_name;
+
+  this->InitSym(&sym, 0x5000, 0x10, 0x40);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  fake_name = "function_one";
+  this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+  offset += sizeof(sym);
+
+  this->InitSym(&sym, 0x3004, 0x200, 0x100);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  fake_name = "function_two";
+  this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
+  offset += sizeof(sym);
+
+  this->InitSym(&sym, 0xa010, 0x20, 0x230);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  fake_name = "function_three";
+  this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
+
+  std::string name;
+  uint64_t func_offset;
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_two", name);
+  ASSERT_EQ(1U, func_offset);
+
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_one", name);
+  ASSERT_EQ(4U, func_offset);
+
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_three", name);
+  ASSERT_EQ(1U, func_offset);
+
+  // Reget some of the others to verify getting one function name doesn't
+  // affect any of the next calls.
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5008, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_one", name);
+  ASSERT_EQ(8U, func_offset);
+
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3008, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_two", name);
+  ASSERT_EQ(4U, func_offset);
+
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa01a, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_three", name);
+  ASSERT_EQ(0xaU, func_offset);
+}
+
+TYPED_TEST_P(SymbolsTest, multiple_entries_nonstandard_size) {
+  uint64_t entry_size = sizeof(TypeParam) + 5;
+  Symbols symbols(0x1000, entry_size * 3, entry_size, 0x2000, 0x500);
+
+  TypeParam sym;
+  uint64_t offset = 0x1000;
+  std::string fake_name;
+
+  this->InitSym(&sym, 0x5000, 0x10, 0x40);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  fake_name = "function_one";
+  this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+  offset += entry_size;
+
+  this->InitSym(&sym, 0x3004, 0x200, 0x100);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  fake_name = "function_two";
+  this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
+  offset += entry_size;
+
+  this->InitSym(&sym, 0xa010, 0x20, 0x230);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  fake_name = "function_three";
+  this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
+
+  std::string name;
+  uint64_t func_offset;
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_two", name);
+  ASSERT_EQ(1U, func_offset);
+
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_one", name);
+  ASSERT_EQ(4U, func_offset);
+
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_three", name);
+  ASSERT_EQ(1U, func_offset);
+}
+
+TYPED_TEST_P(SymbolsTest, load_bias) {
+  Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+  TypeParam sym;
+  this->InitSym(&sym, 0x5000, 0x10, 0x40);
+  uint64_t offset = 0x1000;
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+  std::string fake_name("fake_function");
+  this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+  // Set a non-zero load_bias that should be a valid function offset.
+  std::string name;
+  uint64_t func_offset;
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("fake_function", name);
+  ASSERT_EQ(4U, func_offset);
+
+  // Set a flag that should cause the load_bias to be ignored.
+  sym.st_shndx = SHN_ABS;
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  // Clear the cache to force the symbol data to be re-read.
+  symbols.ClearCache();
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, symtab_value_out_of_bounds) {
+  Symbols symbols_end_at_100(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x100);
+  Symbols symbols_end_at_200(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x200);
+
+  TypeParam sym;
+  uint64_t offset = 0x1000;
+
+  this->InitSym(&sym, 0x5000, 0x10, 0xfb);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  offset += sizeof(sym);
+
+  this->InitSym(&sym, 0x3000, 0x10, 0x100);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+  // Put the name across the end of the tab.
+  std::string fake_name("fake_function");
+  this->memory_.SetMemory(0x20fb, fake_name.c_str(), fake_name.size() + 1);
+
+  std::string name;
+  uint64_t func_offset;
+  // Verify that we can get the function name properly for both entries.
+  ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("fake_function", name);
+  ASSERT_EQ(0U, func_offset);
+  ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function", name);
+  ASSERT_EQ(0U, func_offset);
+
+  // Now use the symbol table that ends at 0x100.
+  ASSERT_FALSE(
+      symbols_end_at_100.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(
+      symbols_end_at_100.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+}
+
+// Verify the entire func table is cached.
+TYPED_TEST_P(SymbolsTest, symtab_read_cached) {
+  Symbols symbols(0x1000, 3 * sizeof(TypeParam), sizeof(TypeParam), 0xa000, 0x1000);
+
+  TypeParam sym;
+  uint64_t offset = 0x1000;
+
+  // Make sure that these entries are not in ascending order.
+  this->InitSym(&sym, 0x5000, 0x10, 0x100);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  offset += sizeof(sym);
+
+  this->InitSym(&sym, 0x2000, 0x300, 0x200);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  offset += sizeof(sym);
+
+  this->InitSym(&sym, 0x1000, 0x100, 0x300);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  offset += sizeof(sym);
+
+  // Do call that should cache all of the entries (except the string data).
+  std::string name;
+  uint64_t func_offset;
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+  this->memory_.Clear();
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+
+  // Clear the memory and only put the symbol data string data in memory.
+  this->memory_.Clear();
+
+  std::string fake_name;
+  fake_name = "first_entry";
+  this->memory_.SetMemory(0xa100, fake_name.c_str(), fake_name.size() + 1);
+  fake_name = "second_entry";
+  this->memory_.SetMemory(0xa200, fake_name.c_str(), fake_name.size() + 1);
+  fake_name = "third_entry";
+  this->memory_.SetMemory(0xa300, fake_name.c_str(), fake_name.size() + 1);
+
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5001, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("first_entry", name);
+  ASSERT_EQ(1U, func_offset);
+
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x2002, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("second_entry", name);
+  ASSERT_EQ(2U, func_offset);
+
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x1003, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("third_entry", name);
+  ASSERT_EQ(3U, func_offset);
+}
+
+REGISTER_TYPED_TEST_CASE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
+                           multiple_entries_nonstandard_size, load_bias, symtab_value_out_of_bounds,
+                           symtab_read_cached);
+
+typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
new file mode 100644
index 0000000..a0d6b9b
--- /dev/null
+++ b/libusbhost/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library {
+    name: "libusbhost",
+    host_supported: true,
+    srcs: ["usbhost.c"],
+    cflags: ["-Werror"],
+    export_include_dirs: ["include"],
+    target: {
+        android: {
+            cflags: [
+                "-g",
+                "-DUSE_LIBLOG",
+            ],
+            shared_libs: ["liblog"],
+        },
+        darwin: {
+            enabled: false,
+        },
+    },
+}
diff --git a/libusbhost/Android.mk b/libusbhost/Android.mk
deleted file mode 100644
index 9439846..0000000
--- a/libusbhost/Android.mk
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# Static library for Linux host
-# ========================================================
-
-ifeq ($(HOST_OS),linux)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libusbhost
-LOCAL_SRC_FILES := usbhost.c
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-endif
-
-# Shared library for target
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libusbhost
-LOCAL_SRC_FILES := usbhost.c
-LOCAL_CFLAGS := -g -DUSE_LIBLOG -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-# needed for logcat
-LOCAL_SHARED_LIBRARIES := libcutils
-include $(BUILD_SHARED_LIBRARY)
-
-# Static library for target
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libusbhost
-LOCAL_SRC_FILES := usbhost.c
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-include $(BUILD_STATIC_LIBRARY)
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 7adb4f2..44b878d 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -23,7 +23,7 @@
 
 #ifdef USE_LIBLOG
 #define LOG_TAG "usbhost"
-#include "utils/Log.h"
+#include "log/log.h"
 #define D ALOGD
 #else
 #define D printf
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 5e76279..508f553 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -17,8 +17,14 @@
     vendor_available: true,
     host_supported: true,
 
-    header_libs: ["libsystem_headers",],
-    export_header_lib_headers: ["libsystem_headers",],
+    header_libs: [
+        "libsystem_headers",
+        "libcutils_headers"
+    ],
+    export_header_lib_headers: [
+        "libsystem_headers",
+        "libcutils_headers"
+    ],
     export_include_dirs: ["include"],
 
     target: {
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 1afa1ec..3c4d81c 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -48,8 +48,16 @@
 
 // Constructor.  Create an empty object.
 FileMap::FileMap(void)
-    : mFileName(NULL), mBasePtr(NULL), mBaseLength(0),
-      mDataPtr(NULL), mDataLength(0)
+    : mFileName(NULL),
+      mBasePtr(NULL),
+      mBaseLength(0),
+      mDataPtr(NULL),
+      mDataLength(0)
+#if defined(__MINGW32__)
+      ,
+      mFileHandle(INVALID_HANDLE_VALUE),
+      mFileMapping(NULL)
+#endif
 {
 }
 
@@ -65,8 +73,8 @@
     other.mBasePtr = NULL;
     other.mDataPtr = NULL;
 #if defined(__MINGW32__)
-    other.mFileHandle = 0;
-    other.mFileMapping = 0;
+    other.mFileHandle = INVALID_HANDLE_VALUE;
+    other.mFileMapping = NULL;
 #endif
 }
 
@@ -84,8 +92,8 @@
 #if defined(__MINGW32__)
     mFileHandle = other.mFileHandle;
     mFileMapping = other.mFileMapping;
-    other.mFileHandle = 0;
-    other.mFileMapping = 0;
+    other.mFileHandle = INVALID_HANDLE_VALUE;
+    other.mFileMapping = NULL;
 #endif
     return *this;
 }
@@ -101,7 +109,7 @@
         ALOGD("UnmapViewOfFile(%p) failed, error = %lu\n", mBasePtr,
               GetLastError() );
     }
-    if (mFileMapping != INVALID_HANDLE_VALUE) {
+    if (mFileMapping != NULL) {
         CloseHandle(mFileMapping);
     }
 #else
@@ -156,7 +164,7 @@
         ALOGE("MapViewOfFile(%" PRId64 ", %zu) failed with error %lu\n",
               adjOffset, adjLength, GetLastError() );
         CloseHandle(mFileMapping);
-        mFileMapping = INVALID_HANDLE_VALUE;
+        mFileMapping = NULL;
         return false;
     }
 #else // !defined(__MINGW32__)
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
index 289f153..696e978 100644
--- a/libvndksupport/linker.c
+++ b/libvndksupport/linker.c
@@ -31,7 +31,9 @@
         };
         void* handle = android_dlopen_ext(name, flag, &dlextinfo);
         if (!handle) {
-            ALOGE("Could not load %s from sphal namespace: %s.", name, dlerror());
+            ALOGE(
+                "Could not load %s from sphal namespace: %s. ",
+                name, dlerror());
         }
         return handle;
     } else {
@@ -43,4 +45,6 @@
     }
 }
 
-int android_unload_sphal_library(void* handle) { return dlclose(handle); }
+int android_unload_sphal_library(void* handle) {
+    return dlclose(handle);
+}
diff --git a/libziparchive/.clang-format b/libziparchive/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libziparchive/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 0a4f088..287a99c 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -124,3 +124,29 @@
         },
     },
 }
+
+// Performance benchmarks.
+cc_benchmark {
+    name: "ziparchive-benchmarks",
+    defaults: ["libziparchive_flags"],
+
+    srcs: [
+        "zip_archive_benchmark.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+
+    static_libs: [
+        "libziparchive",
+        "libz",
+        "libutils",
+    ],
+
+    target: {
+        host: {
+            cppflags: ["-Wno-unnamed-type-template-args"],
+        },
+    },
+}
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
new file mode 100644
index 0000000..cd3e164
--- /dev/null
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <android-base/test_utils.h>
+#include <benchmark/benchmark.h>
+#include <ziparchive/zip_archive.h>
+#include <ziparchive/zip_archive_stream_entry.h>
+#include <ziparchive/zip_writer.h>
+
+static TemporaryFile* CreateZip() {
+  TemporaryFile* result = new TemporaryFile;
+  FILE* fp = fdopen(result->fd, "w");
+
+  ZipWriter writer(fp);
+  std::string lastName = "file";
+  for (size_t i = 0; i < 1000; i++) {
+    // Make file names longer and longer.
+    lastName = lastName + std::to_string(i);
+    writer.StartEntry(lastName.c_str(), ZipWriter::kCompress);
+    writer.WriteBytes("helo", 4);
+    writer.FinishEntry();
+  }
+  writer.Finish();
+  fclose(fp);
+
+  return result;
+}
+
+static void FindEntry_no_match(benchmark::State& state) {
+  // Create a temporary zip archive.
+  std::unique_ptr<TemporaryFile> temp_file(CreateZip());
+  ZipArchiveHandle handle;
+  ZipEntry data;
+
+  // In order to walk through all file names in the archive, look for a name
+  // that does not exist in the archive.
+  ZipString name("thisFileNameDoesNotExist");
+
+  // Start the benchmark.
+  while (state.KeepRunning()) {
+    OpenArchive(temp_file->path, &handle);
+    FindEntry(handle, name, &data);
+    CloseArchive(handle);
+  }
+}
+BENCHMARK(FindEntry_no_match);
+
+static void Iterate_all_files(benchmark::State& state) {
+  std::unique_ptr<TemporaryFile> temp_file(CreateZip());
+  ZipArchiveHandle handle;
+  void* iteration_cookie;
+  ZipEntry data;
+  ZipString name;
+
+  while (state.KeepRunning()) {
+    OpenArchive(temp_file->path, &handle);
+    StartIteration(handle, &iteration_cookie, nullptr, nullptr);
+    while (Next(iteration_cookie, &data, &name) == 0) {
+    }
+    EndIteration(iteration_cookie);
+    CloseArchive(handle);
+  }
+}
+BENCHMARK(Iterate_all_files);
+
+BENCHMARK_MAIN()
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
new file mode 100644
index 0000000..3f8a503
--- /dev/null
+++ b/lmkd/Android.bp
@@ -0,0 +1,13 @@
+cc_binary {
+    name: "lmkd",
+
+    srcs: ["lmkd.c"],
+    shared_libs: [
+        "liblog",
+        "libprocessgroup",
+        "libcutils",
+    ],
+    cflags: ["-Werror"],
+
+    init_rc: ["lmkd.rc"],
+}
diff --git a/lmkd/Android.mk b/lmkd/Android.mk
deleted file mode 100644
index 8980d1c..0000000
--- a/lmkd/Android.mk
+++ /dev/null
@@ -1,12 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := lmkd.c
-LOCAL_SHARED_LIBRARIES := liblog libm libc libprocessgroup libcutils
-LOCAL_CFLAGS := -Werror
-
-LOCAL_MODULE := lmkd
-
-LOCAL_INIT_RC := lmkd.rc
-
-include $(BUILD_EXECUTABLE)
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 909f8e2..efcc817 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -30,12 +30,15 @@
 # 4: Number of allocations
 # 5: Id
 # 6: Percent
+# s: Number of seconds (monotonic time)
 # Default value for data of type int/long is 2 (bytes).
 #
 # TODO: generate ".java" and ".h" files with integer constants from this file.
 
 # These are used for testing, do not modify without updating
 # tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
+# system/core/liblog/tests/liblog_benchmark.cpp
+# system/core/liblog/tests/liblog_test.cpp
 42    answer (to life the universe etc|3)
 314   pi
 2718  e
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 64d1d2f..e9ef9cc 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -530,6 +530,7 @@
         "  process    — Display PID only.\n"
         "  raw        — Display the raw log message, with no other metadata fields.\n"
         "  tag        — Display the priority/tag only.\n"
+        "  thread     — Display priority, PID and TID of process issuing the message.\n"
         "  threadtime — Display the date, invocation time, priority, tag, and the PID\n"
         "               and TID of the thread issuing the message. (the default format).\n"
         "  time       — Display the date, invocation time, priority/tag, and PID of the\n"
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index 06cc90d..07040b0 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -61,3 +61,4 @@
     user logd
     group log
     writepid /dev/cpuset/system-background/tasks
+    oom_score_adjust -600
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 21868f2..786fb14 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -17,6 +17,7 @@
 #include <ctype.h>
 #include <dirent.h>
 #include <signal.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -30,7 +31,9 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
+#include <log/event_tag_map.h>
 #include <log/log.h>
 #include <log/log_event_list.h>
 
@@ -47,6 +50,16 @@
 
 #define BIG_BUFFER (5 * 1024)
 
+// rest(), let the logs settle.
+//
+// logd is in a background cgroup and under extreme load can take up to
+// 3 seconds to land a log entry. Under moderate load we can do with 200ms.
+static void rest() {
+    static const useconds_t restPeriod = 200000;
+
+    usleep(restPeriod);
+}
+
 // enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
 // non-syscall libs. Since we are only using this in the emergency of
 // a signal to stuff a terminating code into the logs, we will spin rather
@@ -68,9 +81,14 @@
     logcat_define(ctx);
 
 #undef LOG_TAG
-#define LOG_TAG "inject"
-    RLOGE(logcat_executable ".buckets");
-    sleep(1);
+#define LOG_TAG "inject.buckets"
+    // inject messages into radio, system, main and events buffers to
+    // ensure that we see all the begin[] bucket messages.
+    RLOGE(logcat_executable);
+    SLOGE(logcat_executable);
+    ALOGE(logcat_executable);
+    __android_log_bswrite(0, logcat_executable ".inject.buckets");
+    rest();
 
     ASSERT_TRUE(NULL !=
                 (fp = logcat_popen(
@@ -95,32 +113,45 @@
 
     logcat_pclose(ctx, fp);
 
-    EXPECT_EQ(15, ids);
+    EXPECT_EQ(ids, 15);
 
-    EXPECT_EQ(4, count);
+    EXPECT_EQ(count, 4);
 }
 
 TEST(logcat, event_tag_filter) {
     FILE* fp;
     logcat_define(ctx);
 
-    ASSERT_TRUE(NULL !=
-                (fp = logcat_popen(ctx, logcat_executable
-                                   " -b events -d -s auditd "
-                                   "am_proc_start am_pss am_proc_bound "
-                                   "dvm_lock_sample am_wtf 2>/dev/null")));
+#undef LOG_TAG
+#define LOG_TAG "inject.filter"
+    // inject messages into radio, system and main buffers
+    // with our unique log tag to test logcat filter.
+    RLOGE(logcat_executable);
+    SLOGE(logcat_executable);
+    ALOGE(logcat_executable);
+    rest();
+
+    std::string command = android::base::StringPrintf(
+        logcat_executable
+        " -b radio -b system -b main --pid=%d -d -s inject.filter 2>/dev/null",
+        getpid());
+    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, command.c_str())));
 
     char buffer[BIG_BUFFER];
 
     int count = 0;
 
     while (fgets(buffer, sizeof(buffer), fp)) {
-        ++count;
+        if (strncmp(begin, buffer, sizeof(begin) - 1)) ++count;
     }
 
     logcat_pclose(ctx, fp);
 
-    EXPECT_LT(4, count);
+    // logcat, liblogcat and logcatd test instances result in the progression
+    // of 3, 6 and 9 for our counts as each round is performed.
+    EXPECT_GE(count, 3);
+    EXPECT_LE(count, 9);
+    EXPECT_EQ(count % 3, 0);
 }
 
 // If there is not enough background noise in the logs, then spam the logs to
@@ -364,7 +395,7 @@
 
     } while ((count < 10) && --tries && inject(10 - count));
 
-    EXPECT_EQ(10, count);  // We want _some_ history, too small, falses below
+    EXPECT_EQ(count, 10);  // We want _some_ history, too small, falses below
     EXPECT_TRUE(last_timestamp != NULL);
     EXPECT_TRUE(first_timestamp != NULL);
     EXPECT_TRUE(second_timestamp != NULL);
@@ -689,9 +720,9 @@
 
     pclose(fp);
 
-    EXPECT_LE(2, count);
+    EXPECT_GE(count, 2);
 
-    EXPECT_EQ(1, signals);
+    EXPECT_EQ(signals, 1);
 }
 
 static void caught_blocking_tail(int signum) {
@@ -759,9 +790,9 @@
 
     pclose(fp);
 
-    EXPECT_LE(2, count);
+    EXPECT_GE(count, 2);
 
-    EXPECT_EQ(1, signals);
+    EXPECT_EQ(signals, 1);
 }
 #endif
 
@@ -869,7 +900,7 @@
             ADD_FAILURE();
         }
         pclose(fp);
-        EXPECT_EQ(11, log_file_count);
+        EXPECT_EQ(log_file_count, 11);
     }
     snprintf(command, sizeof(command), "rm -rf %s", tmp_out_dir);
     EXPECT_FALSE(IsFalse(system(command), command));
@@ -1122,17 +1153,17 @@
     char tmp_out_dir[strlen(tmp_out_dir_form) + 1];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
 
-    EXPECT_EQ(34, logrotate_count_id(logcat_cmd, tmp_out_dir));
-    EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+    EXPECT_EQ(logrotate_count_id(logcat_cmd, tmp_out_dir), 34);
+    EXPECT_EQ(logrotate_count_id(logcat_short_cmd, tmp_out_dir), 34);
 
     char id_file[strlen(tmp_out_dir_form) + strlen(log_filename) + 5];
     snprintf(id_file, sizeof(id_file), "%s/%s.id", tmp_out_dir, log_filename);
     if (getuid() != 0) {
         chmod(id_file, 0);
-        EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+        EXPECT_EQ(logrotate_count_id(logcat_short_cmd, tmp_out_dir), 34);
     }
     unlink(id_file);
-    EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+    EXPECT_EQ(logrotate_count_id(logcat_short_cmd, tmp_out_dir), 34);
 
     FILE* fp = fopen(id_file, "w");
     if (fp) {
@@ -1147,9 +1178,9 @@
     }
 
     int new_signature;
-    EXPECT_LE(
-        2, (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)));
-    EXPECT_GT(34, new_signature);
+    EXPECT_GE(
+        (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)), 2);
+    EXPECT_LT(new_signature, 34);
 
     static const char cleanup_cmd[] = "rm -rf %s";
     char command[strlen(cleanup_cmd) + strlen(tmp_out_dir_form)];
@@ -1204,7 +1235,12 @@
     signal(SIGALRM, caught_blocking_clear);
     alarm(2);
     while (fgets(buffer, sizeof(buffer), fp)) {
-        if (!strncmp(buffer, "clearLog: ", 10)) {
+        if (!strncmp(buffer, "clearLog: ", strlen("clearLog: "))) {
+            fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
+            count = signals = 1;
+            break;
+        }
+        if (!strncmp(buffer, "failed to clear", strlen("failed to clear"))) {
             fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
             count = signals = 1;
             break;
@@ -1285,10 +1321,10 @@
 
     pclose(fp);
 
-    EXPECT_LE(1, count);
-    EXPECT_EQ(1, minus_g);
+    EXPECT_GE(count, 1);
+    EXPECT_EQ(minus_g, 1);
 
-    EXPECT_EQ(1, signals);
+    EXPECT_EQ(signals, 1);
 }
 #endif
 
@@ -1412,7 +1448,7 @@
     LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
                                           logcat_regex_prefix "_aaaa"));
     // Let the logs settle
-    sleep(1);
+    rest();
 
     ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
@@ -1450,8 +1486,7 @@
     LOG_FAILURE_RETRY(
         __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
 
-    // Let the logs settle
-    sleep(1);
+    rest();
 
     ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
@@ -1476,8 +1511,7 @@
 
 static bool End_to_End(const char* tag, const char* fmt, ...) {
     logcat_define(ctx);
-    FILE* fp = logcat_popen(ctx,
-                            "logcat"
+    FILE* fp = logcat_popen(ctx, logcat_executable
                             " -v brief"
                             " -b events"
                             " -v descriptive"
@@ -1523,13 +1557,12 @@
         // Help us pinpoint where things went wrong ...
         fprintf(stderr, "Closest match for\n    %s\n  is\n    %s",
                 expect.c_str(), lastMatch.c_str());
-    } else if (count > 2) {
+    } else if (count > 3) {
         fprintf(stderr, "Too many matches (%d) for %s\n", count, expect.c_str());
     }
 
-    // Expect one the first time around as either liblogcat.descriptive or
-    // logcat.descriptive.  Expect two the second time as the other.
-    return count == 1 || count == 2;
+    // Three different known tests, we can see pollution from the others
+    return count && (count <= 3);
 }
 
 TEST(logcat, descriptive) {
@@ -1537,24 +1570,28 @@
         uint32_t tagNo;
         const char* tagStr;
     };
+    int ret;
 
     {
         static const struct tag hhgtg = { 42, "answer" };
         android_log_event_list ctx(hhgtg.tagNo);
         static const char theAnswer[] = "what is five by seven";
         ctx << theAnswer;
-        ctx.write();
+        // crafted to rest at least once after, and rest between retries.
+        for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+        EXPECT_GE(ret, 0);
         EXPECT_TRUE(
             End_to_End(hhgtg.tagStr, "to life the universe etc=%s", theAnswer));
     }
 
     {
         static const struct tag sync = { 2720, "sync" };
-        static const char id[] = "logcat.decriptive";
+        static const char id[] = ___STRING(logcat) ".descriptive-sync";
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << id << (int32_t)42 << (int32_t)-1 << (int32_t)0;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr,
                                    "[id=%s,event=42,source=-1,account=0]", id));
         }
@@ -1563,7 +1600,8 @@
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << id << (int32_t)43 << (int64_t)-1 << (int32_t)0;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "[id=%s,event=43,-1,0]", id));
         }
 
@@ -1571,7 +1609,8 @@
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << id << (int32_t)44 << (int32_t)-1 << (int64_t)0;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_GE(ret, 0);
             fprintf(stderr, "Expect a \"Closest match\" message\n");
             EXPECT_FALSE(End_to_End(
                 sync.tagStr, "[id=%s,event=44,source=-1,account=0]", id));
@@ -1583,7 +1622,8 @@
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint64_t)30 << (int32_t)2;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(
                 End_to_End(sync.tagStr, "[aggregation time=30ms,count=2]"));
         }
@@ -1591,7 +1631,8 @@
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint64_t)31570 << (int32_t)911;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(
                 End_to_End(sync.tagStr, "[aggregation time=31.57s,count=911]"));
         }
@@ -1602,42 +1643,48 @@
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)512;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=512B"));
         }
 
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)3072;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=3KB"));
         }
 
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)2097152;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=2MB"));
         }
 
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)2097153;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=2097153B"));
         }
 
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)1073741824;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=1GB"));
         }
 
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)3221225472;  // 3MB, but on purpose overflowed
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=-1GB"));
         }
     }
@@ -1645,9 +1692,52 @@
     {
         static const struct tag sync = { 27501, "notification_panel_hidden" };
         android_log_event_list ctx(sync.tagNo);
-        ctx.write();
+        for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+        EXPECT_GE(ret, 0);
         EXPECT_TRUE(End_to_End(sync.tagStr, ""));
     }
+
+    {
+        // Invent new entries because existing can not serve
+        EventTagMap* map = android_openEventTagMap(nullptr);
+        ASSERT_TRUE(nullptr != map);
+        static const char name[] = ___STRING(logcat) ".descriptive-monotonic";
+        int myTag = android_lookupEventTagNum(map, name, "(new|1|s)",
+                                              ANDROID_LOG_UNKNOWN);
+        android_closeEventTagMap(map);
+        ASSERT_NE(-1, myTag);
+
+        const struct tag sync = { (uint32_t)myTag, name };
+
+        {
+            android_log_event_list ctx(sync.tagNo);
+            ctx << (uint32_t)7;
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_GE(ret, 0);
+            EXPECT_TRUE(End_to_End(sync.tagStr, "new=7s"));
+        }
+        {
+            android_log_event_list ctx(sync.tagNo);
+            ctx << (uint32_t)62;
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_GE(ret, 0);
+            EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:02"));
+        }
+        {
+            android_log_event_list ctx(sync.tagNo);
+            ctx << (uint32_t)3673;
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_GE(ret, 0);
+            EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:01:13"));
+        }
+        {
+            android_log_event_list ctx(sync.tagNo);
+            ctx << (uint32_t)(86400 + 7200 + 180 + 58);
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_GE(ret, 0);
+            EXPECT_TRUE(End_to_End(sync.tagStr, "new=1d 2:03:58"));
+        }
+    }
 }
 
 static bool reportedSecurity(const char* command) {
@@ -1685,7 +1775,7 @@
 
 TEST(logcat, help) {
     size_t logcatHelpTextSize = commandOutputSize(logcat_executable " -h 2>&1");
-    EXPECT_LT(4096UL, logcatHelpTextSize);
+    EXPECT_GT(logcatHelpTextSize, 4096UL);
     size_t logcatLastHelpTextSize =
         commandOutputSize(logcat_executable " -L -h 2>&1");
 #ifdef USING_LOGCAT_EXECUTABLE_DEFAULT  // logcat and liblogcat
diff --git a/logd/Android.mk b/logd/Android.mk
index 9211037..fb51992 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -2,12 +2,9 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE:= logd
-
-LOCAL_INIT_RC := logd.rc
+LOCAL_MODULE:= liblogd
 
 LOCAL_SRC_FILES := \
-    main.cpp \
     LogCommand.cpp \
     CommandListener.cpp \
     LogListener.cpp \
@@ -15,6 +12,7 @@
     FlushCommand.cpp \
     LogBuffer.cpp \
     LogBufferElement.cpp \
+    LogBufferInterface.cpp \
     LogTimes.cpp \
     LogStatistics.cpp \
     LogWhiteBlackList.cpp \
@@ -25,12 +23,9 @@
     event.logtags
 
 LOCAL_SHARED_LIBRARIES := \
-    libsysutils \
-    liblog \
-    libcutils \
-    libbase \
-    libpackagelistparser \
-    libcap
+    libbase
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
 
 # This is what we want to do:
 #  event_logtags = $(shell \
@@ -46,6 +41,30 @@
 
 LOCAL_CFLAGS := -Werror $(event_flag)
 
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= logd
+
+LOCAL_INIT_RC := logd.rc
+
+LOCAL_SRC_FILES := \
+    main.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+    liblogd
+
+LOCAL_SHARED_LIBRARIES := \
+    libsysutils \
+    liblog \
+    libcutils \
+    libbase \
+    libpackagelistparser \
+    libcap
+
+LOCAL_CFLAGS := -Werror
+
 include $(BUILD_EXECUTABLE)
 
 include $(CLEAR_VARS)
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index c67d2bf..a9edc3e 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -44,14 +44,14 @@
 // LogTimeEntrys, and spawn a transitory per-client thread to
 // work at filing data to the  socket.
 //
-// global LogTimeEntry::lock() is used to protect access,
+// global LogTimeEntry::wrlock() is used to protect access,
 // reference counts are used to ensure that individual
 // LogTimeEntry lifetime is managed when not protected.
 void FlushCommand::runSocketCommand(SocketClient* client) {
     LogTimeEntry* entry = NULL;
     LastLogTimes& times = mReader.logbuf().mTimes;
 
-    LogTimeEntry::lock();
+    LogTimeEntry::wrlock();
     LastLogTimes::iterator it = times.begin();
     while (it != times.end()) {
         entry = (*it);
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 2d9024a..ee38d1c 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -277,7 +277,7 @@
             ++cp;
         }
         tid = pid;
-        logbuf->lock();
+        logbuf->wrlock();
         uid = logbuf->pidToUid(pid);
         logbuf->unlock();
         memmove(pidptr, cp, strlen(cp) + 1);
@@ -322,7 +322,7 @@
         pid = tid;
         comm = "auditd";
     } else {
-        logbuf->lock();
+        logbuf->wrlock();
         comm = commfree = logbuf->pidToName(pid);
         logbuf->unlock();
         if (!comm) {
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 4aa2c9f..7498325 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -72,22 +72,28 @@
         // as the act of mounting /data would trigger persist.logd.timestamp to
         // be corrected. 1/30 corner case YMMV.
         //
-        pthread_mutex_lock(&mLogElementsLock);
+        rdlock();
         LogBufferElementCollection::iterator it = mLogElements.begin();
         while ((it != mLogElements.end())) {
             LogBufferElement* e = *it;
             if (monotonic) {
                 if (!android::isMonotonic(e->mRealTime)) {
                     LogKlog::convertRealToMonotonic(e->mRealTime);
+                    if ((e->mRealTime.tv_nsec % 1000) == 0) {
+                        e->mRealTime.tv_nsec++;
+                    }
                 }
             } else {
                 if (android::isMonotonic(e->mRealTime)) {
                     LogKlog::convertMonotonicToReal(e->mRealTime);
+                    if ((e->mRealTime.tv_nsec % 1000) == 0) {
+                        e->mRealTime.tv_nsec++;
+                    }
                 }
             }
             ++it;
         }
-        pthread_mutex_unlock(&mLogElementsLock);
+        unlock();
     }
 
     // We may have been triggered by a SIGHUP. Release any sleeping reader
@@ -95,7 +101,7 @@
     //
     // NB: this is _not_ performed in the context of a SIGHUP, it is
     // performed during startup, and in context of reinit administrative thread
-    LogTimeEntry::lock();
+    LogTimeEntry::wrlock();
 
     LastLogTimes::iterator times = mTimes.begin();
     while (times != mTimes.end()) {
@@ -111,7 +117,7 @@
 
 LogBuffer::LogBuffer(LastLogTimes* times)
     : monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
-    pthread_mutex_init(&mLogElementsLock, nullptr);
+    pthread_rwlock_init(&mLogElementsLock, nullptr);
 
     log_id_for_each(i) {
         lastLoggedElements[i] = nullptr;
@@ -196,6 +202,11 @@
         return -EINVAL;
     }
 
+    // Slip the time by 1 nsec if the incoming lands on xxxxxx000 ns.
+    // This prevents any chance that an outside source can request an
+    // exact entry with time specified in ms or us precision.
+    if ((realtime.tv_nsec % 1000) == 0) ++realtime.tv_nsec;
+
     LogBufferElement* elem =
         new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
     if (log_id != LOG_ID_SECURITY) {
@@ -209,16 +220,15 @@
         }
         if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
             // Log traffic received to total
-            pthread_mutex_lock(&mLogElementsLock);
-            stats.add(elem);
-            stats.subtract(elem);
-            pthread_mutex_unlock(&mLogElementsLock);
+            wrlock();
+            stats.addTotal(elem);
+            unlock();
             delete elem;
             return -EACCES;
         }
     }
 
-    pthread_mutex_lock(&mLogElementsLock);
+    wrlock();
     LogBufferElement* currentLast = lastLoggedElements[log_id];
     if (currentLast) {
         LogBufferElement* dropped = droppedElements[log_id];
@@ -319,15 +329,14 @@
                     // check for overflow
                     if (total >= UINT32_MAX) {
                         log(currentLast);
-                        pthread_mutex_unlock(&mLogElementsLock);
+                        unlock();
                         return len;
                     }
-                    stats.add(currentLast);
-                    stats.subtract(currentLast);
+                    stats.addTotal(currentLast);
                     delete currentLast;
                     swab = total;
                     event->payload.data = htole32(swab);
-                    pthread_mutex_unlock(&mLogElementsLock);
+                    unlock();
                     return len;
                 }
                 if (count == USHRT_MAX) {
@@ -339,13 +348,12 @@
                 }
             }
             if (count) {
-                stats.add(currentLast);
-                stats.subtract(currentLast);
+                stats.addTotal(currentLast);
                 currentLast->setDropped(count);
             }
             droppedElements[log_id] = currentLast;
             lastLoggedElements[log_id] = elem;
-            pthread_mutex_unlock(&mLogElementsLock);
+            unlock();
             return len;
         }
         if (dropped) {         // State 1 or 2
@@ -363,12 +371,12 @@
     lastLoggedElements[log_id] = new LogBufferElement(*elem);
 
     log(elem);
-    pthread_mutex_unlock(&mLogElementsLock);
+    unlock();
 
     return len;
 }
 
-// assumes mLogElementsLock held, owns elem, will look after garbage collection
+// assumes LogBuffer::wrlock() held, owns elem, look after garbage collection
 void LogBuffer::log(LogBufferElement* elem) {
     // cap on how far back we will sort in-place, otherwise append
     static uint32_t too_far_back = 5;  // five seconds
@@ -389,7 +397,7 @@
         bool end_set = false;
         bool end_always = false;
 
-        LogTimeEntry::lock();
+        LogTimeEntry::rdlock();
 
         LastLogTimes::iterator times = mTimes.begin();
         while (times != mTimes.end()) {
@@ -431,7 +439,7 @@
 
 // Prune at most 10% of the log entries or maxPrune, whichever is less.
 //
-// mLogElementsLock must be held when this function is called.
+// LogBuffer::wrlock() must be held when this function is called.
 void LogBuffer::maybePrune(log_id_t id) {
     size_t sizes = stats.sizes(id);
     unsigned long maxSize = log_buffer_size(id);
@@ -610,6 +618,33 @@
     }
 };
 
+// Determine if watermark is within pruneMargin + 1s from the end of the list,
+// the caller will use this result to set an internal busy flag indicating
+// the prune operation could not be completed because a reader is blocking
+// the request.
+bool LogBuffer::isBusy(log_time watermark) {
+    LogBufferElementCollection::iterator ei = mLogElements.end();
+    --ei;
+    return watermark < ((*ei)->getRealTime() - pruneMargin - log_time(1, 0));
+}
+
+// If the selected reader is blocking our pruning progress, decide on
+// what kind of mitigation is necessary to unblock the situation.
+void LogBuffer::kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows) {
+    if (stats.sizes(id) > (2 * log_buffer_size(id))) {  // +100%
+        // A misbehaving or slow reader has its connection
+        // dropped if we hit too much memory pressure.
+        me->release_Locked();
+    } else if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
+        // Allow a blocked WRAP timeout reader to
+        // trigger and start reporting the log data.
+        me->triggerReader_Locked();
+    } else {
+        // tell slow reader to skip entries to catch up
+        me->triggerSkip_Locked(id, pruneRows);
+    }
+}
+
 // prune "pruneRows" of type "id" from the buffer.
 //
 // This garbage collection task is used to expire log entries. It is called to
@@ -655,14 +690,14 @@
 // The third thread is optional, and only gets hit if there was a whitelist
 // and more needs to be pruned against the backstop of the region lock.
 //
-// mLogElementsLock must be held when this function is called.
+// LogBuffer::wrlock() must be held when this function is called.
 //
 bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
     LogTimeEntry* oldest = nullptr;
     bool busy = false;
     bool clearAll = pruneRows == ULONG_MAX;
 
-    LogTimeEntry::lock();
+    LogTimeEntry::rdlock();
 
     // Region locked?
     LastLogTimes::iterator times = mTimes.begin();
@@ -700,12 +735,8 @@
             }
 
             if (oldest && (watermark <= element->getRealTime())) {
-                busy = true;
-                if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
-                    oldest->triggerReader_Locked();
-                } else {
-                    oldest->triggerSkip_Locked(id, pruneRows);
-                }
+                busy = isBusy(watermark);
+                if (busy) kickMe(oldest, id, pruneRows);
                 break;
             }
 
@@ -792,10 +823,8 @@
             LogBufferElement* element = *it;
 
             if (oldest && (watermark <= element->getRealTime())) {
-                busy = true;
-                if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
-                    oldest->triggerReader_Locked();
-                }
+                busy = isBusy(watermark);
+                // Do not let chatty eliding trigger any reader mitigation
                 break;
             }
 
@@ -946,19 +975,8 @@
         }
 
         if (oldest && (watermark <= element->getRealTime())) {
-            busy = true;
-            if (whitelist) {
-                break;
-            }
-
-            if (stats.sizes(id) > (2 * log_buffer_size(id))) {
-                // kick a misbehaving log reader client off the island
-                oldest->release_Locked();
-            } else if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
-                oldest->triggerReader_Locked();
-            } else {
-                oldest->triggerSkip_Locked(id, pruneRows);
-            }
+            busy = isBusy(watermark);
+            if (!whitelist && busy) kickMe(oldest, id, pruneRows);
             break;
         }
 
@@ -990,15 +1008,8 @@
             }
 
             if (oldest && (watermark <= element->getRealTime())) {
-                busy = true;
-                if (stats.sizes(id) > (2 * log_buffer_size(id))) {
-                    // kick a misbehaving log reader client off the island
-                    oldest->release_Locked();
-                } else if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
-                    oldest->triggerReader_Locked();
-                } else {
-                    oldest->triggerSkip_Locked(id, pruneRows);
-                }
+                busy = isBusy(watermark);
+                if (busy) kickMe(oldest, id, pruneRows);
                 break;
             }
 
@@ -1022,15 +1033,15 @@
             // one entry, not another clear run, so we are looking for
             // the quick side effect of the return value to tell us if
             // we have a _blocked_ reader.
-            pthread_mutex_lock(&mLogElementsLock);
+            wrlock();
             busy = prune(id, 1, uid);
-            pthread_mutex_unlock(&mLogElementsLock);
+            unlock();
             // It is still busy, blocked reader(s), lets kill them all!
             // otherwise, lets be a good citizen and preserve the slow
             // readers and let the clear run (below) deal with determining
             // if we are still blocked and return an error code to caller.
             if (busy) {
-                LogTimeEntry::lock();
+                LogTimeEntry::wrlock();
                 LastLogTimes::iterator times = mTimes.begin();
                 while (times != mTimes.end()) {
                     LogTimeEntry* entry = (*times);
@@ -1043,9 +1054,9 @@
                 LogTimeEntry::unlock();
             }
         }
-        pthread_mutex_lock(&mLogElementsLock);
+        wrlock();
         busy = prune(id, ULONG_MAX, uid);
-        pthread_mutex_unlock(&mLogElementsLock);
+        unlock();
         if (!busy || !--retry) {
             break;
         }
@@ -1056,9 +1067,9 @@
 
 // get the used space associated with "id".
 unsigned long LogBuffer::getSizeUsed(log_id_t id) {
-    pthread_mutex_lock(&mLogElementsLock);
+    rdlock();
     size_t retval = stats.sizes(id);
-    pthread_mutex_unlock(&mLogElementsLock);
+    unlock();
     return retval;
 }
 
@@ -1068,17 +1079,17 @@
     if (!__android_logger_valid_buffer_size(size)) {
         return -1;
     }
-    pthread_mutex_lock(&mLogElementsLock);
+    wrlock();
     log_buffer_size(id) = size;
-    pthread_mutex_unlock(&mLogElementsLock);
+    unlock();
     return 0;
 }
 
 // get the total space allocated to "id"
 unsigned long LogBuffer::getSize(log_id_t id) {
-    pthread_mutex_lock(&mLogElementsLock);
+    rdlock();
     size_t retval = log_buffer_size(id);
-    pthread_mutex_unlock(&mLogElementsLock);
+    unlock();
     return retval;
 }
 
@@ -1090,7 +1101,7 @@
     LogBufferElementCollection::iterator it;
     uid_t uid = reader->getUid();
 
-    pthread_mutex_lock(&mLogElementsLock);
+    rdlock();
 
     if (start == log_time::EPOCH) {
         // client wants to start from the beginning
@@ -1111,6 +1122,9 @@
             LogBufferElement* element = *it;
             if (element->getRealTime() > start) {
                 last = it;
+            } else if (element->getRealTime() == start) {
+                last = ++it;
+                break;
             } else if (!--count || (element->getRealTime() < min)) {
                 break;
             }
@@ -1118,7 +1132,7 @@
         it = last;
     }
 
-    log_time max = start;
+    log_time curr = start;
 
     LogBufferElement* lastElement = nullptr;  // iterator corruption paranoia
     static const size_t maxSkip = 4194304;    // maximum entries to skip
@@ -1144,11 +1158,7 @@
             continue;
         }
 
-        if (element->getRealTime() <= start) {
-            continue;
-        }
-
-        // NB: calling out to another object with mLogElementsLock held (safe)
+        // NB: calling out to another object with wrlock() held (safe)
         if (filter) {
             int ret = (*filter)(element, arg);
             if (ret == false) {
@@ -1171,30 +1181,30 @@
                 (element->getDropped() && !sameTid) ? 0 : element->getTid();
         }
 
-        pthread_mutex_unlock(&mLogElementsLock);
+        unlock();
 
         // range locking in LastLogTimes looks after us
-        max = element->flushTo(reader, this, privileged, sameTid);
+        curr = element->flushTo(reader, this, privileged, sameTid);
 
-        if (max == element->FLUSH_ERROR) {
-            return max;
+        if (curr == element->FLUSH_ERROR) {
+            return curr;
         }
 
         skip = maxSkip;
-        pthread_mutex_lock(&mLogElementsLock);
+        rdlock();
     }
-    pthread_mutex_unlock(&mLogElementsLock);
+    unlock();
 
-    return max;
+    return curr;
 }
 
 std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
                                         unsigned int logMask) {
-    pthread_mutex_lock(&mLogElementsLock);
+    wrlock();
 
     std::string ret = stats.format(uid, pid, logMask);
 
-    pthread_mutex_unlock(&mLogElementsLock);
+    unlock();
 
     return ret;
 }
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 1272c20..0942987 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -27,6 +27,7 @@
 #include <sysutils/SocketClient.h>
 
 #include "LogBufferElement.h"
+#include "LogBufferInterface.h"
 #include "LogStatistics.h"
 #include "LogTags.h"
 #include "LogTimes.h"
@@ -74,9 +75,9 @@
 
 typedef std::list<LogBufferElement*> LogBufferElementCollection;
 
-class LogBuffer {
+class LogBuffer : public LogBufferInterface {
     LogBufferElementCollection mLogElements;
-    pthread_mutex_t mLogElementsLock;
+    pthread_rwlock_t mLogElementsLock;
 
     LogStatistics stats;
 
@@ -107,14 +108,14 @@
     LastLogTimes& mTimes;
 
     explicit LogBuffer(LastLogTimes* times);
-    ~LogBuffer();
+    ~LogBuffer() override;
     void init();
     bool isMonotonic() {
         return monotonic;
     }
 
     int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
-            const char* msg, unsigned short len);
+            const char* msg, unsigned short len) override;
     // lastTid is an optional context to help detect if the last previous
     // valid message was from the same source so we can differentiate chatty
     // filter types (identical or expired)
@@ -154,21 +155,27 @@
         return tags.tagToName(tag);
     }
 
-    // helper must be protected directly or implicitly by lock()/unlock()
+    // helper must be protected directly or implicitly by wrlock()/unlock()
     const char* pidToName(pid_t pid) {
         return stats.pidToName(pid);
     }
-    uid_t pidToUid(pid_t pid) {
+    virtual uid_t pidToUid(pid_t pid) override {
         return stats.pidToUid(pid);
     }
+    virtual pid_t tidToPid(pid_t tid) override {
+        return stats.tidToPid(tid);
+    }
     const char* uidToName(uid_t uid) {
         return stats.uidToName(uid);
     }
-    void lock() {
-        pthread_mutex_lock(&mLogElementsLock);
+    void wrlock() {
+        pthread_rwlock_wrlock(&mLogElementsLock);
+    }
+    void rdlock() {
+        pthread_rwlock_rdlock(&mLogElementsLock);
     }
     void unlock() {
-        pthread_mutex_unlock(&mLogElementsLock);
+        pthread_rwlock_unlock(&mLogElementsLock);
     }
 
    private:
@@ -177,6 +184,9 @@
     static const log_time pruneMargin;
 
     void maybePrune(log_id_t id);
+    bool isBusy(log_time watermark);
+    void kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows);
+
     bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
     LogBufferElementCollection::iterator erase(
         LogBufferElementCollection::iterator it, bool coalesce = false);
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 04a620c..381c974 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -121,7 +121,7 @@
     }
 
     static const char format_uid[] = "uid=%u%s%s %s %u line%s";
-    parent->lock();
+    parent->wrlock();
     const char* name = parent->uidToName(mUid);
     parent->unlock();
     const char* commName = android::tidToName(mTid);
@@ -129,7 +129,7 @@
         commName = android::tidToName(mPid);
     }
     if (!commName) {
-        parent->lock();
+        parent->wrlock();
         commName = parent->pidToName(mPid);
         parent->unlock();
     }
diff --git a/libsystem/include/system/window.h b/logd/LogBufferInterface.cpp
similarity index 60%
copy from libsystem/include/system/window.h
copy to logd/LogBufferInterface.cpp
index efa10d6..4b6d363 100644
--- a/libsystem/include/system/window.h
+++ b/logd/LogBufferInterface.cpp
@@ -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.
@@ -14,9 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
-#define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
+#include "LogBufferInterface.h"
+#include "LogUtils.h"
 
-#include <system/window-deprecated.h>
-
-#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
+LogBufferInterface::LogBufferInterface() {
+}
+LogBufferInterface::~LogBufferInterface() {
+}
+uid_t LogBufferInterface::pidToUid(pid_t pid) {
+    return android::pidToUid(pid);
+}
+pid_t LogBufferInterface::tidToPid(pid_t tid) {
+    return android::tidToPid(tid);
+}
diff --git a/logd/LogBufferInterface.h b/logd/LogBufferInterface.h
new file mode 100644
index 0000000..ff73a22
--- /dev/null
+++ b/logd/LogBufferInterface.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012-2014 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 _LOGD_LOG_BUFFER_INTERFACE_H__
+#define _LOGD_LOG_BUFFER_INTERFACE_H__
+
+#include <sys/types.h>
+
+#include <android-base/macros.h>
+#include <log/log_id.h>
+#include <log/log_time.h>
+
+// Abstract interface that handles log when log available.
+class LogBufferInterface {
+   public:
+    LogBufferInterface();
+    virtual ~LogBufferInterface();
+    // Handles a log entry when available in LogListener.
+    // Returns the size of the handled log message.
+    virtual int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
+                    pid_t tid, const char* msg, unsigned short len) = 0;
+
+    virtual uid_t pidToUid(pid_t pid);
+    virtual pid_t tidToPid(pid_t tid);
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(LogBufferInterface);
+};
+
+#endif  // _LOGD_LOG_BUFFER_INTERFACE_H__
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index d23254d..a7e7208 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -579,7 +579,7 @@
     const pid_t tid = pid;
     uid_t uid = AID_ROOT;
     if (pid) {
-        logbuf->lock();
+        logbuf->wrlock();
         uid = logbuf->pidToUid(pid);
         logbuf->unlock();
     }
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index dadc75f..d2df68e 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
 #include <limits.h>
+#include <stdio.h>
 #include <sys/cdefs.h>
 #include <sys/prctl.h>
 #include <sys/socket.h>
@@ -30,7 +32,7 @@
 #include "LogListener.h"
 #include "LogUtils.h"
 
-LogListener::LogListener(LogBuffer* buf, LogReader* reader)
+LogListener::LogListener(LogBufferInterface* buf, LogReader* reader)
     : SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {
 }
 
@@ -72,8 +74,11 @@
         cmsg = CMSG_NXTHDR(&hdr, cmsg);
     }
 
+    struct ucred fake_cred;
     if (cred == NULL) {
-        return false;
+        cred = &fake_cred;
+        cred->pid = 0;
+        cred->uid = DEFAULT_OVERFLOWUID;
     }
 
     if (cred->uid == AID_LOGD) {
@@ -96,17 +101,41 @@
         return false;
     }
 
+    // Check credential validity, acquire corrected details if not supplied.
+    if (cred->pid == 0) {
+        cred->pid = logbuf ? logbuf->tidToPid(header->tid)
+                           : android::tidToPid(header->tid);
+        if (cred->pid == getpid()) {
+            // We expect that /proc/<tid>/ is accessible to self even without
+            // readproc group, so that we will always drop messages that come
+            // from any of our logd threads and their library calls.
+            return false;  // ignore self
+        }
+    }
+    if (cred->uid == DEFAULT_OVERFLOWUID) {
+        uid_t uid =
+            logbuf ? logbuf->pidToUid(cred->pid) : android::pidToUid(cred->pid);
+        if (uid == AID_LOGD) {
+            uid = logbuf ? logbuf->pidToUid(header->tid)
+                         : android::pidToUid(cred->pid);
+        }
+        if (uid != AID_LOGD) cred->uid = uid;
+    }
+
     char* msg = ((char*)buffer) + sizeof(android_log_header_t);
     n -= sizeof(android_log_header_t);
 
     // NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
     // truncated message to the logs.
 
-    if (logbuf->log((log_id_t)header->id, header->realtime, cred->uid,
-                    cred->pid, header->tid, msg,
-                    ((size_t)n <= USHRT_MAX) ? (unsigned short)n : USHRT_MAX) >=
-        0) {
-        reader->notifyNewLog();
+    if (logbuf != nullptr) {
+        int res = logbuf->log(
+            (log_id_t)header->id, header->realtime, cred->uid, cred->pid,
+            header->tid, msg,
+            ((size_t)n <= USHRT_MAX) ? (unsigned short)n : USHRT_MAX);
+        if (res > 0 && reader != nullptr) {
+            reader->notifyNewLog();
+        }
     }
 
     return true;
@@ -116,14 +145,14 @@
     static const char socketName[] = "logdw";
     int sock = android_get_control_socket(socketName);
 
-    if (sock < 0) {
+    if (sock < 0) {  // logd started up in init.sh
         sock = socket_local_server(
             socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM);
-    }
 
-    int on = 1;
-    if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
-        return -1;
+        int on = 1;
+        if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
+            return -1;
+        }
     }
     return sock;
 }
diff --git a/logd/LogListener.h b/logd/LogListener.h
index 2973b8b..a562a54 100644
--- a/logd/LogListener.h
+++ b/logd/LogListener.h
@@ -20,12 +20,22 @@
 #include <sysutils/SocketListener.h>
 #include "LogReader.h"
 
+// DEFAULT_OVERFLOWUID is defined in linux/highuid.h, which is not part of
+// the uapi headers for userspace to use.  This value is filled in on the
+// out-of-band socket credentials if the OS fails to find one available.
+// One of the causes of this is if SO_PASSCRED is set, all the packets before
+// that point will have this value.  We also use it in a fake credential if
+// no socket credentials are supplied.
+#ifndef DEFAULT_OVERFLOWUID
+#define DEFAULT_OVERFLOWUID 65534
+#endif
+
 class LogListener : public SocketListener {
-    LogBuffer* logbuf;
+    LogBufferInterface* logbuf;
     LogReader* reader;
 
    public:
-    LogListener(LogBuffer* buf, LogReader* reader);
+    LogListener(LogBufferInterface* buf, LogReader* reader /* nullable */);
 
    protected:
     virtual bool onDataAvailable(SocketClient* cli);
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index af19279..6d69316 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <ctype.h>
+#include <inttypes.h>
 #include <poll.h>
 #include <sys/prctl.h>
 #include <sys/socket.h>
@@ -110,7 +111,7 @@
     if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
         // Allow writer to get some cycles, and wait for pending notifications
         sched_yield();
-        LogTimeEntry::lock();
+        LogTimeEntry::wrlock();
         LogTimeEntry::unlock();
         sched_yield();
         nonBlock = true;
@@ -192,6 +193,12 @@
         }
     }
 
+    android::prdebug(
+        "logdr: UID=%d GID=%d PID=%d %c tail=%lu logMask=%x pid=%d "
+        "start=%" PRIu64 "ns timeout=%" PRIu64 "ns\n",
+        cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail,
+        logMask, (int)pid, sequence.nsec(), timeout);
+
     FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence, timeout);
 
     // Set acceptable upper limit to wait for slow reader processing b/27242723
@@ -205,7 +212,7 @@
 
 void LogReader::doSocketDelete(SocketClient* cli) {
     LastLogTimes& times = mLogbuf.mTimes;
-    LogTimeEntry::lock();
+    LogTimeEntry::wrlock();
     LastLogTimes::iterator it = times.begin();
     while (it != times.end()) {
         LogTimeEntry* entry = (*it);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index cc30f77..af59ddc 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <string.h>
@@ -23,19 +25,26 @@
 
 #include <list>
 
-#include <android/log.h>
+#include <private/android_logger.h>
 
 #include "LogStatistics.h"
 
+static const uint64_t hourSec = 60 * 60;
+static const uint64_t monthSec = 31 * 24 * hourSec;
+
 size_t LogStatistics::SizesTotal;
 
 LogStatistics::LogStatistics() : enable(false) {
+    log_time now(CLOCK_REALTIME);
     log_id_for_each(id) {
         mSizes[id] = 0;
         mElements[id] = 0;
         mDroppedElements[id] = 0;
         mSizesTotal[id] = 0;
         mElementsTotal[id] = 0;
+        mOldest[id] = now;
+        mNewest[id] = now;
+        mNewestDropped[id] = now;
     }
 }
 
@@ -70,25 +79,56 @@
 }
 }
 
+void LogStatistics::addTotal(LogBufferElement* element) {
+    if (element->getDropped()) return;
+
+    log_id_t log_id = element->getLogId();
+    unsigned short size = element->getMsgLen();
+    mSizesTotal[log_id] += size;
+    SizesTotal += size;
+    ++mElementsTotal[log_id];
+}
+
 void LogStatistics::add(LogBufferElement* element) {
     log_id_t log_id = element->getLogId();
     unsigned short size = element->getMsgLen();
     mSizes[log_id] += size;
     ++mElements[log_id];
 
+    // When caller adding a chatty entry, they will have already
+    // called add() and subtract() for each entry as they are
+    // evaluated and trimmed, thus recording size and number of
+    // elements, but we must recognize the manufactured dropped
+    // entry as not contributing to the lifetime totals.
     if (element->getDropped()) {
         ++mDroppedElements[log_id];
     } else {
-        // When caller adding a chatty entry, they will have already
-        // called add() and subtract() for each entry as they are
-        // evaluated and trimmed, thus recording size and number of
-        // elements, but we must recognize the manufactured dropped
-        // entry as not contributing to the lifetime totals.
         mSizesTotal[log_id] += size;
         SizesTotal += size;
         ++mElementsTotal[log_id];
     }
 
+    log_time stamp(element->getRealTime());
+    if (mNewest[log_id] < stamp) {
+        // A major time update invalidates the statistics :-(
+        log_time diff = stamp - mNewest[log_id];
+        mNewest[log_id] = stamp;
+
+        if (diff.tv_sec > hourSec) {
+            // approximate Do-Your-Best fixup
+            diff += mOldest[log_id];
+            if ((diff > stamp) && ((diff - stamp).tv_sec < hourSec)) {
+                diff = stamp;
+            }
+            if (diff <= stamp) {
+                mOldest[log_id] = diff;
+                if (mNewestDropped[log_id] < diff) {
+                    mNewestDropped[log_id] = diff;
+                }
+            }
+        }
+    }
+
     if (log_id == LOG_ID_KERNEL) {
         return;
     }
@@ -113,6 +153,10 @@
             tagTable.add(tag, element);
         }
     }
+
+    if (!element->getDropped()) {
+        tagNameTable.add(TagNameKey(element), element);
+    }
 }
 
 void LogStatistics::subtract(LogBufferElement* element) {
@@ -124,6 +168,10 @@
         --mDroppedElements[log_id];
     }
 
+    if (mOldest[log_id] < element->getRealTime()) {
+        mOldest[log_id] = element->getRealTime();
+    }
+
     if (log_id == LOG_ID_KERNEL) {
         return;
     }
@@ -148,6 +196,10 @@
             tagTable.subtract(tag, element);
         }
     }
+
+    if (!element->getDropped()) {
+        tagNameTable.subtract(TagNameKey(element), element);
+    }
 }
 
 // Atomically set an entry to drop
@@ -158,6 +210,10 @@
     mSizes[log_id] -= size;
     ++mDroppedElements[log_id];
 
+    if (mNewestDropped[log_id] < element->getRealTime()) {
+        mNewestDropped[log_id] = element->getRealTime();
+    }
+
     uidTable[log_id].drop(element->getUid(), element);
     if (element->getUid() == AID_SYSTEM) {
         pidSystemTable[log_id].drop(element->getPid(), element);
@@ -178,9 +234,12 @@
             tagTable.drop(tag, element);
         }
     }
+
+    tagNameTable.subtract(TagNameKey(element), element);
 }
 
 // caller must own and free character string
+// Requires parent LogBuffer::wrlock() to be held
 const char* LogStatistics::uidToName(uid_t uid) const {
     // Local hard coded favourites
     if (uid == AID_LOGD) {
@@ -248,18 +307,38 @@
                       std::string(isprune ? "NUM" : ""));
 }
 
+// Helper to truncate name, if too long, and add name dressings
+static void formatTmp(const LogStatistics& stat, const char* nameTmp, uid_t uid,
+                      std::string& name, std::string& size, size_t nameLen) {
+    const char* allocNameTmp = nullptr;
+    if (!nameTmp) nameTmp = allocNameTmp = stat.uidToName(uid);
+    if (nameTmp) {
+        size_t lenSpace = std::max(nameLen - name.length(), (size_t)1);
+        size_t len = EntryBaseConstants::total_len -
+                     EntryBaseConstants::pruned_len - size.length() -
+                     name.length() - lenSpace - 2;
+        size_t lenNameTmp = strlen(nameTmp);
+        while ((len < lenNameTmp) && (lenSpace > 1)) {
+            ++len;
+            --lenSpace;
+        }
+        name += android::base::StringPrintf("%*s", (int)lenSpace, "");
+        if (len < lenNameTmp) {
+            name += "...";
+            nameTmp += lenNameTmp - std::max(len - 3, (size_t)1);
+        }
+        name += nameTmp;
+        free(const_cast<char*>(allocNameTmp));
+    }
+}
+
 std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const {
     uid_t uid = getUid();
     std::string name = android::base::StringPrintf("%u", uid);
-    const char* nameTmp = stat.uidToName(uid);
-    if (nameTmp) {
-        name += android::base::StringPrintf(
-            "%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", nameTmp);
-        free(const_cast<char*>(nameTmp));
-    }
-
     std::string size = android::base::StringPrintf("%zu", getSizes());
 
+    formatTmp(stat, nullptr, uid, name, size, 6);
+
     std::string pruned = "";
     if (worstUidEnabledForLogid(id)) {
         size_t totalDropped = 0;
@@ -366,18 +445,10 @@
     uid_t uid = getUid();
     pid_t pid = getPid();
     std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
-    const char* nameTmp = getName();
-    if (nameTmp) {
-        name += android::base::StringPrintf(
-            "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
-    } else if ((nameTmp = stat.uidToName(uid))) {
-        name += android::base::StringPrintf(
-            "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
-        free(const_cast<char*>(nameTmp));
-    }
-
     std::string size = android::base::StringPrintf("%zu", getSizes());
 
+    formatTmp(stat, getName(), uid, name, size, 12);
+
     std::string pruned = "";
     size_t dropped = getDropped();
     if (dropped) {
@@ -398,21 +469,10 @@
                              log_id_t /* id */) const {
     uid_t uid = getUid();
     std::string name = android::base::StringPrintf("%5u/%u", getTid(), uid);
-    const char* nameTmp = getName();
-    if (nameTmp) {
-        name += android::base::StringPrintf(
-            "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
-    } else if ((nameTmp = stat.uidToName(uid))) {
-        // if we do not have a PID name, lets punt to try UID name?
-        name += android::base::StringPrintf(
-            "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
-        free(const_cast<char*>(nameTmp));
-        // We tried, better to not have a name at all, we still
-        // have TID/UID by number to report in any case.
-    }
-
     std::string size = android::base::StringPrintf("%zu", getSizes());
 
+    formatTmp(stat, getName(), uid, name, size, 12);
+
     std::string pruned = "";
     size_t dropped = getDropped();
     if (dropped) {
@@ -456,6 +516,101 @@
     return formatLine(name, size, pruned);
 }
 
+std::string TagNameEntry::formatHeader(const std::string& name,
+                                       log_id_t /* id */) const {
+    return formatLine(name, std::string("Size"), std::string("")) +
+           formatLine(std::string("  TID/PID/UID   LOG_TAG NAME"),
+                      std::string("BYTES"), std::string(""));
+}
+
+std::string TagNameEntry::format(const LogStatistics& /* stat */,
+                                 log_id_t /* id */) const {
+    std::string name;
+    pid_t tid = getTid();
+    pid_t pid = getPid();
+    std::string pidstr;
+    if (pid != (pid_t)-1) {
+        pidstr = android::base::StringPrintf("%u", pid);
+        if ((tid != (pid_t)-1) && (tid != pid)) pidstr = "/" + pidstr;
+    }
+    int len = 9 - pidstr.length();
+    if (len < 0) len = 0;
+    if ((tid == (pid_t)-1) || (tid == pid)) {
+        name = android::base::StringPrintf("%*s", len, "");
+    } else {
+        name = android::base::StringPrintf("%*u", len, tid);
+    }
+    name += pidstr;
+    uid_t uid = getUid();
+    if (uid != (uid_t)-1) {
+        name += android::base::StringPrintf("/%u", uid);
+    }
+
+    std::string size = android::base::StringPrintf("%zu", getSizes());
+
+    const char* nameTmp = getName();
+    if (nameTmp) {
+        size_t lenSpace = std::max(16 - name.length(), (size_t)1);
+        size_t len = EntryBaseConstants::total_len -
+                     EntryBaseConstants::pruned_len - size.length() -
+                     name.length() - lenSpace - 2;
+        size_t lenNameTmp = strlen(nameTmp);
+        while ((len < lenNameTmp) && (lenSpace > 1)) {
+            ++len;
+            --lenSpace;
+        }
+        name += android::base::StringPrintf("%*s", (int)lenSpace, "");
+        if (len < lenNameTmp) {
+            name += "...";
+            nameTmp += lenNameTmp - std::max(len - 3, (size_t)1);
+        }
+        name += nameTmp;
+    }
+
+    std::string pruned = "";
+
+    return formatLine(name, size, pruned);
+}
+
+static std::string formatMsec(uint64_t val) {
+    static const unsigned subsecDigits = 3;
+    static const uint64_t sec = MS_PER_SEC;
+
+    static const uint64_t minute = 60 * sec;
+    static const uint64_t hour = 60 * minute;
+    static const uint64_t day = 24 * hour;
+
+    std::string output;
+    if (val < sec) return output;
+
+    if (val >= day) {
+        output = android::base::StringPrintf("%" PRIu64 "d ", val / day);
+        val = (val % day) + day;
+    }
+    if (val >= minute) {
+        if (val >= hour) {
+            output += android::base::StringPrintf("%" PRIu64 ":",
+                                                  (val / hour) % (day / hour));
+        }
+        output += android::base::StringPrintf(
+            (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
+            (val / minute) % (hour / minute));
+    }
+    output +=
+        android::base::StringPrintf((val >= minute) ? "%02" PRIu64 : "%" PRIu64,
+                                    (val / sec) % (minute / sec));
+    val %= sec;
+    unsigned digits = subsecDigits;
+    while (digits && ((val % 10) == 0)) {
+        val /= 10;
+        --digits;
+    }
+    if (digits) {
+        output += android::base::StringPrintf(".%0*" PRIu64, digits, val);
+    }
+    return output;
+}
+
 std::string LogStatistics::format(uid_t uid, pid_t pid,
                                   unsigned int logMask) const {
     static const unsigned short spaces_total = 19;
@@ -525,6 +680,67 @@
     output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize,
                                           totalEls);
 
+    static const char SpanStr[] = "\nLogspan";
+    spaces = 10 - strlen(SpanStr);
+    output += SpanStr;
+
+    // Total reports the greater of the individual maximum time span, or the
+    // validated minimum start and maximum end time span if it makes sense.
+    uint64_t minTime = UINT64_MAX;
+    uint64_t maxTime = 0;
+    uint64_t maxSpan = 0;
+    totalSize = 0;
+
+    log_id_for_each(id) {
+        if (!(logMask & (1 << id))) continue;
+
+        // validity checking
+        uint64_t oldest = mOldest[id].msec();
+        uint64_t newest = mNewest[id].msec();
+        if (newest <= oldest) {
+            spaces += spaces_total;
+            continue;
+        }
+
+        uint64_t span = newest - oldest;
+        if (span > (monthSec * MS_PER_SEC)) {
+            spaces += spaces_total;
+            continue;
+        }
+
+        // total span
+        if (minTime > oldest) minTime = oldest;
+        if (maxTime < newest) maxTime = newest;
+        if (span > maxSpan) maxSpan = span;
+        totalSize += span;
+
+        uint64_t dropped = mNewestDropped[id].msec();
+        if (dropped < oldest) dropped = oldest;
+        if (dropped > newest) dropped = newest;
+
+        oldLength = output.length();
+        output += android::base::StringPrintf("%*s%s", spaces, "",
+                                              formatMsec(span).c_str());
+        unsigned permille = ((newest - dropped) * 1000 + (span / 2)) / span;
+        if ((permille > 1) && (permille < 999)) {
+            output += android::base::StringPrintf("(%u", permille / 10);
+            permille %= 10;
+            if (permille) {
+                output += android::base::StringPrintf(".%u", permille);
+            }
+            output += android::base::StringPrintf("%%)");
+        }
+        spaces -= output.length() - oldLength;
+        spaces += spaces_total;
+    }
+    if ((maxTime > minTime) && ((maxTime -= minTime) < totalSize) &&
+        (maxTime > maxSpan)) {
+        maxSpan = maxTime;
+    }
+    if (spaces < 0) spaces = 0;
+    output += android::base::StringPrintf("%*s%s", spaces, "",
+                                          formatMsec(maxSpan).c_str());
+
     static const char OverheadStr[] = "\nOverhead";
     spaces = 10 - strlen(OverheadStr);
     output += OverheadStr;
@@ -591,6 +807,13 @@
             securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
     }
 
+    if (enable) {
+        name = "Chattiest TAGs";
+        if (pid) name += android::base::StringPrintf(" for PID %d", pid);
+        name += ":";
+        output += tagNameTable.format(*this, uid, pid, name);
+    }
+
     return output;
 }
 
@@ -602,8 +825,10 @@
     FILE* fp = fopen(buffer, "r");
     if (fp) {
         while (fgets(buffer, sizeof(buffer), fp)) {
-            int uid;
-            if (sscanf(buffer, "Uid: %d", &uid) == 1) {
+            int uid = AID_LOGD;
+            char space = 0;
+            if ((sscanf(buffer, "Uid: %d%c", &uid, &space) == 2) &&
+                isspace(space)) {
                 fclose(fp);
                 return uid;
             }
@@ -612,12 +837,35 @@
     }
     return AID_LOGD;  // associate this with the logger
 }
+
+pid_t tidToPid(pid_t tid) {
+    char buffer[512];
+    snprintf(buffer, sizeof(buffer), "/proc/%u/status", tid);
+    FILE* fp = fopen(buffer, "r");
+    if (fp) {
+        while (fgets(buffer, sizeof(buffer), fp)) {
+            int pid = tid;
+            char space = 0;
+            if ((sscanf(buffer, "Tgid: %d%c", &pid, &space) == 2) &&
+                isspace(space)) {
+                fclose(fp);
+                return pid;
+            }
+        }
+        fclose(fp);
+    }
+    return tid;
+}
 }
 
 uid_t LogStatistics::pidToUid(pid_t pid) {
     return pidTable.add(pid)->second.getUid();
 }
 
+pid_t LogStatistics::tidToPid(pid_t tid) {
+    return tidTable.add(tid)->second.getPid();
+}
+
 // caller must free character string
 const char* LogStatistics::pidToName(pid_t pid) const {
     // An inconvenient truth ... getName() can alter the object
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 066b7de..8808aac 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -18,17 +18,23 @@
 #define _LOGD_LOG_STATISTICS_H__
 
 #include <ctype.h>
+#include <inttypes.h>
+#include <stdint.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/types.h>
 
 #include <algorithm>  // std::max
+#include <experimental/string_view>
 #include <memory>
 #include <string>  // std::string
 #include <unordered_map>
 
 #include <android-base/stringprintf.h>
 #include <android/log.h>
+#include <log/log_time.h>
 #include <private/android_filesystem_config.h>
+#include <utils/FastStrcmp.h>
 
 #include "LogBufferElement.h"
 #include "LogUtils.h"
@@ -76,7 +82,7 @@
     std::unique_ptr<const TEntry* []> sort(uid_t uid, pid_t pid,
                                            size_t len) const {
         if (!len) {
-            std::unique_ptr<const TEntry* []> sorted(NULL);
+            std::unique_ptr<const TEntry* []> sorted(nullptr);
             return sorted;
         }
 
@@ -111,7 +117,7 @@
         return sorted;
     }
 
-    inline iterator add(TKey key, LogBufferElement* element) {
+    inline iterator add(const TKey& key, const LogBufferElement* element) {
         iterator it = map.find(key);
         if (it == map.end()) {
             it = map.insert(std::make_pair(key, TEntry(element))).first;
@@ -131,14 +137,21 @@
         return it;
     }
 
-    void subtract(TKey key, LogBufferElement* element) {
+    void subtract(TKey&& key, const LogBufferElement* element) {
+        iterator it = map.find(std::move(key));
+        if ((it != map.end()) && it->second.subtract(element)) {
+            map.erase(it);
+        }
+    }
+
+    void subtract(const TKey& key, const LogBufferElement* element) {
         iterator it = map.find(key);
         if ((it != map.end()) && it->second.subtract(element)) {
             map.erase(it);
         }
     }
 
-    inline void drop(TKey key, LogBufferElement* element) {
+    inline void drop(TKey key, const LogBufferElement* element) {
         iterator it = map.find(key);
         if (it != map.end()) {
             it->second.drop(element);
@@ -198,17 +211,18 @@
 
     EntryBase() : size(0) {
     }
-    explicit EntryBase(LogBufferElement* element) : size(element->getMsgLen()) {
+    explicit EntryBase(const LogBufferElement* element)
+        : size(element->getMsgLen()) {
     }
 
     size_t getSizes() const {
         return size;
     }
 
-    inline void add(LogBufferElement* element) {
+    inline void add(const LogBufferElement* element) {
         size += element->getMsgLen();
     }
-    inline bool subtract(LogBufferElement* element) {
+    inline bool subtract(const LogBufferElement* element) {
         size -= element->getMsgLen();
         return !size;
     }
@@ -239,7 +253,7 @@
 
     EntryBaseDropped() : dropped(0) {
     }
-    explicit EntryBaseDropped(LogBufferElement* element)
+    explicit EntryBaseDropped(const LogBufferElement* element)
         : EntryBase(element), dropped(element->getDropped()) {
     }
 
@@ -247,15 +261,15 @@
         return dropped;
     }
 
-    inline void add(LogBufferElement* element) {
+    inline void add(const LogBufferElement* element) {
         dropped += element->getDropped();
         EntryBase::add(element);
     }
-    inline bool subtract(LogBufferElement* element) {
+    inline bool subtract(const LogBufferElement* element) {
         dropped -= element->getDropped();
         return EntryBase::subtract(element) && !dropped;
     }
-    inline void drop(LogBufferElement* element) {
+    inline void drop(const LogBufferElement* element) {
         dropped += 1;
         EntryBase::subtract(element);
     }
@@ -265,7 +279,7 @@
     const uid_t uid;
     pid_t pid;
 
-    explicit UidEntry(LogBufferElement* element)
+    explicit UidEntry(const LogBufferElement* element)
         : EntryBaseDropped(element),
           uid(element->getUid()),
           pid(element->getPid()) {
@@ -281,7 +295,7 @@
         return pid;
     }
 
-    inline void add(LogBufferElement* element) {
+    inline void add(const LogBufferElement* element) {
         if (pid != element->getPid()) {
             pid = -1;
         }
@@ -292,10 +306,6 @@
     std::string format(const LogStatistics& stat, log_id_t id) const;
 };
 
-namespace android {
-uid_t pidToUid(pid_t pid);
-}
-
 struct PidEntry : public EntryBaseDropped {
     const pid_t pid;
     uid_t uid;
@@ -307,7 +317,7 @@
           uid(android::pidToUid(pid)),
           name(android::pidToName(pid)) {
     }
-    explicit PidEntry(LogBufferElement* element)
+    explicit PidEntry(const LogBufferElement* element)
         : EntryBaseDropped(element),
           pid(element->getPid()),
           uid(element->getUid()),
@@ -317,7 +327,7 @@
         : EntryBaseDropped(element),
           pid(element.pid),
           uid(element.uid),
-          name(element.name ? strdup(element.name) : NULL) {
+          name(element.name ? strdup(element.name) : nullptr) {
     }
     ~PidEntry() {
         free(name);
@@ -339,14 +349,14 @@
     inline void add(pid_t newPid) {
         if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
             free(name);
-            name = NULL;
+            name = nullptr;
         }
         if (!name) {
             name = android::pidToName(newPid);
         }
     }
 
-    inline void add(LogBufferElement* element) {
+    inline void add(const LogBufferElement* element) {
         uid_t incomingUid = element->getUid();
         if (getUid() != incomingUid) {
             uid = incomingUid;
@@ -375,7 +385,14 @@
           uid(android::pidToUid(tid)),
           name(android::tidToName(tid)) {
     }
-    explicit TidEntry(LogBufferElement* element)
+    TidEntry(pid_t tid)
+        : EntryBaseDropped(),
+          tid(tid),
+          pid(android::tidToPid(tid)),
+          uid(android::pidToUid(tid)),
+          name(android::tidToName(tid)) {
+    }
+    explicit TidEntry(const LogBufferElement* element)
         : EntryBaseDropped(element),
           tid(element->getTid()),
           pid(element->getPid()),
@@ -387,7 +404,7 @@
           tid(element.tid),
           pid(element.pid),
           uid(element.uid),
-          name(element.name ? strdup(element.name) : NULL) {
+          name(element.name ? strdup(element.name) : nullptr) {
     }
     ~TidEntry() {
         free(name);
@@ -412,14 +429,14 @@
     inline void add(pid_t incomingTid) {
         if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
             free(name);
-            name = NULL;
+            name = nullptr;
         }
         if (!name) {
             name = android::tidToName(incomingTid);
         }
     }
 
-    inline void add(LogBufferElement* element) {
+    inline void add(const LogBufferElement* element) {
         uid_t incomingUid = element->getUid();
         pid_t incomingPid = element->getPid();
         if ((getUid() != incomingUid) || (getPid() != incomingPid)) {
@@ -442,7 +459,7 @@
     pid_t pid;
     uid_t uid;
 
-    explicit TagEntry(LogBufferElement* element)
+    explicit TagEntry(const LogBufferElement* element)
         : EntryBaseDropped(element),
           tag(element->getTag()),
           pid(element->getPid()),
@@ -462,7 +479,7 @@
         return android::tagToName(tag);
     }
 
-    inline void add(LogBufferElement* element) {
+    inline void add(const LogBufferElement* element) {
         if (uid != element->getUid()) {
             uid = -1;
         }
@@ -476,6 +493,144 @@
     std::string format(const LogStatistics& stat, log_id_t id) const;
 };
 
+struct TagNameKey {
+    std::string* alloc;
+    std::experimental::string_view name;  // Saves space if const char*
+
+    explicit TagNameKey(const LogBufferElement* element)
+        : alloc(nullptr), name("", strlen("")) {
+        if (element->isBinary()) {
+            uint32_t tag = element->getTag();
+            if (tag) {
+                const char* cp = android::tagToName(tag);
+                if (cp) {
+                    name = std::experimental::string_view(cp, strlen(cp));
+                    return;
+                }
+            }
+            alloc = new std::string(
+                android::base::StringPrintf("[%" PRIu32 "]", tag));
+            if (!alloc) return;
+            name = std::experimental::string_view(alloc->c_str(), alloc->size());
+            return;
+        }
+        const char* msg = element->getMsg();
+        if (!msg) {
+            name = std::experimental::string_view("chatty", strlen("chatty"));
+            return;
+        }
+        ++msg;
+        unsigned short len = element->getMsgLen();
+        len = (len <= 1) ? 0 : strnlen(msg, len - 1);
+        if (!len) {
+            name = std::experimental::string_view("<NULL>", strlen("<NULL>"));
+            return;
+        }
+        alloc = new std::string(msg, len);
+        if (!alloc) return;
+        name = std::experimental::string_view(alloc->c_str(), alloc->size());
+    }
+
+    explicit TagNameKey(TagNameKey&& rval)
+        : alloc(rval.alloc), name(rval.name.data(), rval.name.length()) {
+        rval.alloc = nullptr;
+    }
+
+    explicit TagNameKey(const TagNameKey& rval)
+        : alloc(rval.alloc ? new std::string(*rval.alloc) : nullptr),
+          name(alloc ? alloc->data() : rval.name.data(), rval.name.length()) {
+    }
+
+    ~TagNameKey() {
+        if (alloc) delete alloc;
+    }
+
+    operator const std::experimental::string_view() const {
+        return name;
+    }
+
+    const char* data() const {
+        return name.data();
+    }
+    size_t length() const {
+        return name.length();
+    }
+
+    bool operator==(const TagNameKey& rval) const {
+        if (length() != rval.length()) return false;
+        if (length() == 0) return true;
+        return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
+    }
+    bool operator!=(const TagNameKey& rval) const {
+        return !(*this == rval);
+    }
+
+    size_t getAllocLength() const {
+        return alloc ? alloc->length() + 1 + sizeof(std::string) : 0;
+    }
+};
+
+// Hash for TagNameKey
+template <>
+struct std::hash<TagNameKey>
+    : public std::unary_function<const TagNameKey&, size_t> {
+    size_t operator()(const TagNameKey& __t) const noexcept {
+        if (!__t.length()) return 0;
+        return std::hash<std::experimental::string_view>()(
+            std::experimental::string_view(__t));
+    }
+};
+
+struct TagNameEntry : public EntryBase {
+    pid_t tid;
+    pid_t pid;
+    uid_t uid;
+    TagNameKey name;
+
+    explicit TagNameEntry(const LogBufferElement* element)
+        : EntryBase(element),
+          tid(element->getTid()),
+          pid(element->getPid()),
+          uid(element->getUid()),
+          name(element) {
+    }
+
+    const TagNameKey& getKey() const {
+        return name;
+    }
+    const pid_t& getTid() const {
+        return tid;
+    }
+    const pid_t& getPid() const {
+        return pid;
+    }
+    const uid_t& getUid() const {
+        return uid;
+    }
+    const char* getName() const {
+        return name.data();
+    }
+    size_t getNameAllocLength() const {
+        return name.getAllocLength();
+    }
+
+    inline void add(const LogBufferElement* element) {
+        if (uid != element->getUid()) {
+            uid = -1;
+        }
+        if (pid != element->getPid()) {
+            pid = -1;
+        }
+        if (tid != element->getTid()) {
+            tid = -1;
+        }
+        EntryBase::add(element);
+    }
+
+    std::string formatHeader(const std::string& name, log_id_t id) const;
+    std::string format(const LogStatistics& stat, log_id_t id) const;
+};
+
 template <typename TEntry>
 class LogFindWorst {
     std::unique_ptr<const TEntry* []> sorted;
@@ -520,6 +675,9 @@
     size_t mDroppedElements[LOG_ID_MAX];
     size_t mSizesTotal[LOG_ID_MAX];
     size_t mElementsTotal[LOG_ID_MAX];
+    log_time mOldest[LOG_ID_MAX];
+    log_time mNewest[LOG_ID_MAX];
+    log_time mNewestDropped[LOG_ID_MAX];
     static size_t SizesTotal;
     bool enable;
 
@@ -546,9 +704,14 @@
     // security tag list
     tagTable_t securityTagTable;
 
+    // global tag list
+    typedef LogHashtable<TagNameKey, TagNameEntry> tagNameTable_t;
+    tagNameTable_t tagNameTable;
+
     size_t sizeOf() const {
         size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() +
                       tagTable.sizeOf() + securityTagTable.sizeOf() +
+                      tagNameTable.sizeOf() +
                       (pidTable.size() * sizeof(pidTable_t::iterator)) +
                       (tagTable.size() * sizeof(tagTable_t::iterator));
         for (auto it : pidTable) {
@@ -559,6 +722,7 @@
             const char* name = it.second.getName();
             if (name) size += strlen(name) + 1;
         }
+        for (auto it : tagNameTable) size += it.second.getNameAllocLength();
         log_id_for_each(id) {
             size += uidTable[id].sizeOf();
             size += uidTable[id].size() * sizeof(uidTable_t::iterator);
@@ -576,6 +740,7 @@
         enable = true;
     }
 
+    void addTotal(LogBufferElement* entry);
     void add(LogBufferElement* entry);
     void subtract(LogBufferElement* entry);
     // entry->setDropped(1) must follow this call
@@ -623,6 +788,7 @@
     // helper (must be locked directly or implicitly by mLogElementsLock)
     const char* pidToName(pid_t pid) const;
     uid_t pidToUid(pid_t pid);
+    pid_t tidToPid(pid_t tid);
     const char* uidToName(uid_t uid) const;
 };
 
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index ccc550a..25c2ad2 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -78,7 +78,7 @@
 void LogTimeEntry::threadStop(void* obj) {
     LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
 
-    lock();
+    wrlock();
 
     if (me->mNonBlock) {
         me->error_Locked();
@@ -134,7 +134,7 @@
 
     me->leadingDropped = true;
 
-    lock();
+    wrlock();
 
     log_time start = me->mStart;
 
@@ -160,7 +160,7 @@
         start = logbuf.flushTo(client, start, me->mLastTid, privileged,
                                security, FilterSecondPass, me);
 
-        lock();
+        wrlock();
 
         if (start == LogBufferElement::FLUSH_ERROR) {
             me->error_Locked();
@@ -191,7 +191,7 @@
 int LogTimeEntry::FilterFirstPass(const LogBufferElement* element, void* obj) {
     LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
 
-    LogTimeEntry::lock();
+    LogTimeEntry::wrlock();
 
     if (me->leadingDropped) {
         if (element->getDropped()) {
@@ -219,7 +219,7 @@
 int LogTimeEntry::FilterSecondPass(const LogBufferElement* element, void* obj) {
     LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
 
-    LogTimeEntry::lock();
+    LogTimeEntry::wrlock();
 
     me->mStart = element->getRealTime();
 
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index ec8252e..9ca2aea 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -61,7 +61,10 @@
     const log_time mEnd;  // only relevant if mNonBlock
 
     // Protect List manipulations
-    static void lock(void) {
+    static void wrlock(void) {
+        pthread_mutex_lock(&timesLock);
+    }
+    static void rdlock(void) {
         pthread_mutex_lock(&timesLock);
     }
     static void unlock(void) {
@@ -104,7 +107,7 @@
         mError = true;
     }
     void error(void) {
-        lock();
+        wrlock();
         error_Locked();
         unlock();
     }
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index fa9f398..4dcd3e7 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -38,6 +38,8 @@
 // Caller must own and free returned value
 char* pidToName(pid_t pid);
 char* tidToName(pid_t tid);
+uid_t pidToUid(pid_t pid);
+pid_t tidToPid(pid_t tid);
 
 // Furnished in LogTags.cpp. Thread safe.
 const char* tagToName(uint32_t tag);
diff --git a/logd/event.logtags b/logd/event.logtags
index 39063a9..fa13a62 100644
--- a/logd/event.logtags
+++ b/logd/event.logtags
@@ -29,6 +29,7 @@
 # 4: Number of allocations
 # 5: Id
 # 6: Percent
+# s: Number of seconds (monotonic time)
 # Default value for data of type int/long is 2 (bytes).
 #
 # TODO: generate ".java" and ".h" files with integer constants from this file.
diff --git a/logd/logd.rc b/logd/logd.rc
index ee89b83..8804246 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -1,11 +1,11 @@
 service logd /system/bin/logd
     socket logd stream 0666 logd logd
     socket logdr seqpacket 0666 logd logd
-    socket logdw dgram 0222 logd logd
+    socket logdw dgram+passcred 0222 logd logd
     file /proc/kmsg r
     file /dev/kmsg w
     user logd
-    group logd system readproc
+    group logd system package_info readproc
     writepid /dev/cpuset/system-background/tasks
 
 service logd-reinit /system/bin/logd --reinit
diff --git a/logd/main.cpp b/logd/main.cpp
index 946485b..c8183f0 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -240,23 +240,36 @@
     set_sched_policy(0, SP_BACKGROUND);
     setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND);
 
+    // We should drop to AID_LOGD, if we are anything else, we have
+    // even lesser privileges and accept our fate.
+    gid_t groups[] = {
+        AID_SYSTEM,        // search access to /data/system path
+        AID_PACKAGE_INFO,  // readonly access to /data/system/packages.list
+    };
+    if (setgroups(arraysize(groups), groups) == -1) {
+        android::prdebug(
+            "logd.daemon: failed to set AID_SYSTEM AID_PACKAGE_INFO groups");
+    }
+    if (setgid(AID_LOGD) != 0) {
+        android::prdebug("logd.daemon: failed to set AID_LOGD gid");
+    }
+    if (setuid(AID_LOGD) != 0) {
+        android::prdebug("logd.daemon: failed to set AID_LOGD uid");
+    }
+
     cap_t caps = cap_init();
     (void)cap_clear(caps);
     (void)cap_set_proc(caps);
     (void)cap_free(caps);
 
-    // If we are AID_ROOT, we should drop to AID_LOGD+AID_SYSTEM, if we are
-    // anything else, we have even lesser privileges and accept our fate. Not
-    // worth checking for error returns setting this thread's privileges.
-    (void)setgid(AID_SYSTEM);  // readonly access to /data/system/packages.list
-    (void)setuid(AID_LOGD);    // access to everything logd, eg /data/misc/logd
-
     while (reinit_running && !sem_wait(&reinit) && reinit_running) {
         // uidToName Privileged Worker
         if (uid) {
             name = nullptr;
 
-            packagelist_parse(package_list_parser_cb, nullptr);
+            // if we got the perms wrong above, this would spam if we reported
+            // problems with acquisition of an uid name from the packages.
+            (void)packagelist_parse(package_list_parser_cb, nullptr);
 
             uid = 0;
             sem_post(&uidName);
@@ -407,6 +420,11 @@
 // logging plugins like auditd and restart control. Additional
 // transitory per-client threads are created for each reader.
 int main(int argc, char* argv[]) {
+    // logd is written under the assumption that the timezone is UTC.
+    // If TZ is not set, persist.sys.timezone is looked up in some time utility
+    // libc functions, including mktime. It confuses the logd time handling,
+    // so here explicitly set TZ to UTC, which overrides the property.
+    setenv("TZ", "UTC", 1);
     // issue reinit command. KISS argument parsing.
     if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {
         return issueReinit();
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
index c053993..1915677 100644
--- a/logd/tests/Android.mk
+++ b/logd/tests/Android.mk
@@ -49,3 +49,38 @@
 LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
 LOCAL_SRC_FILES := $(test_src_files)
 include $(BUILD_NATIVE_TEST)
+
+cts_executable := CtsLogdTestCases
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(cts_executable)
+LOCAL_MODULE_TAGS := tests
+LOCAL_CFLAGS += $(test_c_flags)
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
+LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
+LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_CTS_TEST_PACKAGE := android.core.logd
+include $(BUILD_CTS_EXECUTABLE)
+
+ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(cts_executable)_list
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := $(test_c_flags) -DHOST
+LOCAL_C_INCLUDES := external/gtest/include
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_CXX_STL := libc++
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_HOST_NATIVE_TEST)
+
+endif  # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
diff --git a/logd/tests/AndroidTest.xml b/logd/tests/AndroidTest.xml
new file mode 100644
index 0000000..8704611
--- /dev/null
+++ b/logd/tests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for CTS Logging Daemon test cases">
+    <option name="config-descriptor:metadata" key="component" value="systems" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsLogdTestCases->/data/local/tmp/CtsLogdTestCases" />
+        <option name="append-bitness" value="true" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="CtsLogdTestCases" />
+        <option name="runtime-hint" value="65s" />
+    </test>
+</configuration>
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index c81aa32..cd80212 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <poll.h>
@@ -26,12 +27,13 @@
 
 #include <string>
 
+#include <android-base/file.h>
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <cutils/sockets.h>
 #include <gtest/gtest.h>
-#include <log/log.h>
 #include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
 #ifdef __ANDROID__
 #include <selinux/selinux.h>
 #endif
@@ -39,6 +41,7 @@
 #include "../LogReader.h"  // pickup LOGD_SNDTIMEO
 #include "../libaudit.h"   // pickup AUDIT_RATE_LIMIT_*
 
+#ifdef __ANDROID__
 static void send_to_control(char* buf, size_t len) {
     int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
                                    SOCK_STREAM);
@@ -84,7 +87,7 @@
         size_t ret = atol(buf) + 1;
         if (ret < 4) {
             delete[] buf;
-            buf = NULL;
+            buf = nullptr;
             break;
         }
         bool check = ret <= len;
@@ -106,7 +109,7 @@
     // UID   PACKAGE                                                BYTES LINES
     // 0     root                                                  54164 147569
     //
-    char* benchmark = NULL;
+    char* benchmark = nullptr;
     do {
         static const char signature[] = "\n0     root ";
 
@@ -121,7 +124,7 @@
         benchmark = cp;
 #ifdef DEBUG
         char* end = strstr(benchmark, "\n");
-        if (end == NULL) {
+        if (end == nullptr) {
             end = benchmark + strlen(benchmark);
         }
         fprintf(stderr, "parse for spam counter in \"%.*s\"\n",
@@ -153,18 +156,25 @@
         if (value > 10UL) {
             break;
         }
-        benchmark = NULL;
+        benchmark = nullptr;
     } while (*cp);
     return benchmark;
 }
+#endif
 
 TEST(logd, statistics) {
+#ifdef __ANDROID__
     size_t len;
     char* buf;
 
+    // Drop cache so that any access problems can be discovered.
+    if (!android::base::WriteStringToFile("3\n", "/proc/sys/vm/drop_caches")) {
+        GTEST_LOG_(INFO) << "Could not open trigger dropping inode cache";
+    }
+
     alloc_statistics(&buf, &len);
 
-    ASSERT_TRUE(NULL != buf);
+    ASSERT_TRUE(nullptr != buf);
 
     // remove trailing FF
     char* cp = buf + len - 1;
@@ -189,23 +199,46 @@
     EXPECT_EQ(0, truncated);
 
     char* main_logs = strstr(cp, "\nChattiest UIDs in main ");
-    EXPECT_TRUE(NULL != main_logs);
+    EXPECT_TRUE(nullptr != main_logs);
 
     char* radio_logs = strstr(cp, "\nChattiest UIDs in radio ");
     if (!radio_logs)
-        GTEST_LOG_(INFO) << "Value of: NULL != radio_logs\n"
+        GTEST_LOG_(INFO) << "Value of: nullptr != radio_logs\n"
                             "Actual: false\n"
                             "Expected: false\n";
 
     char* system_logs = strstr(cp, "\nChattiest UIDs in system ");
-    EXPECT_TRUE(NULL != system_logs);
+    EXPECT_TRUE(nullptr != system_logs);
 
     char* events_logs = strstr(cp, "\nChattiest UIDs in events ");
-    EXPECT_TRUE(NULL != events_logs);
+    EXPECT_TRUE(nullptr != events_logs);
+
+    // Check if there is any " u0_a#### " as this means packagelistparser broken
+    char* used_getpwuid = nullptr;
+    int used_getpwuid_len;
+    char* uid_name = cp;
+    static const char getpwuid_prefix[] = " u0_a";
+    while ((uid_name = strstr(uid_name, getpwuid_prefix)) != nullptr) {
+        used_getpwuid = uid_name + 1;
+        uid_name += strlen(getpwuid_prefix);
+        while (isdigit(*uid_name)) ++uid_name;
+        used_getpwuid_len = uid_name - used_getpwuid;
+        if (isspace(*uid_name)) break;
+        used_getpwuid = nullptr;
+    }
+    EXPECT_TRUE(nullptr == used_getpwuid);
+    if (used_getpwuid) {
+        fprintf(stderr, "libpackagelistparser failed to pick up %.*s\n",
+                used_getpwuid_len, used_getpwuid);
+    }
 
     delete[] buf;
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
 }
 
+#ifdef __ANDROID__
 static void caught_signal(int /* signum */) {
 }
 
@@ -315,8 +348,10 @@
     fprintf(stderr, "}\n");
     fflush(stderr);
 }
+#endif
 
 TEST(logd, both) {
+#ifdef __ANDROID__
     log_msg msg;
 
     // check if we can read any logs from logd
@@ -343,7 +378,7 @@
         }
 
         alarm(old_alarm);
-        sigaction(SIGALRM, &old_sigaction, NULL);
+        sigaction(SIGALRM, &old_sigaction, nullptr);
 
         close(fd);
     }
@@ -390,8 +425,12 @@
     EXPECT_EQ(0, !user_logger_available && !kernel_logger_available);
     EXPECT_EQ(0, user_logger_content && kernel_logger_content);
     EXPECT_EQ(0, !user_logger_content && !kernel_logger_content);
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
 }
 
+#ifdef __ANDROID__
 // BAD ROBOT
 //   Benchmark threshold are generally considered bad form unless there is
 //   is some human love applied to the continued maintenance and whether the
@@ -421,12 +460,12 @@
 
     // Introduce some extreme spam for the worst UID filter
     ASSERT_TRUE(
-        NULL !=
+        nullptr !=
         (fp = popen("/data/nativetest/liblog-benchmarks/liblog-benchmarks"
                     " BM_log_maximum_retry"
                     " BM_log_maximum"
                     " BM_clock_overhead"
-                    " BM_log_overhead"
+                    " BM_log_print_overhead"
                     " BM_log_latency"
                     " BM_log_delay",
                     "r")));
@@ -434,13 +473,13 @@
     char buffer[5120];
 
     static const char* benchmarks[] = {
-        "BM_log_maximum_retry ", "BM_log_maximum ", "BM_clock_overhead ",
-        "BM_log_overhead ",      "BM_log_latency ", "BM_log_delay "
+        "BM_log_maximum_retry ",  "BM_log_maximum ", "BM_clock_overhead ",
+        "BM_log_print_overhead ", "BM_log_latency ", "BM_log_delay "
     };
     static const unsigned int log_maximum_retry = 0;
     static const unsigned int log_maximum = 1;
     static const unsigned int clock_overhead = 2;
-    static const unsigned int log_overhead = 3;
+    static const unsigned int log_print_overhead = 3;
     static const unsigned int log_latency = 4;
     static const unsigned int log_delay = 5;
 
@@ -469,31 +508,33 @@
     }
 
     EXPECT_GE(200000UL, ns[log_maximum_retry]);  // 104734 user
+    EXPECT_NE(0UL, ns[log_maximum_retry]);       // failure to parse
 
     EXPECT_GE(90000UL, ns[log_maximum]);  // 46913 user
+    EXPECT_NE(0UL, ns[log_maximum]);      // failure to parse
 
     EXPECT_GE(4096UL, ns[clock_overhead]);  // 4095
+    EXPECT_NE(0UL, ns[clock_overhead]);     // failure to parse
 
-    EXPECT_GE(250000UL, ns[log_overhead]);  // 126886 user
+    EXPECT_GE(250000UL, ns[log_print_overhead]);  // 126886 user
+    EXPECT_NE(0UL, ns[log_print_overhead]);       // failure to parse
 
     EXPECT_GE(10000000UL,
               ns[log_latency]);  // 1453559 user space (background cgroup)
+    EXPECT_NE(0UL, ns[log_latency]);  // failure to parse
 
     EXPECT_GE(20000000UL, ns[log_delay]);  // 10500289 user
-
-    for (unsigned i = 0; i < arraysize(ns); ++i) {
-        EXPECT_NE(0UL, ns[i]);
-    }
+    EXPECT_NE(0UL, ns[log_delay]);         // failure to parse
 
     alloc_statistics(&buf, &len);
 
     bool collected_statistics = !!buf;
     EXPECT_EQ(true, collected_statistics);
 
-    ASSERT_TRUE(NULL != buf);
+    ASSERT_TRUE(nullptr != buf);
 
     char* benchmark_statistics_found = find_benchmark_spam(buf);
-    ASSERT_TRUE(benchmark_statistics_found != NULL);
+    ASSERT_TRUE(benchmark_statistics_found != nullptr);
 
     // Check how effective the SPAM filter is, parse out Now size.
     // 0     root                      54164 147569
@@ -558,9 +599,11 @@
     // 50% threshold for SPAM filter (<20% typical, lots of engineering margin)
     ASSERT_GT(totalSize, nowSpamSize * 2);
 }
+#endif
 
 // b/26447386 confirm fixed
 void timeout_negative(const char* command) {
+#ifdef __ANDROID__
     log_msg msg_wrap, msg_timeout;
     bool content_wrap = false, content_timeout = false, written = false;
     unsigned int alarm_wrap = 0, alarm_timeout = 0;
@@ -586,7 +629,7 @@
         written = write(fd, ask.c_str(), len) == (ssize_t)len;
         if (!written) {
             alarm(old_alarm);
-            sigaction(SIGALRM, &old_sigaction, NULL);
+            sigaction(SIGALRM, &old_sigaction, nullptr);
             close(fd);
             continue;
         }
@@ -608,7 +651,7 @@
                                    : (old_alarm > (1 + 3 - alarm_wrap))
                                          ? old_alarm - 3 + alarm_wrap
                                          : 2);
-        sigaction(SIGALRM, &old_sigaction, NULL);
+        sigaction(SIGALRM, &old_sigaction, nullptr);
 
         close(fd);
 
@@ -630,6 +673,10 @@
     EXPECT_NE(0U, alarm_wrap);
     EXPECT_TRUE(content_timeout);
     EXPECT_NE(0U, alarm_timeout);
+#else
+    command = nullptr;
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
 }
 
 TEST(logd, timeout_no_start) {
@@ -643,6 +690,7 @@
 
 // b/26447386 refined behavior
 TEST(logd, timeout) {
+#ifdef __ANDROID__
     // b/33962045 This test interferes with other log reader tests that
     // follow because of file descriptor socket persistence in the same
     // process.  So let's fork it to isolate it from giving us pain.
@@ -691,7 +739,7 @@
         written = write(fd, ask.c_str(), len) == (ssize_t)len;
         if (!written) {
             alarm(old_alarm);
-            sigaction(SIGALRM, &old_sigaction, NULL);
+            sigaction(SIGALRM, &old_sigaction, nullptr);
             close(fd);
             continue;
         }
@@ -713,7 +761,7 @@
                                    : (old_alarm > (1 + 3 - alarm_wrap))
                                          ? old_alarm - 3 + alarm_wrap
                                          : 2);
-        sigaction(SIGALRM, &old_sigaction, NULL);
+        sigaction(SIGALRM, &old_sigaction, nullptr);
 
         close(fd);
 
@@ -766,10 +814,14 @@
 
     _exit(!written + content_wrap + alarm_wrap + !content_timeout +
           !alarm_timeout);
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
 }
 
 // b/27242723 confirmed fixed
 TEST(logd, SNDTIMEO) {
+#ifdef __ANDROID__
     static const unsigned sndtimeo =
         LOGD_SNDTIMEO;  // <sigh> it has to be done!
     static const unsigned sleep_time = sndtimeo + 3;
@@ -811,7 +863,7 @@
     int save_errno = (recv_ret < 0) ? errno : 0;
 
     EXPECT_NE(0U, alarm(old_alarm));
-    sigaction(SIGALRM, &old_sigaction, NULL);
+    sigaction(SIGALRM, &old_sigaction, nullptr);
 
     EXPECT_EQ(0, recv_ret);
     if (recv_ret > 0) {
@@ -820,6 +872,9 @@
     EXPECT_EQ(0, save_errno);
 
     close(fd);
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
 }
 
 TEST(logd, getEventTag_list) {
@@ -847,8 +902,8 @@
     char* cp;
     long ret = strtol(buffer, &cp, 10);
     EXPECT_GT(ret, 16);
-    EXPECT_TRUE(strstr(buffer, "\t(to life the universe etc|3)") != NULL);
-    EXPECT_TRUE(strstr(buffer, "answer") != NULL);
+    EXPECT_TRUE(strstr(buffer, "\t(to life the universe etc|3)") != nullptr);
+    EXPECT_TRUE(strstr(buffer, "answer") != nullptr);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
@@ -868,8 +923,8 @@
     char* cp;
     long ret = strtol(buffer, &cp, 10);
     EXPECT_GT(ret, 16);
-    EXPECT_TRUE(strstr(buffer, "\t(new|1)") != NULL);
-    EXPECT_TRUE(strstr(buffer, name) != NULL);
+    EXPECT_TRUE(strstr(buffer, "\t(new|1)") != nullptr);
+    EXPECT_TRUE(strstr(buffer, name) != nullptr);
 // ToDo: also look for this in /data/misc/logd/event-log-tags and
 // /dev/event-log-tags.
 #else
@@ -877,11 +932,14 @@
 #endif
 }
 
+#ifdef __ANDROID__
 static inline int32_t get4LE(const char* src) {
     return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
 }
+#endif
 
 void __android_log_btwrite_multiple__helper(int count) {
+#ifdef __ANDROID__
     log_time ts(CLOCK_MONOTONIC);
 
     log_time ts1(CLOCK_MONOTONIC);
@@ -910,7 +968,7 @@
     ASSERT_EQ(0, info.si_status);
 
     struct logger_list* logger_list;
-    ASSERT_TRUE(NULL !=
+    ASSERT_TRUE(nullptr !=
                 (logger_list = android_logger_list_open(
                      LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
                      0, pid)));
@@ -974,6 +1032,10 @@
     EXPECT_EQ(expected_chatty_count, chatty_count);
     EXPECT_EQ(expected_identical_count, identical_count);
     EXPECT_EQ(expected_expire_count, expire_count);
+#else
+    count = 0;
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
 }
 
 TEST(logd, multiple_test_1) {
@@ -999,22 +1061,24 @@
 
     if (pid) {
         siginfo_t info = {};
-        if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) return 0;
-        if (info.si_status) return 0;
+        if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) return -1;
+        if (info.si_status) return -1;
         return pid;
     }
 
     // We may have DAC, but let's not have MAC
-    if (setcon("u:object_r:shell:s0") < 0) {
+    if ((setcon("u:object_r:shell:s0") < 0) && (setcon("u:r:shell:s0") < 0)) {
         int save_errno = errno;
         security_context_t context;
         getcon(&context);
-        fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n", context,
-                strerror(save_errno));
-        freecon(context);
-        _exit(-1);
-        // NOTREACHED
-        return 0;
+        if (strcmp(context, "u:r:shell:s0")) {
+            fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n",
+                    context, strerror(save_errno));
+            freecon(context);
+            _exit(-1);
+            // NOTREACHED
+            return -1;
+        }
     }
 
     // The key here is we are root, but we are in u:r:shell:s0,
@@ -1042,25 +1106,39 @@
         if (access(android::base::StringPrintf(file, num).c_str(), F_OK) == 0) {
             _exit(-1);
             // NOTREACHED
-            return 0;
+            return -1;
         }
         usleep(usec);
         --num;
     }
     _exit(0);
     // NOTREACHED
-    return 0;
+    return -1;
 }
 
+static constexpr int background_period = 10;
+
 static int count_avc(pid_t pid) {
     int count = 0;
 
-    if (pid == 0) return count;
+    // pid=-1 skip as pid is in error
+    if (pid == (pid_t)-1) return count;
 
-    struct logger_list* logger_list;
-    if (!(logger_list = android_logger_list_open(
-              LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, pid)))
+    // pid=0 means we want to report the background count of avc: activities
+    struct logger_list* logger_list =
+        pid ? android_logger_list_alloc(
+                  ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, pid)
+            : android_logger_list_alloc_time(
+                  ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+                  log_time(android_log_clockid()) -
+                      log_time(background_period, 0),
+                  0);
+    if (!logger_list) return count;
+    struct logger* logger = android_logger_open(logger_list, LOG_ID_EVENTS);
+    if (!logger) {
+        android_logger_list_close(logger_list);
         return count;
+    }
     for (;;) {
         log_msg log_msg;
 
@@ -1092,56 +1170,64 @@
 }
 #endif
 
-TEST(logd, sepolicy_rate_limiter_maximum) {
+TEST(logd, sepolicy_rate_limiter) {
 #ifdef __ANDROID__
-    static const int rate = AUDIT_RATE_LIMIT_MAX;
-    static const int duration = 2;
-    // Two seconds of a liveable sustained rate
-    EXPECT_EQ(rate * duration, count_avc(sepolicy_rate(rate, rate * duration)));
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
-TEST(logd, sepolicy_rate_limiter_sub_burst) {
-#ifdef __ANDROID__
-    // maximum period below half way between sustainable and burst rate.
-    static const int threshold =
-        ((AUDIT_RATE_LIMIT_BURST_DURATION *
-          (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) +
-         1) /
-        2;
-    static const int rate = (threshold / AUDIT_RATE_LIMIT_BURST_DURATION) - 1;
-    static const int duration = AUDIT_RATE_LIMIT_BURST_DURATION;
-    EXPECT_EQ(rate * duration, count_avc(sepolicy_rate(rate, rate * duration)));
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
-TEST(logd, sepolicy_rate_limiter_spam) {
-#ifdef __ANDROID__
-    // maximum period of double the maximum burst rate
-    static const int threshold =
-        ((AUDIT_RATE_LIMIT_BURST_DURATION *
-          (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) +
-         1) /
-        2;
-    static const int rate = AUDIT_RATE_LIMIT_DEFAULT * 2;
-    static const int duration = threshold / AUDIT_RATE_LIMIT_DEFAULT;
-    EXPECT_GE(
-        ((AUDIT_RATE_LIMIT_DEFAULT * duration) * 115) / 100,  // +15% margin
-        count_avc(sepolicy_rate(rate, rate * duration)));
-    // give logd another 3 seconds to react to the burst before checking
-    sepolicy_rate(rate, rate * 3);
-    // maximum period at double the maximum burst rate (spam filter kicked in)
-    EXPECT_GE(
-        threshold * 2,
-        count_avc(sepolicy_rate(rate, rate * AUDIT_RATE_LIMIT_BURST_DURATION)));
-    // cool down, and check unspammy rate still works
-    sleep(2);
-    EXPECT_LE(AUDIT_RATE_LIMIT_BURST_DURATION - 1,  // allow _one_ to be lost
-              count_avc(sepolicy_rate(1, AUDIT_RATE_LIMIT_BURST_DURATION)));
+    int background_selinux_activity_too_high = count_avc(0);
+    if (background_selinux_activity_too_high > 2) {
+        GTEST_LOG_(ERROR) << "Too much background selinux activity "
+                          << background_selinux_activity_too_high * 60 /
+                                 background_period
+                          << "/minute on the device, this test\n"
+                          << "can not measure the functionality of the "
+                          << "sepolicy rate limiter.  Expect test to\n"
+                          << "fail as this device is in a bad state, "
+                          << "but is not strictly a unit test failure.";
+    }
+    // sepolicy_rate_limiter_maximum
+    {  // maximum precharch test block.
+        static constexpr int rate = AUDIT_RATE_LIMIT_MAX;
+        static constexpr int duration = 2;
+        // Two seconds of a liveable sustained rate
+        EXPECT_EQ(rate * duration,
+                  count_avc(sepolicy_rate(rate, rate * duration)));
+    }
+    // sepolicy_rate_limiter_sub_burst
+    {  // maximum period below half way between sustainable and burst rate
+        static constexpr int threshold =
+            ((AUDIT_RATE_LIMIT_BURST_DURATION *
+              (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) +
+             1) /
+            2;
+        static constexpr int rate =
+            (threshold / AUDIT_RATE_LIMIT_BURST_DURATION) - 1;
+        static constexpr int duration = AUDIT_RATE_LIMIT_BURST_DURATION;
+        EXPECT_EQ(rate * duration,
+                  count_avc(sepolicy_rate(rate, rate * duration)));
+    }
+    // sepolicy_rate_limiter_spam
+    {  // hit avc: hard beyond reason block.
+        // maximum period of double the maximum burst rate
+        static constexpr int threshold =
+            ((AUDIT_RATE_LIMIT_BURST_DURATION *
+              (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) +
+             1) /
+            2;
+        static constexpr int rate = AUDIT_RATE_LIMIT_DEFAULT * 2;
+        static constexpr int duration = threshold / AUDIT_RATE_LIMIT_DEFAULT;
+        EXPECT_GE(
+            ((AUDIT_RATE_LIMIT_DEFAULT * duration) * 115) / 100,  // +15% margin
+            count_avc(sepolicy_rate(rate, rate * duration)));
+        // give logd another 3 seconds to react to the burst before checking
+        sepolicy_rate(rate, rate * 3);
+        // maximum period at double maximum burst rate (spam filter kicked in)
+        EXPECT_GE(threshold * 2,
+                  count_avc(sepolicy_rate(
+                      rate, rate * AUDIT_RATE_LIMIT_BURST_DURATION)));
+        // cool down, and check unspammy rate still works
+        sleep(2);
+        EXPECT_LE(AUDIT_RATE_LIMIT_BURST_DURATION - 1,  // allow _one_ lost
+                  count_avc(sepolicy_rate(1, AUDIT_RATE_LIMIT_BURST_DURATION)));
+    }
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/platform_tools_tool_version.mk b/platform_tools_tool_version.mk
new file mode 100644
index 0000000..eed2ab5
--- /dev/null
+++ b/platform_tools_tool_version.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2017 Google Inc.
+#
+# 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.
+
+# We rewrite ${PLATFORM_SDK_VERSION} with 0 rather than $(PLATFORM_SDK_VERSION)
+# because on the actual platform tools release branches the file contains a
+# literal instead. Using 0 lets us easily distinguish non-canonical builds.
+platform_tools_version := $(shell sed \
+    's/$${PLATFORM_SDK_VERSION}/0/ ; s/^Pkg.Revision=\(.*\)/\1/p ; d' \
+    development/sdk/plat_tools_source.prop_template \
+  )
+tool_version := $(platform_tools_version)-$(BUILD_NUMBER_FROM_FILE)
diff --git a/qemu_pipe/Android.bp b/qemu_pipe/Android.bp
new file mode 100644
index 0000000..93c347b
--- /dev/null
+++ b/qemu_pipe/Android.bp
@@ -0,0 +1,14 @@
+// Copyright 2011 The Android Open Source Project
+
+cc_library_static {
+    name: "libqemu_pipe",
+    vendor_available: true,
+    sanitize: {
+        misc_undefined: ["integer"],
+    },
+    srcs: ["qemu_pipe.cpp"],
+    local_include_dirs: ["include"],
+    static_libs: ["libbase"],
+    export_include_dirs: ["include"],
+    cflags: ["-Werror"],
+}
diff --git a/qemu_pipe/include/qemu_pipe.h b/qemu_pipe/include/qemu_pipe.h
new file mode 100644
index 0000000..0987498
--- /dev/null
+++ b/qemu_pipe/include/qemu_pipe.h
@@ -0,0 +1,64 @@
+/*
+ * 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 ANDROID_CORE_INCLUDE_QEMU_PIPE_H
+#define ANDROID_CORE_INCLUDE_QEMU_PIPE_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+// Try to open a new Qemu fast-pipe. This function returns a file descriptor
+// that can be used to communicate with a named service managed by the
+// emulator.
+//
+// This file descriptor can be used as a standard pipe/socket descriptor.
+//
+// 'pipeName' is the name of the emulator service you want to connect to,
+// and should begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
+// For backward compatibility, the 'pipe:' prefix can be omitted, and in
+// that case, qemu_pipe_open will add it for you.
+
+// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
+//
+// EINVAL  -> unknown/unsupported pipeName
+// ENOSYS  -> fast pipes not available in this system.
+//
+// ENOSYS should never happen, except if you're trying to run within a
+// misconfigured emulator.
+//
+// You should be able to open several pipes to the same pipe service,
+// except for a few special cases (e.g. GSM modem), where EBUSY will be
+// returned if more than one client tries to connect to it.
+int qemu_pipe_open(const char* pipeName);
+
+// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
+// This really adds a 4-hexchar prefix describing the payload size.
+// Returns 0 on success, and -1 on error.
+int qemu_pipe_frame_send(int fd, const void* buff, size_t len);
+
+// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
+// If the framed message is larger than |len|, then this returns -1 and the
+// content is lost. Otherwise, this returns the size of the message. NOTE:
+// empty messages are possible in a framed wire protocol and do not mean
+// end-of-stream.
+int qemu_pipe_frame_recv(int fd, void* buff, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ANDROID_CORE_INCLUDE_QEMU_PIPE_H */
diff --git a/qemu_pipe/qemu_pipe.cpp b/qemu_pipe/qemu_pipe.cpp
new file mode 100644
index 0000000..beeccb0
--- /dev/null
+++ b/qemu_pipe/qemu_pipe.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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 "qemu_pipe.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <android-base/file.h>
+
+using android::base::ReadFully;
+using android::base::WriteFully;
+
+// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
+// occurs during pipe operations. The macro should simply take a printf-style
+// formatting string followed by optional arguments.
+#ifndef QEMU_PIPE_DEBUG
+#  define  QEMU_PIPE_DEBUG(...)   (void)0
+#endif
+
+int qemu_pipe_open(const char* pipeName) {
+    // Sanity check.
+    if (!pipeName) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
+    if (fd < 0) {
+        QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__,
+                        strerror(errno));
+        return -1;
+    }
+
+    // Write the pipe name, *including* the trailing zero which is necessary.
+    size_t pipeNameLen = strlen(pipeName);
+    if (WriteFully(fd, pipeName, pipeNameLen + 1U)) {
+        return fd;
+    }
+
+    // now, add 'pipe:' prefix and try again
+    // Note: host side will wait for the trailing '\0' to start
+    // service lookup.
+    const char pipe_prefix[] = "pipe:";
+    if (WriteFully(fd, pipe_prefix, strlen(pipe_prefix)) &&
+            WriteFully(fd, pipeName, pipeNameLen + 1U)) {
+        return fd;
+    }
+    QEMU_PIPE_DEBUG("%s: Could not write to %s pipe service: %s",
+            __FUNCTION__, pipeName, strerror(errno));
+    close(fd);
+    return -1;
+}
+
+int qemu_pipe_frame_send(int fd, const void* buff, size_t len) {
+    char header[5];
+    snprintf(header, sizeof(header), "%04zx", len);
+    if (!WriteFully(fd, header, 4)) {
+        QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
+        return -1;
+    }
+    if (!WriteFully(fd, buff, len)) {
+        QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+int qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
+    char header[5];
+    if (!ReadFully(fd, header, 4)) {
+        QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
+        return -1;
+    }
+    header[4] = '\0';
+    size_t size;
+    if (sscanf(header, "%04zx", &size) != 1) {
+        QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
+        return -1;
+    }
+    if (size > len) {
+        QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size,
+                        len);
+        return -1;
+    }
+    if (!ReadFully(fd, buff, size)) {
+        QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s",
+                        strerror(errno));
+        return -1;
+    }
+    return size;
+}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 48a46c6..2e5575f 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -101,6 +101,21 @@
   $(foreach binary, $(SANITIZE_ASAN_OPTIONS_FOR), $(eval $(call create-asan-options-module,$(binary))))
 endif
 
+# ASAN extration.
+ASAN_EXTRACT_FILES :=
+ifeq ($(SANITIZE_TARGET_SYSTEM),true)
+include $(CLEAR_VARS)
+LOCAL_MODULE:= asan_extract
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_SRC_FILES := asan_extract.sh
+LOCAL_INIT_RC := asan_extract.rc
+# We need bzip2 on device for extraction.
+LOCAL_REQUIRED_MODULES := bzip2
+include $(BUILD_PREBUILT)
+ASAN_EXTRACT_FILES := asan_extract
+endif
+
 endif
 
 #######################################
@@ -114,7 +129,7 @@
 EXPORT_GLOBAL_ASAN_OPTIONS :=
 ifneq ($(filter address,$(SANITIZE_TARGET)),)
   EXPORT_GLOBAL_ASAN_OPTIONS := export ASAN_OPTIONS include=/system/asan.options
-  LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES)
+  LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES) $(ASAN_EXTRACT_FILES)
 endif
 
 EXPORT_GLOBAL_GCOV_OPTIONS :=
diff --git a/rootdir/asan_extract.rc b/rootdir/asan_extract.rc
new file mode 100644
index 0000000..4aea6a3
--- /dev/null
+++ b/rootdir/asan_extract.rc
@@ -0,0 +1,3 @@
+# When /data is available, look for /system/asan.tar.gz and potentially extract.
+on post-fs-data
+    exec - system system -- /system/bin/asan_extract
diff --git a/rootdir/asan_extract.sh b/rootdir/asan_extract.sh
new file mode 100644
index 0000000..2d72320
--- /dev/null
+++ b/rootdir/asan_extract.sh
@@ -0,0 +1,95 @@
+#!/system/bin/sh
+
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# This script will extract ASAN libraries from /system/asan.tar.gz to /data and then reboot.
+
+# TODO:
+#   * Timestamp or something to know when to run this again. Right now take the existence of
+#     /data/lib as we're already done.
+#   * Need to distinguish pre- from post-decryption for FDE.
+
+SRC=/system/asan.tar.bz2
+MD5_FILE=/data/asan.md5sum
+ASAN_DIR=/data/asan
+# Minimum /data size in blocks. Arbitrarily 512M.
+MIN_DATA_SIZE=131072
+
+# Checks for FDE pre-decrypt state.
+
+VOLD_STATUS=$(getprop vold.decrypt)
+if [ "$VOLD_STATUS" = "trigger_restart_min_framework" ] ; then
+  log -p i -t asan_install "Pre-decrypt FDE detected (by vold property)!"
+  exit 1
+fi
+
+STATFS_BLOCKS=$(stat -f -c '%b' /data)
+if [ "$STATFS_BLOCKS" -le "$MIN_DATA_SIZE" ] ; then
+  log -p i -t asan_install "Pre-decrypt FDE detected (by /data size)!"
+  exit 1
+fi
+
+# Check for ASAN source.
+
+if ! test -f $SRC ; then
+  log -p i -t asan_install "Did not find $SRC!"
+  exit 1
+fi
+
+log -p i -t asan_install "Found $SRC, checking whether we need to apply it."
+
+# Checksum check.
+
+ASAN_TAR_MD5=$(md5sum $SRC)
+if test -f $MD5_FILE ; then
+  INSTALLED_MD5=$(cat $MD5_FILE)
+  if [ "x$ASAN_TAR_MD5" = "x$INSTALLED_MD5" ] ; then
+    log -p i -t asan_install "Checksums match, nothing to be done here."
+    exit 0
+  fi
+fi
+
+# Actually apply the source.
+
+# Just clean up, helps with restorecon.
+rm -rf $ASAN_DIR
+
+log -p i -t asan_install "Untarring $SRC..."
+
+# Unzip from /system/asan.tar.gz into data. Need to pipe as gunzip is not on device.
+bzip2 -c -d $SRC | tar -x -f - --no-same-owner -C / || exit 1
+
+# Cannot log here, log would run with system_data_file.
+
+# Set correct permission bits.
+chmod -R 744 $ASAN_DIR
+cd $ASAN_DIR ; find . -type d -exec chmod 755 {} \;
+
+restorecon -R -F $ASAN_DIR/*/lib*
+
+log -p i -t asan_install "Fixed selinux labels..."
+
+
+# Now write down our checksum to mark the extraction complete.
+echo "$ASAN_TAR_MD5" > $MD5_FILE
+
+# We want to reboot now. It seems it is not possible to run "reboot" here, the device will
+# just be stuck.
+
+log -p i -t asan_install "Signaling init to reboot..."
+
+setprop sys.powerctl reboot
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index e2c70f3..56066bc 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -7,6 +7,10 @@
 dir.system = /system/bin/
 dir.system = /system/xbin/
 dir.vendor = /vendor/bin/
+dir.test = /data/nativetest/
+dir.test = /data/nativetest64/
+dir.test = /data/benchmarktest/
+dir.test = /data/benchmarktest64/
 
 [system]
 additional.namespaces = sphal,vndk,rs
@@ -63,7 +67,7 @@
 namespace.sphal.link.default.shared_libs = libc.so:libz.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libvndksupport.so
 
 # WARNING: only VNDK-SP libs can be listed here. DO NOT EDIT this line.
-namespace.sphal.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.base@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so
+namespace.sphal.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so
 
 # Renderscript gets separate namespace
 namespace.sphal.link.rs.shared_libs = libRS_internal.so
@@ -84,8 +88,8 @@
 namespace.rs.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data
 
 namespace.rs.links = default,vndk
-namespace.rs.link.default.shared_libs = libc.so:libz.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libmediandk.so:libui.so:libvndksupport.so
-namespace.rs.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.base@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so
+namespace.rs.link.default.shared_libs = libc.so:libz.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libmediandk.so:libvndksupport.so
+namespace.rs.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so
 
 ###############################################################################
 # "vndk" namespace
@@ -105,9 +109,23 @@
 namespace.vndk.links = default
 namespace.vndk.link.default.shared_libs = android.hidl.memory@1.0-impl.so:libc.so:libz.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libvndksupport.so
 
-
+###############################################################################
+# Namespace config for vendor processes. In O, no restriction is enforced for
+# them. However, in O-MR1, access to /system/${LIB} will not be allowed to
+# the default namespace. 'system' namespace will be added to give limited
+# (LL-NDK only) access.
+###############################################################################
 [vendor]
 namespace.default.isolated = false
 namespace.default.search.paths = /vendor/${LIB}:/vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/system/${LIB}
 
 namespace.default.asan.search.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/system/${LIB}:/system/${LIB}
+
+###############################################################################
+# Namespace config for tests. No VNDK restriction is enforced for these tests.
+###############################################################################
+[test]
+namespace.default.isolated = false
+namespace.default.search.paths = /vendor/${LIB}:/vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/system/${LIB}
+
+namespace.default.asan.search.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/system/${LIB}:/system/${LIB}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 3b996a4..fd2c838 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -27,6 +27,16 @@
     # Set the security context of /postinstall if present.
     restorecon /postinstall
 
+    # Mount cgroup mount point for cpu accounting
+    mount cgroup none /acct cpuacct
+    mkdir /acct/uid
+
+    # root memory control cgroup, used by lmkd
+    mkdir /dev/memcg 0700 root system
+    mount cgroup none /dev/memcg memory
+    # app mem cgroups, used by activity manager, lmkd and zygote
+    mkdir /dev/memcg/apps/ 0755 system system
+
     start ueventd
 
 on init
@@ -43,28 +53,28 @@
     # Link /vendor to /system/vendor for devices without a vendor partition.
     symlink /system/vendor /vendor
 
-    # Mount cgroup mount point for cpu accounting
-    mount cgroup none /acct cpuacct
-    mkdir /acct/uid
-
     # Create energy-aware scheduler tuning nodes
     mkdir /dev/stune
     mount cgroup none /dev/stune schedtune
     mkdir /dev/stune/foreground
     mkdir /dev/stune/background
     mkdir /dev/stune/top-app
+    mkdir /dev/stune/rt
     chown system system /dev/stune
     chown system system /dev/stune/foreground
     chown system system /dev/stune/background
     chown system system /dev/stune/top-app
+    chown system system /dev/stune/rt
     chown system system /dev/stune/tasks
     chown system system /dev/stune/foreground/tasks
     chown system system /dev/stune/background/tasks
     chown system system /dev/stune/top-app/tasks
+    chown system system /dev/stune/rt/tasks
     chmod 0664 /dev/stune/tasks
     chmod 0664 /dev/stune/foreground/tasks
     chmod 0664 /dev/stune/background/tasks
     chmod 0664 /dev/stune/top-app/tasks
+    chmod 0664 /dev/stune/rt/tasks
 
     # Mount staging areas for devices managed by vold
     # See storage config details at http://source.android.com/tech/storage/
@@ -99,12 +109,6 @@
     symlink /storage/self/primary /mnt/sdcard
     symlink /mnt/user/0/primary /mnt/runtime/default/self/primary
 
-    # root memory control cgroup, used by lmkd
-    mkdir /dev/memcg 0700 root system
-    mount cgroup none /dev/memcg memory
-    # app mem cgroups, used by activity manager, lmkd and zygote
-    mkdir /dev/memcg/apps/ 0755 system system
-
     write /proc/sys/kernel/panic_on_oops 1
     write /proc/sys/kernel/hung_task_timeout_secs 0
     write /proc/cpu/alignment 4
@@ -375,6 +379,20 @@
 
     # create basic filesystem structure
     mkdir /data/misc 01771 system misc
+    mkdir /data/misc/recovery 0770 system log
+    copy /data/misc/recovery/ro.build.fingerprint /data/misc/recovery/ro.build.fingerprint.1
+    chmod 0440 /data/misc/recovery/ro.build.fingerprint.1
+    chown system log /data/misc/recovery/ro.build.fingerprint.1
+    write /data/misc/recovery/ro.build.fingerprint ${ro.build.fingerprint}
+    chmod 0440 /data/misc/recovery/ro.build.fingerprint
+    chown system log /data/misc/recovery/ro.build.fingerprint
+    mkdir /data/misc/recovery/proc 0770 system log
+    copy /data/misc/recovery/proc/version /data/misc/recovery/proc/version.1
+    chmod 0440 /data/misc/recovery/proc/version.1
+    chown system log /data/misc/recovery/proc/version.1
+    copy /proc/version /data/misc/recovery/proc/version
+    chmod 0440 /data/misc/recovery/proc/version
+    chown system log /data/misc/recovery/proc/version
     mkdir /data/misc/bluedroid 02770 bluetooth bluetooth
     # Fix the access permissions and group ownership for 'bt_config.conf'
     chmod 0660 /data/misc/bluedroid/bt_config.conf
@@ -674,12 +692,17 @@
 on property:security.perf_harden=1
     write /proc/sys/kernel/perf_event_paranoid 3
 
+# on shutdown
+# In device's init.rc, this trigger can be used to do device-specific actions
+# before shutdown. e.g disable watchdog and mask error handling
+
 ## Daemon processes to be run by init.
 ##
 service ueventd /sbin/ueventd
     class core
     critical
     seclabel u:r:ueventd:s0
+    shutdown critical
 
 service healthd /system/bin/healthd
     class core
diff --git a/rootdir/init.usb.configfs.rc b/rootdir/init.usb.configfs.rc
index 32f0198..de1aab3 100644
--- a/rootdir/init.usb.configfs.rc
+++ b/rootdir/init.usb.configfs.rc
@@ -2,6 +2,7 @@
     write /config/usb_gadget/g1/UDC "none"
     stop adbd
     setprop sys.usb.ffs.ready 0
+    setprop sys.usb.ffs.mtp.ready 0
     write /config/usb_gadget/g1/bDeviceClass 0
     write /config/usb_gadget/g1/bDeviceSubClass 0
     write /config/usb_gadget/g1/bDeviceProtocol 0
@@ -11,6 +12,9 @@
     rmdir /config/usb_gadget/g1/functions/rndis.gs4
     setprop sys.usb.state ${sys.usb.config}
 
+on property:init.svc.adbd=stopped
+    setprop sys.usb.ffs.ready 0
+
 on property:sys.usb.config=adb && property:sys.usb.configfs=1
     start adbd
 
@@ -20,7 +24,7 @@
     write /config/usb_gadget/g1/UDC ${sys.usb.controller}
     setprop sys.usb.state ${sys.usb.config}
 
-on property:sys.usb.config=mtp && property:sys.usb.configfs=1
+on property:sys.usb.ffs.mtp.ready=1 && property:sys.usb.config=mtp && property:sys.usb.configfs=1
     write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp"
     symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
     write /config/usb_gadget/g1/UDC ${sys.usb.controller}
@@ -29,14 +33,15 @@
 on property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
     start adbd
 
-on property:sys.usb.ffs.ready=1 && property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
+on property:sys.usb.ffs.ready=1 && property:sys.usb.ffs.mtp.ready=1 && \
+property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
     write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp_adb"
     symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
     symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
     write /config/usb_gadget/g1/UDC ${sys.usb.controller}
     setprop sys.usb.state ${sys.usb.config}
 
-on property:sys.usb.config=ptp && property:sys.usb.configfs=1
+on property:sys.usb.ffs.mtp.ready=1 && property:sys.usb.config=ptp && property:sys.usb.configfs=1
     write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp"
     symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
     write /config/usb_gadget/g1/UDC ${sys.usb.controller}
@@ -45,7 +50,8 @@
 on property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
     start adbd
 
-on property:sys.usb.ffs.ready=1 && property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
+on property:sys.usb.ffs.ready=1 && property:sys.usb.ffs.mtp.ready=1 && \
+property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
     write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp_adb"
     symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
     symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index f5c93b7..eadf219 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,5 +1,37 @@
 subsystem adf
-	devname uevent_devname
+    devname uevent_devname
+
+subsystem graphics
+    devname uevent_devpath
+    dirname /dev/graphics
+
+subsystem drm
+    devname uevent_devpath
+    dirname /dev/dri
+
+subsystem oncrpc
+    devname uevent_devpath
+    dirname /dev/oncrpc
+
+subsystem adsp
+    devname uevent_devpath
+    dirname /dev/adsp
+
+subsystem msm_camera
+    devname uevent_devpath
+    dirname /dev/msm_camera
+
+subsystem input
+    devname uevent_devpath
+    dirname /dev/input
+
+subsystem mtd
+    devname uevent_devpath
+    dirname /dev/mtd
+
+subsystem sound
+    devname uevent_devpath
+    dirname /dev/snd
 
 # ueventd can only set permissions on device nodes and their associated
 # sysfs attributes, not on arbitrary paths.
@@ -22,9 +54,6 @@
 /dev/hwbinder             0666   root       root
 /dev/vndbinder            0666   root       root
 
-# Anyone can read the logs, but if they're not in the "logs"
-# group, then they'll only see log entries for their UID.
-/dev/log/*                0666   root       log
 /dev/pmsg0                0222   root       log
 
 # the msm hw3d client device node is world writable/readable.
@@ -40,7 +69,7 @@
 /dev/diag                 0660   radio      radio
 /dev/diag_arm9            0660   radio      radio
 /dev/ttyMSM0              0600   bluetooth  bluetooth
-/dev/uhid                 0660   system     bluetooth
+/dev/uhid                 0660   uhid       uhid
 /dev/uinput               0660   system     bluetooth
 /dev/alarm                0664   system     radio
 /dev/rtc0                 0640   system     system
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 4f4fc5d..6d35fed 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -3,6 +3,7 @@
     required: [
         "bzip2",
         "grep",
+        "grep_vendor",
         "gzip",
         "mkshrc",
         "mkshrc_vendor",
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
new file mode 100644
index 0000000..5d10c18
--- /dev/null
+++ b/shell_and_utilities/README.md
@@ -0,0 +1,157 @@
+Android's shell and utilities
+=============================
+
+Since IceCreamSandwich Android has used
+[mksh](https://www.mirbsd.org/mksh.htm) as its shell. Before then it used
+[ash](https://en.wikipedia.org/wiki/Almquist_shell) (which actually
+remained in the tree up to and including KitKat).
+
+Initially Android had a very limited command-line provided by its
+own "toolbox" binary. These days almost everything is supplied by
+[toybox](http://landley.net/toybox/) instead.
+
+We started moving a few of the more important tools to full
+BSD implementations in JellyBean before we started in earnest in
+Lollipop. Lollipop was a major break with the past in many ways (LP64
+support and the switch to ART both having lots of knock-on effects around
+the system), so although this was the beginning of the end of toolbox it
+(a) didn't stand out given all the other systems-level changes and (b)
+in Marshmallow we changed direction and started the move to toybox.
+
+The lists below show what tools were provided and where they came from in
+each release starting with Gingerbread. This doesn't tell the full story,
+because the toolbox implementations did have bugs fixed and options added
+over the years. Gingerbread's rm, for example, supported `-r`/`-R` but not
+`-f`. But this gives you an idea of what was available in any given release,
+and how usable it was likely to be.
+
+
+Android 2.3 (Gingerbread)
+-------------------------
+
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+umount uptime vmstat watchprops wipe
+
+
+Android 4.0 (IceCreamSandwich)
+------------------------------
+
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+touch umount uptime vmstat watchprops wipe
+
+
+Android 4.1-4.3 (JellyBean)
+---------------------------
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mount mv nandread netstat notify
+printenv ps reboot renice restorecon rm rmdir rmmod route runcon schedtop
+sendevent setconsole setenforce setprop setsebool sleep smd start stop
+sync top touch umount uptime vmstat watchprops wipe
+
+
+Android 4.4 (KitKat)
+--------------------
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mkswap mount mv nandread netstat
+notify printenv ps readlink renice restorecon rm rmdir rmmod route runcon
+schedtop sendevent setconsole setenforce setprop setsebool sleep smd start
+stop swapoff swapon sync top touch umount uptime vmstat watchprops wipe
+
+
+Android 5.0 (Lollipop)
+----------------------
+
+BSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync
+
+toolbox: chcon chmod clear cmp date df dmesg getenforce getevent getprop
+getsebool hd id ifconfig iftop insmod ioctl ionice load\_policy log ls
+lsmod lsof md5 mkdir mknod mkswap mount nandread netstat newfs\_msdos
+nohup notify ps readlink renice restorecon rmmod route runcon schedtop
+sendevent setenforce setprop setsebool smd start stop swapoff swapon
+top touch umount uptime vmstat watchprops wipe
+
+
+Android 6.0 (Marshmallow)
+-------------------------
+
+BSD: dd du grep
+
+toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
+newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
+
+toybox: acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
+chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
+env expand expr fallocate false find free getenforce getprop groups
+head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
+logname losetup lsmod lsusb md5sum mkdir mknod mkswap mktemp modinfo
+more mountpoint mv netstat nice nl nohup od paste patch pgrep pidof
+pkill pmap printenv printf pwd readlink realpath restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate umount uname uniq unix2dos usleep
+vmstat wc which whoami xargs yes
+
+
+Android 7.0 (Nougat)
+--------------------
+
+BSD: dd grep
+
+toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
+sendevent start stop top
+
+toybox: acpi base64 basename blockdev bzcat cal cat chcon chgrp chmod
+chown chroot cksum clear comm cmp cp cpio cut date df dirname dmesg
+dos2unix du echo env expand expr fallocate false find flock free
+getenforce getprop groups head hostname hwclock id ifconfig inotifyd
+insmod ionice iorenice kill killall load\_policy ln logname losetup ls
+lsmod lsof lsusb md5sum mkdir mknod mkswap mktemp modinfo more mount
+mountpoint mv netstat nice nl nohup od paste patch pgrep pidof pkill
+pmap printenv printf pwd readlink realpath renice restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate tty ulimit umount uname uniq unix2dos
+uptime usleep vmstat wc which whoami xargs xxd yes
+
+
+Current AOSP
+------------
+
+BSD: dd grep
+
+bzip2: bzcat bzip2 bunzip2
+
+toolbox: getevent gzip newfs\_msdos gunzip zcat
+
+toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
+dos2unix du echo env expand expr fallocate false file find flock free
+getenforce getprop groups head hostname hwclock id ifconfig inotifyd
+insmod ionice iorenice kill killall ln load\_policy log logname losetup
+ls lsmod lsof lsusb md5sum microcom mkdir mknod mkswap mktemp modinfo
+modprobe more mount mountpoint mv netstat nice nl nohup od paste patch
+pgrep pidof pkill pmap printenv printf ps pwd readlink realpath renice
+restorecon rm rmdir rmmod runcon sed sendevent seq setenforce setprop
+setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort split
+start stat stop strings swapoff swapon sync sysctl tac tail tar taskset
+tee time timeout top touch tr true truncate tty ulimit umount uname uniq
+unix2dos uptime usleep uudecode uuencode vmstat wc which whoami xargs
+xxd yes
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
new file mode 100644
index 0000000..8db8327
--- /dev/null
+++ b/toolbox/Android.bp
@@ -0,0 +1,58 @@
+common_cflags = [
+    "-Werror",
+    "-Wno-unused-parameter",
+    "-Wno-unused-const-variable",
+    "-include bsd-compatibility.h"
+]
+
+cc_library_static {
+    srcs: [
+        "upstream-netbsd/bin/dd/args.c",
+        "upstream-netbsd/bin/dd/conv.c",
+        "upstream-netbsd/bin/dd/dd.c",
+        "upstream-netbsd/bin/dd/dd_hostops.c",
+        "upstream-netbsd/bin/dd/misc.c",
+        "upstream-netbsd/bin/dd/position.c",
+        "upstream-netbsd/lib/libc/gen/getbsize.c",
+        "upstream-netbsd/lib/libc/gen/humanize_number.c",
+        "upstream-netbsd/lib/libc/stdlib/strsuftoll.c",
+        "upstream-netbsd/lib/libc/string/swab.c",
+        "upstream-netbsd/lib/libutil/raise_default_signal.c",
+    ],
+    cflags: common_cflags + [
+        "-Dmain=dd_main",
+        "-DNO_CONV",
+    ],
+    local_include_dirs: ["upstream-netbsd/include/"],
+    name: "libtoolbox_dd",
+}
+
+// We build BSD grep separately, so it can provide egrep and fgrep too.
+cc_defaults {
+    name: "grep_common",
+    srcs: [
+        "upstream-netbsd/usr.bin/grep/fastgrep.c",
+        "upstream-netbsd/usr.bin/grep/file.c",
+        "upstream-netbsd/usr.bin/grep/grep.c",
+        "upstream-netbsd/usr.bin/grep/queue.c",
+        "upstream-netbsd/usr.bin/grep/util.c",
+    ],
+    cflags: common_cflags,
+    local_include_dirs: ["upstream-netbsd/include/"],
+    symlinks: ["egrep", "fgrep"],
+}
+
+cc_binary {
+    name: "grep",
+    defaults: ["grep_common"],
+}
+
+// Build vendor grep.
+// TODO: Add vendor_available to "grep" module and remove "grep_vendor" module
+//       when vendor_available is fully supported.
+cc_binary {
+    name: "grep_vendor",
+    stem: "grep",
+    vendor: true,
+    defaults: ["grep_common"],
+}
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index d6ead1a..94029d8 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -1,30 +1,9 @@
 LOCAL_PATH:= $(call my-dir)
 
-
 common_cflags := \
     -Werror -Wno-unused-parameter -Wno-unused-const-variable \
     -include bsd-compatibility.h \
 
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
-    upstream-netbsd/bin/dd/args.c \
-    upstream-netbsd/bin/dd/conv.c \
-    upstream-netbsd/bin/dd/dd.c \
-    upstream-netbsd/bin/dd/dd_hostops.c \
-    upstream-netbsd/bin/dd/misc.c \
-    upstream-netbsd/bin/dd/position.c \
-    upstream-netbsd/lib/libc/gen/getbsize.c \
-    upstream-netbsd/lib/libc/gen/humanize_number.c \
-    upstream-netbsd/lib/libc/stdlib/strsuftoll.c \
-    upstream-netbsd/lib/libc/string/swab.c \
-    upstream-netbsd/lib/libutil/raise_default_signal.c
-LOCAL_CFLAGS += $(common_cflags) -Dmain=dd_main -DNO_CONV
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/upstream-netbsd/include/
-LOCAL_MODULE := libtoolbox_dd
-include $(BUILD_STATIC_LIBRARY)
-
-
 include $(CLEAR_VARS)
 
 BSD_TOOLS := \
@@ -80,18 +59,3 @@
 $(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 build BSD grep separately, so it can provide egrep and fgrep too.
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
-    upstream-netbsd/usr.bin/grep/fastgrep.c \
-    upstream-netbsd/usr.bin/grep/file.c \
-    upstream-netbsd/usr.bin/grep/grep.c \
-    upstream-netbsd/usr.bin/grep/queue.c \
-    upstream-netbsd/usr.bin/grep/util.c
-LOCAL_CFLAGS += $(common_cflags)
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/upstream-netbsd/include/
-LOCAL_MODULE := grep
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,egrep fgrep,ln -sf grep $(TARGET_OUT)/bin/$(t);)
-include $(BUILD_EXECUTABLE)
diff --git a/trusty/Android.bp b/trusty/Android.bp
new file mode 100644
index 0000000..386fbe6
--- /dev/null
+++ b/trusty/Android.bp
@@ -0,0 +1,7 @@
+subdirs = [
+    "gatekeeper",
+    "keymaster",
+    "libtrusty",
+    "nvram",
+    "storage/*",
+]
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp
new file mode 100644
index 0000000..a9566a1
--- /dev/null
+++ b/trusty/gatekeeper/Android.bp
@@ -0,0 +1,46 @@
+//
+// 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.
+//
+
+// WARNING: Everything listed here will be built on ALL platforms,
+// including x86, the emulator, and the SDK.  Modules must be uniquely
+// named (liblights.panda), and must build everywhere, or limit themselves
+// to only building on ARM if they include assembly. Individual makefiles
+// are responsible for having their own logic, for fine-grained control.
+
+cc_library_shared {
+    name: "gatekeeper.trusty",
+
+    relative_install_path: "hw",
+
+    srcs: [
+        "module.cpp",
+        "trusty_gatekeeper_ipc.c",
+        "trusty_gatekeeper.cpp",
+    ],
+
+    cflags: [
+        "-fvisibility=hidden",
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "libgatekeeper",
+        "liblog",
+        "libcutils",
+        "libtrusty",
+    ],
+}
diff --git a/trusty/gatekeeper/Android.mk b/trusty/gatekeeper/Android.mk
deleted file mode 100644
index 3982c8f..0000000
--- a/trusty/gatekeeper/Android.mk
+++ /dev/null
@@ -1,46 +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.
-#
-
-# WARNING: Everything listed here will be built on ALL platforms,
-# including x86, the emulator, and the SDK.  Modules must be uniquely
-# named (liblights.panda), and must build everywhere, or limit themselves
-# to only building on ARM if they include assembly. Individual makefiles
-# are responsible for having their own logic, for fine-grained control.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := gatekeeper.trusty
-
-LOCAL_MODULE_RELATIVE_PATH := hw
-
-LOCAL_SRC_FILES := \
-	module.cpp \
-	trusty_gatekeeper_ipc.c \
-	trusty_gatekeeper.cpp
-
-LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
-	libgatekeeper \
-	liblog \
-	libcutils \
-	libtrusty
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
new file mode 100644
index 0000000..6b9d723
--- /dev/null
+++ b/trusty/keymaster/Android.bp
@@ -0,0 +1,68 @@
+//
+// 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.
+//
+
+// WARNING: Everything listed here will be built on ALL platforms,
+// including x86, the emulator, and the SDK.  Modules must be uniquely
+// named (liblights.panda), and must build everywhere, or limit themselves
+// to only building on ARM if they include assembly. Individual makefiles
+// are responsible for having their own logic, for fine-grained control.
+
+// trusty_keymaster is a binary used only for on-device testing.  It
+// runs Trusty Keymaster through a basic set of operations with RSA
+// and ECDSA keys.
+cc_binary {
+    name: "trusty_keymaster_tipc",
+    srcs: [
+        "trusty_keymaster_device.cpp",
+        "trusty_keymaster_ipc.cpp",
+        "trusty_keymaster_main.cpp",
+    ],
+    shared_libs: [
+        "libcrypto",
+        "libcutils",
+        "libkeymaster_portable",
+        "libkeymaster_staging",
+        "libtrusty",
+        "libkeymaster_messages",
+        "libsoftkeymasterdevice",
+        "liblog",
+    ],
+}
+
+// keystore.trusty is the HAL used by keystore on Trusty devices.
+cc_library_shared {
+    name: "keystore.trusty",
+    relative_install_path: "hw",
+    srcs: [
+        "module.cpp",
+        "trusty_keymaster_ipc.cpp",
+        "trusty_keymaster_device.cpp",
+    ],
+
+    cflags: [
+        "-fvisibility=hidden",
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "libcrypto",
+        "libkeymaster_messages",
+        "libtrusty",
+        "liblog",
+        "libcutils",
+    ],
+}
diff --git a/trusty/keymaster/Android.mk b/trusty/keymaster/Android.mk
deleted file mode 100644
index 0ebf52d..0000000
--- a/trusty/keymaster/Android.mk
+++ /dev/null
@@ -1,67 +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.
-#
-
-# WARNING: Everything listed here will be built on ALL platforms,
-# including x86, the emulator, and the SDK.  Modules must be uniquely
-# named (liblights.panda), and must build everywhere, or limit themselves
-# to only building on ARM if they include assembly. Individual makefiles
-# are responsible for having their own logic, for fine-grained control.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-###
-# trusty_keymaster is a binary used only for on-device testing.  It
-# runs Trusty Keymaster through a basic set of operations with RSA
-# and ECDSA keys.
-###
-LOCAL_MODULE := trusty_keymaster_tipc
-LOCAL_SRC_FILES := \
-	trusty_keymaster_device.cpp \
-	trusty_keymaster_ipc.c \
-	trusty_keymaster_main.cpp
-LOCAL_SHARED_LIBRARIES := \
-	libcrypto \
-	libcutils \
-	libkeymaster1 \
-	libtrusty \
-	libkeymaster_messages \
-	liblog
-
-include $(BUILD_EXECUTABLE)
-
-###
-# keystore.trusty is the HAL used by keystore on Trusty devices.
-##
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := keystore.trusty
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SRC_FILES := module.cpp \
-	trusty_keymaster_ipc.c \
-	trusty_keymaster_device.cpp
-LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
-LOCAL_SHARED_LIBRARIES := \
-	libcrypto \
-	libkeymaster_messages \
-	libtrusty \
-	liblog \
-	libcutils
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/keymaster/keymaster_ipc.h b/trusty/keymaster/keymaster_ipc.h
index 48fa53d..b38eb05 100644
--- a/trusty/keymaster/keymaster_ipc.h
+++ b/trusty/keymaster/keymaster_ipc.h
@@ -16,13 +16,15 @@
 
 #pragma once
 
+// clang-format off
+
 #define KEYMASTER_PORT "com.android.trusty.keymaster"
 #define KEYMASTER_MAX_BUFFER_LENGTH 4096
 
 // Commands
-enum keymaster_command {
-	KEYMASTER_RESP_BIT              = 1,
-	KEYMASTER_REQ_SHIFT             = 1,
+enum keymaster_command : uint32_t {
+    KEYMASTER_RESP_BIT              = 1,
+    KEYMASTER_REQ_SHIFT             = 1,
 
     KM_GENERATE_KEY                 = (0 << KEYMASTER_REQ_SHIFT),
     KM_BEGIN_OPERATION              = (1 << KEYMASTER_REQ_SHIFT),
@@ -40,6 +42,9 @@
     KM_GET_SUPPORTED_IMPORT_FORMATS = (13 << KEYMASTER_REQ_SHIFT),
     KM_GET_SUPPORTED_EXPORT_FORMATS = (14 << KEYMASTER_REQ_SHIFT),
     KM_GET_KEY_CHARACTERISTICS      = (15 << KEYMASTER_REQ_SHIFT),
+    KM_ATTEST_KEY                   = (16 << KEYMASTER_REQ_SHIFT),
+    KM_UPGRADE_KEY                  = (17 << KEYMASTER_REQ_SHIFT),
+    KM_CONFIGURE                    = (18 << KEYMASTER_REQ_SHIFT),
 };
 
 #ifdef __ANDROID__
@@ -50,8 +55,8 @@
  * @payload: start of the serialized command specific payload
  */
 struct keymaster_message {
-	uint32_t cmd;
-	uint8_t payload[0];
+    uint32_t cmd;
+    uint8_t payload[0];
 };
 
 #endif
diff --git a/trusty/keymaster/module.cpp b/trusty/keymaster/module.cpp
index 81597d9..b472680 100644
--- a/trusty/keymaster/module.cpp
+++ b/trusty/keymaster/module.cpp
@@ -26,14 +26,15 @@
 /*
  * Generic device handling
  */
-static int trusty_keymaster_open(const hw_module_t* module, const char* name,
-                                 hw_device_t** device) {
-    if (strcmp(name, KEYSTORE_KEYMASTER) != 0)
+static int trusty_keymaster_open(const hw_module_t* module, const char* name, hw_device_t** device) {
+    if (strcmp(name, KEYSTORE_KEYMASTER) != 0) {
         return -EINVAL;
+    }
 
     TrustyKeymasterDevice* dev = new TrustyKeymasterDevice(module);
-    if (dev == NULL)
+    if (dev == NULL) {
         return -ENOMEM;
+    }
     *device = dev->hw_device();
     // Do not delete dev; it will get cleaned up when the caller calls device->close(), and must
     // exist until then.
@@ -47,14 +48,14 @@
 struct keystore_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
     .common =
         {
-         .tag = HARDWARE_MODULE_TAG,
-         .module_api_version = KEYMASTER_MODULE_API_VERSION_0_3,
-         .hal_api_version = HARDWARE_HAL_API_VERSION,
-         .id = KEYSTORE_HARDWARE_MODULE_ID,
-         .name = "Trusty Keymaster HAL",
-         .author = "The Android Open Source Project",
-         .methods = &keystore_module_methods,
-         .dso = 0,
-         .reserved = {},
+            .tag = HARDWARE_MODULE_TAG,
+            .module_api_version = KEYMASTER_MODULE_API_VERSION_2_0,
+            .hal_api_version = HARDWARE_HAL_API_VERSION,
+            .id = KEYSTORE_HARDWARE_MODULE_ID,
+            .name = "Trusty Keymaster HAL",
+            .author = "The Android Open Source Project",
+            .methods = &keystore_module_methods,
+            .dso = 0,
+            .reserved = {},
         },
 };
diff --git a/trusty/keymaster/trusty_keymaster_device.cpp b/trusty/keymaster/trusty_keymaster_device.cpp
index 1368f88..5f16fd0 100644
--- a/trusty/keymaster/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/trusty_keymaster_device.cpp
@@ -25,49 +25,53 @@
 #include <string.h>
 #include <time.h>
 
+#include <algorithm>
 #include <type_traits>
 
-#include <hardware/keymaster0.h>
+#include <hardware/keymaster2.h>
 #include <keymaster/authorization_set.h>
 #include <log/log.h>
 
+#include "keymaster_ipc.h"
 #include "trusty_keymaster_device.h"
 #include "trusty_keymaster_ipc.h"
-#include "keymaster_ipc.h"
 
-const uint32_t SEND_BUF_SIZE = 8192;
-const uint32_t RECV_BUF_SIZE = 8192;
+const uint32_t RECV_BUF_SIZE = PAGE_SIZE;
+const uint32_t SEND_BUF_SIZE = (PAGE_SIZE - sizeof(struct keymaster_message) - 16 /* tipc header */);
+
+const size_t kMaximumAttestationChallengeLength = 128;
+const size_t kMaximumFinishInputLength = 2048;
 
 namespace keymaster {
 
 static keymaster_error_t translate_error(int err) {
     switch (err) {
-    case 0:
-        return KM_ERROR_OK;
-    case -EPERM:
-    case -EACCES:
-        return KM_ERROR_SECURE_HW_ACCESS_DENIED;
+        case 0:
+            return KM_ERROR_OK;
+        case -EPERM:
+        case -EACCES:
+            return KM_ERROR_SECURE_HW_ACCESS_DENIED;
 
-    case -ECANCELED:
-        return KM_ERROR_OPERATION_CANCELLED;
+        case -ECANCELED:
+            return KM_ERROR_OPERATION_CANCELLED;
 
-    case -ENODEV:
-        return KM_ERROR_UNIMPLEMENTED;
+        case -ENODEV:
+            return KM_ERROR_UNIMPLEMENTED;
 
-    case -ENOMEM:
-        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        case -ENOMEM:
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
 
-    case -EBUSY:
-        return KM_ERROR_SECURE_HW_BUSY;
+        case -EBUSY:
+            return KM_ERROR_SECURE_HW_BUSY;
 
-    case -EIO:
-        return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+        case -EIO:
+            return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
 
-    case -EOVERFLOW:
-        return KM_ERROR_INVALID_INPUT_LENGTH;
+        case -EOVERFLOW:
+            return KM_ERROR_INVALID_INPUT_LENGTH;
 
-    default:
-        return KM_ERROR_UNKNOWN_ERROR;
+        default:
+            return KM_ERROR_UNKNOWN_ERROR;
     }
 }
 
@@ -75,31 +79,36 @@
     static_assert(std::is_standard_layout<TrustyKeymasterDevice>::value,
                   "TrustyKeymasterDevice must be standard layout");
     static_assert(offsetof(TrustyKeymasterDevice, device_) == 0,
-                  "device_ must be the first member of KeymasterOpenSsl");
+                  "device_ must be the first member of TrustyKeymasterDevice");
     static_assert(offsetof(TrustyKeymasterDevice, device_.common) == 0,
-                  "common must be the first member of keymaster_device");
+                  "common must be the first member of keymaster2_device");
 
     ALOGI("Creating device");
     ALOGD("Device address: %p", this);
 
-    memset(&device_, 0, sizeof(device_));
+    device_ = {};
 
     device_.common.tag = HARDWARE_DEVICE_TAG;
     device_.common.version = 1;
     device_.common.module = const_cast<hw_module_t*>(module);
     device_.common.close = close_device;
 
-    device_.flags = KEYMASTER_BLOBS_ARE_STANDALONE | KEYMASTER_SUPPORTS_EC;
+    device_.flags = KEYMASTER_SUPPORTS_EC;
 
-    device_.generate_keypair = generate_keypair;
-    device_.import_keypair = import_keypair;
-    device_.get_keypair_public = get_keypair_public;
-    device_.delete_keypair = NULL;
-    device_.delete_all = NULL;
-    device_.sign_data = sign_data;
-    device_.verify_data = verify_data;
-
-    device_.context = NULL;
+    device_.configure = configure;
+    device_.add_rng_entropy = add_rng_entropy;
+    device_.generate_key = generate_key;
+    device_.get_key_characteristics = get_key_characteristics;
+    device_.import_key = import_key;
+    device_.export_key = export_key;
+    device_.attest_key = attest_key;
+    device_.upgrade_key = upgrade_key;
+    device_.delete_key = nullptr;
+    device_.delete_all_keys = nullptr;
+    device_.begin = begin;
+    device_.update = update;
+    device_.finish = finish;
+    device_.abort = abort;
 
     int rc = trusty_keymaster_connect();
     error_ = translate_error(rc);
@@ -110,11 +119,11 @@
 
     GetVersionRequest version_request;
     GetVersionResponse version_response;
-    error_ = Send(version_request, &version_response);
+    error_ = Send(KM_GET_VERSION, version_request, &version_response);
     if (error_ == KM_ERROR_INVALID_ARGUMENT || error_ == KM_ERROR_UNIMPLEMENTED) {
-        ALOGI("\"Bad parameters\" error on GetVersion call.  Assuming version 0.");
-        message_version_ = 0;
-        error_ = KM_ERROR_OK;
+        ALOGE("\"Bad parameters\" error on GetVersion call.  Version 0 is not supported.");
+        error_ = KM_ERROR_VERSION_MISMATCH;
+        return;
     }
     message_version_ = MessageVersion(version_response.major_ver, version_response.minor_ver,
                                       version_response.subminor_ver);
@@ -130,261 +139,510 @@
     trusty_keymaster_disconnect();
 }
 
-const uint64_t HUNDRED_YEARS = 1000LL * 60 * 60 * 24 * 365 * 100;
+namespace {
 
-int TrustyKeymasterDevice::generate_keypair(const keymaster_keypair_t key_type,
-                                            const void* key_params, uint8_t** key_blob,
-                                            size_t* key_blob_length) {
-    ALOGD("Device received generate_keypair");
+// Allocates a new buffer with malloc and copies the contents of |buffer| to it. Caller takes
+// ownership of the returned buffer.
+uint8_t* DuplicateBuffer(const uint8_t* buffer, size_t size) {
+    uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(size));
+    if (tmp) {
+        memcpy(tmp, buffer, size);
+    }
+    return tmp;
+}
 
-    if (error_ != KM_ERROR_OK)
+template <typename RequestType>
+void AddClientAndAppData(const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+                         RequestType* request) {
+    request->additional_params.Clear();
+    if (client_id) {
+        request->additional_params.push_back(TAG_APPLICATION_ID, *client_id);
+    }
+    if (app_data) {
+        request->additional_params.push_back(TAG_APPLICATION_DATA, *app_data);
+    }
+}
+
+}  //  unnamed namespace
+
+keymaster_error_t TrustyKeymasterDevice::configure(const keymaster_key_param_set_t* params) {
+    ALOGD("Device received configure\n");
+
+    if (error_ != KM_ERROR_OK) {
         return error_;
-
-    GenerateKeyRequest req(message_version_);
-    StoreNewKeyParams(&req.key_description);
-
-    switch (key_type) {
-    case TYPE_RSA: {
-        req.key_description.push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA);
-        const keymaster_rsa_keygen_params_t* rsa_params =
-            static_cast<const keymaster_rsa_keygen_params_t*>(key_params);
-        ALOGD("Generating RSA pair, modulus size: %u, public exponent: %lu",
-              rsa_params->modulus_size, rsa_params->public_exponent);
-        req.key_description.push_back(TAG_KEY_SIZE, rsa_params->modulus_size);
-        req.key_description.push_back(TAG_RSA_PUBLIC_EXPONENT, rsa_params->public_exponent);
-        break;
+    }
+    if (!params) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
     }
 
-    case TYPE_EC: {
-        req.key_description.push_back(TAG_ALGORITHM, KM_ALGORITHM_EC);
-        const keymaster_ec_keygen_params_t* ec_params =
-            static_cast<const keymaster_ec_keygen_params_t*>(key_params);
-        ALOGD("Generating ECDSA pair, key size: %u", ec_params->field_size);
-        req.key_description.push_back(TAG_KEY_SIZE, ec_params->field_size);
-        break;
-    }
-    default:
-        ALOGD("Received request for unsuported key type %d", key_type);
-        return KM_ERROR_UNSUPPORTED_ALGORITHM;
+    AuthorizationSet params_copy(*params);
+    ConfigureRequest request;
+    if (!params_copy.GetTagValue(TAG_OS_VERSION, &request.os_version) ||
+        !params_copy.GetTagValue(TAG_OS_PATCHLEVEL, &request.os_patchlevel)) {
+        ALOGD("Configuration parameters must contain OS version and patch level");
+        return KM_ERROR_INVALID_ARGUMENT;
     }
 
-    GenerateKeyResponse rsp(message_version_);
-    ALOGD("Sending generate request");
-    keymaster_error_t err = Send(req, &rsp);
+    ConfigureResponse response;
+    keymaster_error_t err = Send(KM_CONFIGURE, request, &response);
     if (err != KM_ERROR_OK) {
-        ALOGE("Got error %d from send", err);
         return err;
     }
 
-    *key_blob_length = rsp.key_blob.key_material_size;
-    *key_blob = static_cast<uint8_t*>(malloc(*key_blob_length));
-    memcpy(*key_blob, rsp.key_blob.key_material, *key_blob_length);
-    ALOGD("Returning %d bytes in key blob\n", (int)*key_blob_length);
-
     return KM_ERROR_OK;
 }
 
-struct EVP_PKEY_Delete {
-    void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
-};
+keymaster_error_t TrustyKeymasterDevice::add_rng_entropy(const uint8_t* data, size_t data_length) {
+    ALOGD("Device received add_rng_entropy");
 
-struct PKCS8_PRIV_KEY_INFO_Delete {
-    void operator()(PKCS8_PRIV_KEY_INFO* p) const { PKCS8_PRIV_KEY_INFO_free(p); }
-};
-
-int TrustyKeymasterDevice::import_keypair(const uint8_t* key, const size_t key_length,
-                                          uint8_t** key_blob, size_t* key_blob_length) {
-    ALOGD("Device received import_keypair");
-    if (error_ != KM_ERROR_OK)
+    if (error_ != KM_ERROR_OK) {
         return error_;
+    }
 
-    if (!key)
+    AddEntropyRequest request;
+    request.random_data.Reinitialize(data, data_length);
+    AddEntropyResponse response;
+    return Send(KM_ADD_RNG_ENTROPY, request, &response);
+}
+
+keymaster_error_t TrustyKeymasterDevice::generate_key(
+    const keymaster_key_param_set_t* params, keymaster_key_blob_t* key_blob,
+    keymaster_key_characteristics_t* characteristics) {
+    ALOGD("Device received generate_key");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!params) {
         return KM_ERROR_UNEXPECTED_NULL_POINTER;
-
-    if (!key_blob || !key_blob_length)
+    }
+    if (!key_blob) {
         return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    GenerateKeyRequest request(message_version_);
+    request.key_description.Reinitialize(*params);
+    request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+
+    GenerateKeyResponse response(message_version_);
+    keymaster_error_t err = Send(KM_GENERATE_KEY, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    key_blob->key_material_size = response.key_blob.key_material_size;
+    key_blob->key_material =
+        DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+    if (!key_blob->key_material) {
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
+
+    if (characteristics) {
+        response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+        response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+    }
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
+    const keymaster_key_blob_t* key_blob, const keymaster_blob_t* client_id,
+    const keymaster_blob_t* app_data, keymaster_key_characteristics_t* characteristics) {
+    ALOGD("Device received get_key_characteristics");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!key_blob || !key_blob->key_material) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!characteristics) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    GetKeyCharacteristicsRequest request;
+    request.SetKeyMaterial(*key_blob);
+    AddClientAndAppData(client_id, app_data, &request);
+
+    GetKeyCharacteristicsResponse response;
+    keymaster_error_t err = Send(KM_GET_KEY_CHARACTERISTICS, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+    response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::import_key(
+    const keymaster_key_param_set_t* params, keymaster_key_format_t key_format,
+    const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
+    keymaster_key_characteristics_t* characteristics) {
+    ALOGD("Device received import_key");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!params || !key_data) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!key_blob) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
 
     ImportKeyRequest request(message_version_);
-    StoreNewKeyParams(&request.key_description);
-    keymaster_algorithm_t algorithm;
-    keymaster_error_t err = GetPkcs8KeyAlgorithm(key, key_length, &algorithm);
-    if (err != KM_ERROR_OK)
-        return err;
-    request.key_description.push_back(TAG_ALGORITHM, algorithm);
+    request.key_description.Reinitialize(*params);
+    request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
 
-    request.SetKeyMaterial(key, key_length);
-    request.key_format = KM_KEY_FORMAT_PKCS8;
+    request.key_format = key_format;
+    request.SetKeyMaterial(key_data->data, key_data->data_length);
+
     ImportKeyResponse response(message_version_);
-    err = Send(request, &response);
-    if (err != KM_ERROR_OK)
+    keymaster_error_t err = Send(KM_IMPORT_KEY, request, &response);
+    if (err != KM_ERROR_OK) {
         return err;
+    }
 
-    *key_blob_length = response.key_blob.key_material_size;
-    *key_blob = static_cast<uint8_t*>(malloc(*key_blob_length));
-    memcpy(*key_blob, response.key_blob.key_material, *key_blob_length);
-    printf("Returning %d bytes in key blob\n", (int)*key_blob_length);
+    key_blob->key_material_size = response.key_blob.key_material_size;
+    key_blob->key_material =
+        DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+    if (!key_blob->key_material) {
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
+
+    if (characteristics) {
+        response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+        response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+    }
 
     return KM_ERROR_OK;
 }
 
-keymaster_error_t TrustyKeymasterDevice::GetPkcs8KeyAlgorithm(const uint8_t* key, size_t key_length,
-                                                              keymaster_algorithm_t* algorithm) {
-    if (key == NULL) {
-        ALOGE("No key specified for import");
+keymaster_error_t TrustyKeymasterDevice::export_key(keymaster_key_format_t export_format,
+                                                    const keymaster_key_blob_t* key_to_export,
+                                                    const keymaster_blob_t* client_id,
+                                                    const keymaster_blob_t* app_data,
+                                                    keymaster_blob_t* export_data) {
+    ALOGD("Device received export_key");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!key_to_export || !key_to_export->key_material) {
         return KM_ERROR_UNEXPECTED_NULL_POINTER;
     }
-
-    UniquePtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> pkcs8(
-        d2i_PKCS8_PRIV_KEY_INFO(NULL, &key, key_length));
-    if (pkcs8.get() == NULL) {
-        ALOGE("Could not parse PKCS8 key blob");
-        return KM_ERROR_INVALID_KEY_BLOB;
+    if (!export_data) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
     }
 
-    UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKCS82PKEY(pkcs8.get()));
-    if (pkey.get() == NULL) {
-        ALOGE("Could not extract key from PKCS8 key blob");
-        return KM_ERROR_INVALID_KEY_BLOB;
-    }
-
-    switch (EVP_PKEY_type(pkey->type)) {
-    case EVP_PKEY_RSA:
-        *algorithm = KM_ALGORITHM_RSA;
-        break;
-    case EVP_PKEY_EC:
-        *algorithm = KM_ALGORITHM_EC;
-        break;
-    default:
-        ALOGE("Unsupported algorithm %d", EVP_PKEY_type(pkey->type));
-        return KM_ERROR_UNSUPPORTED_ALGORITHM;
-    }
-
-    return KM_ERROR_OK;
-}
-
-int TrustyKeymasterDevice::get_keypair_public(const uint8_t* key_blob, const size_t key_blob_length,
-                                              uint8_t** x509_data, size_t* x509_data_length) {
-    ALOGD("Device received get_keypair_public");
-    if (error_ != KM_ERROR_OK)
-        return error_;
+    export_data->data = nullptr;
+    export_data->data_length = 0;
 
     ExportKeyRequest request(message_version_);
-    request.SetKeyMaterial(key_blob, key_blob_length);
-    request.key_format = KM_KEY_FORMAT_X509;
+    request.key_format = export_format;
+    request.SetKeyMaterial(*key_to_export);
+    AddClientAndAppData(client_id, app_data, &request);
+
     ExportKeyResponse response(message_version_);
-    keymaster_error_t err = Send(request, &response);
-    if (err != KM_ERROR_OK)
+    keymaster_error_t err = Send(KM_EXPORT_KEY, request, &response);
+    if (err != KM_ERROR_OK) {
         return err;
+    }
 
-    *x509_data_length = response.key_data_length;
-    *x509_data = static_cast<uint8_t*>(malloc(*x509_data_length));
-    memcpy(*x509_data, response.key_data, *x509_data_length);
-    printf("Returning %d bytes in x509 key\n", (int)*x509_data_length);
+    export_data->data_length = response.key_data_length;
+    export_data->data = DuplicateBuffer(response.key_data, response.key_data_length);
+    if (!export_data->data) {
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
 
     return KM_ERROR_OK;
 }
 
-int TrustyKeymasterDevice::sign_data(const void* signing_params, const uint8_t* key_blob,
-                                     const size_t key_blob_length, const uint8_t* data,
-                                     const size_t data_length, uint8_t** signed_data,
-                                     size_t* signed_data_length) {
-    ALOGD("Device received sign_data, %d", error_);
-    if (error_ != KM_ERROR_OK)
+keymaster_error_t TrustyKeymasterDevice::attest_key(const keymaster_key_blob_t* key_to_attest,
+                                                    const keymaster_key_param_set_t* attest_params,
+                                                    keymaster_cert_chain_t* cert_chain) {
+    ALOGD("Device received attest_key");
+
+    if (error_ != KM_ERROR_OK) {
         return error_;
+    }
+    if (!key_to_attest || !attest_params) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!cert_chain) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
 
-    BeginOperationRequest begin_request(message_version_);
-    begin_request.purpose = KM_PURPOSE_SIGN;
-    begin_request.SetKeyMaterial(key_blob, key_blob_length);
-    keymaster_error_t err = StoreSigningParams(signing_params, key_blob, key_blob_length,
-                                               &begin_request.additional_params);
+    cert_chain->entry_count = 0;
+    cert_chain->entries = nullptr;
+
+    AttestKeyRequest request;
+    request.SetKeyMaterial(*key_to_attest);
+    request.attest_params.Reinitialize(*attest_params);
+
+    keymaster_blob_t attestation_challenge = {};
+    request.attest_params.GetTagValue(TAG_ATTESTATION_CHALLENGE, &attestation_challenge);
+    if (attestation_challenge.data_length > kMaximumAttestationChallengeLength) {
+        ALOGE("%zu-byte attestation challenge; only %zu bytes allowed",
+              attestation_challenge.data_length, kMaximumAttestationChallengeLength);
+        return KM_ERROR_INVALID_INPUT_LENGTH;
+    }
+
+    AttestKeyResponse response;
+    keymaster_error_t err = Send(KM_ATTEST_KEY, request, &response);
     if (err != KM_ERROR_OK) {
-        ALOGE("Error extracting signing params: %d", err);
         return err;
     }
 
-    BeginOperationResponse begin_response(message_version_);
-    ALOGD("Sending signing request begin");
-    err = Send(begin_request, &begin_response);
-    if (err != KM_ERROR_OK) {
-        ALOGE("Error sending sign begin: %d", err);
-        return err;
+    // Allocate and clear storage for cert_chain.
+    keymaster_cert_chain_t& rsp_chain = response.certificate_chain;
+    cert_chain->entries = reinterpret_cast<keymaster_blob_t*>(
+        malloc(rsp_chain.entry_count * sizeof(*cert_chain->entries)));
+    if (!cert_chain->entries) {
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
+    cert_chain->entry_count = rsp_chain.entry_count;
+    for (keymaster_blob_t& entry : array_range(cert_chain->entries, cert_chain->entry_count)) {
+        entry = {};
     }
 
-    UpdateOperationRequest update_request(message_version_);
-    update_request.op_handle = begin_response.op_handle;
-    update_request.input.Reinitialize(data, data_length);
-    UpdateOperationResponse update_response(message_version_);
-    ALOGD("Sending signing request update");
-    err = Send(update_request, &update_response);
-    if (err != KM_ERROR_OK) {
-        ALOGE("Error sending sign update: %d", err);
-        return err;
+    // Copy cert_chain contents
+    size_t i = 0;
+    for (keymaster_blob_t& entry : array_range(rsp_chain.entries, rsp_chain.entry_count)) {
+        cert_chain->entries[i].data = DuplicateBuffer(entry.data, entry.data_length);
+        if (!cert_chain->entries[i].data) {
+            keymaster_free_cert_chain(cert_chain);
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        }
+        cert_chain->entries[i].data_length = entry.data_length;
+        ++i;
     }
 
-    FinishOperationRequest finish_request(message_version_);
-    finish_request.op_handle = begin_response.op_handle;
-    FinishOperationResponse finish_response(message_version_);
-    ALOGD("Sending signing request finish");
-    err = Send(finish_request, &finish_response);
-    if (err != KM_ERROR_OK) {
-        ALOGE("Error sending sign finish: %d", err);
-        return err;
-    }
-
-    *signed_data_length = finish_response.output.available_read();
-    *signed_data = static_cast<uint8_t*>(malloc(*signed_data_length));
-    if (!finish_response.output.read(*signed_data, *signed_data_length)) {
-        ALOGE("Error reading response data: %d", err);
-        return KM_ERROR_UNKNOWN_ERROR;
-    }
     return KM_ERROR_OK;
 }
 
-int TrustyKeymasterDevice::verify_data(const void* signing_params, const uint8_t* key_blob,
-                                       const size_t key_blob_length, const uint8_t* signed_data,
-                                       const size_t signed_data_length, const uint8_t* signature,
-                                       const size_t signature_length) {
-    ALOGD("Device received verify_data");
-    if (error_ != KM_ERROR_OK)
+keymaster_error_t TrustyKeymasterDevice::upgrade_key(const keymaster_key_blob_t* key_to_upgrade,
+                                                     const keymaster_key_param_set_t* upgrade_params,
+                                                     keymaster_key_blob_t* upgraded_key) {
+    ALOGD("Device received upgrade_key");
+
+    if (error_ != KM_ERROR_OK) {
         return error_;
+    }
+    if (!key_to_upgrade || !upgrade_params) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!upgraded_key) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
 
-    BeginOperationRequest begin_request(message_version_);
-    begin_request.purpose = KM_PURPOSE_VERIFY;
-    begin_request.SetKeyMaterial(key_blob, key_blob_length);
-    keymaster_error_t err = StoreSigningParams(signing_params, key_blob, key_blob_length,
-                                               &begin_request.additional_params);
-    if (err != KM_ERROR_OK)
-        return err;
+    UpgradeKeyRequest request;
+    request.SetKeyMaterial(*key_to_upgrade);
+    request.upgrade_params.Reinitialize(*upgrade_params);
 
-    BeginOperationResponse begin_response(message_version_);
-    err = Send(begin_request, &begin_response);
-    if (err != KM_ERROR_OK)
+    UpgradeKeyResponse response;
+    keymaster_error_t err = Send(KM_UPGRADE_KEY, request, &response);
+    if (err != KM_ERROR_OK) {
         return err;
+    }
 
-    UpdateOperationRequest update_request(message_version_);
-    update_request.op_handle = begin_response.op_handle;
-    update_request.input.Reinitialize(signed_data, signed_data_length);
-    UpdateOperationResponse update_response(message_version_);
-    err = Send(update_request, &update_response);
-    if (err != KM_ERROR_OK)
-        return err;
+    upgraded_key->key_material_size = response.upgraded_key.key_material_size;
+    upgraded_key->key_material = DuplicateBuffer(response.upgraded_key.key_material,
+                                                 response.upgraded_key.key_material_size);
+    if (!upgraded_key->key_material) {
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
 
-    FinishOperationRequest finish_request(message_version_);
-    finish_request.op_handle = begin_response.op_handle;
-    finish_request.signature.Reinitialize(signature, signature_length);
-    FinishOperationResponse finish_response(message_version_);
-    err = Send(finish_request, &finish_response);
-    if (err != KM_ERROR_OK)
-        return err;
     return KM_ERROR_OK;
 }
 
+keymaster_error_t TrustyKeymasterDevice::begin(keymaster_purpose_t purpose,
+                                               const keymaster_key_blob_t* key,
+                                               const keymaster_key_param_set_t* in_params,
+                                               keymaster_key_param_set_t* out_params,
+                                               keymaster_operation_handle_t* operation_handle) {
+    ALOGD("Device received begin");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!key || !key->key_material) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!operation_handle) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    if (out_params) {
+        *out_params = {};
+    }
+
+    BeginOperationRequest request;
+    request.purpose = purpose;
+    request.SetKeyMaterial(*key);
+    request.additional_params.Reinitialize(*in_params);
+
+    BeginOperationResponse response;
+    keymaster_error_t err = Send(KM_BEGIN_OPERATION, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    if (response.output_params.size() > 0) {
+        if (out_params) {
+            response.output_params.CopyToParamSet(out_params);
+        } else {
+            return KM_ERROR_OUTPUT_PARAMETER_NULL;
+        }
+    }
+    *operation_handle = response.op_handle;
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::update(keymaster_operation_handle_t operation_handle,
+                                                const keymaster_key_param_set_t* in_params,
+                                                const keymaster_blob_t* input,
+                                                size_t* input_consumed,
+                                                keymaster_key_param_set_t* out_params,
+                                                keymaster_blob_t* output) {
+    ALOGD("Device received update");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!input) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!input_consumed) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    if (out_params) {
+        *out_params = {};
+    }
+    if (output) {
+        *output = {};
+    }
+
+    UpdateOperationRequest request;
+    request.op_handle = operation_handle;
+    if (in_params) {
+        request.additional_params.Reinitialize(*in_params);
+    }
+    if (input && input->data_length > 0) {
+        size_t max_input_size = SEND_BUF_SIZE - request.SerializedSize();
+        request.input.Reinitialize(input->data, std::min(input->data_length, max_input_size));
+    }
+
+    UpdateOperationResponse response;
+    keymaster_error_t err = Send(KM_UPDATE_OPERATION, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    if (response.output_params.size() > 0) {
+        if (out_params) {
+            response.output_params.CopyToParamSet(out_params);
+        } else {
+            return KM_ERROR_OUTPUT_PARAMETER_NULL;
+        }
+    }
+    *input_consumed = response.input_consumed;
+    if (output) {
+        output->data_length = response.output.available_read();
+        output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
+        if (!output->data) {
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        }
+    } else if (response.output.available_read() > 0) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::finish(keymaster_operation_handle_t operation_handle,
+                                                const keymaster_key_param_set_t* in_params,
+                                                const keymaster_blob_t* input,
+                                                const keymaster_blob_t* signature,
+                                                keymaster_key_param_set_t* out_params,
+                                                keymaster_blob_t* output) {
+    ALOGD("Device received finish");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (input && input->data_length > kMaximumFinishInputLength) {
+        return KM_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (out_params) {
+        *out_params = {};
+    }
+    if (output) {
+        *output = {};
+    }
+
+    FinishOperationRequest request;
+    request.op_handle = operation_handle;
+    if (signature && signature->data && signature->data_length > 0) {
+        request.signature.Reinitialize(signature->data, signature->data_length);
+    }
+    if (input && input->data && input->data_length) {
+        request.input.Reinitialize(input->data, input->data_length);
+    }
+    if (in_params) {
+        request.additional_params.Reinitialize(*in_params);
+    }
+
+    FinishOperationResponse response;
+    keymaster_error_t err = Send(KM_FINISH_OPERATION, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    if (response.output_params.size() > 0) {
+        if (out_params) {
+            response.output_params.CopyToParamSet(out_params);
+        } else {
+            return KM_ERROR_OUTPUT_PARAMETER_NULL;
+        }
+    }
+    if (output) {
+        output->data_length = response.output.available_read();
+        output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
+        if (!output->data) {
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        }
+    } else if (response.output.available_read() > 0) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::abort(keymaster_operation_handle_t operation_handle) {
+    ALOGD("Device received abort");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+
+    AbortOperationRequest request;
+    request.op_handle = operation_handle;
+    AbortOperationResponse response;
+    return Send(KM_ABORT_OPERATION, request, &response);
+}
+
 hw_device_t* TrustyKeymasterDevice::hw_device() {
     return &device_.common;
 }
 
-static inline TrustyKeymasterDevice* convert_device(const keymaster0_device_t* dev) {
-    return reinterpret_cast<TrustyKeymasterDevice*>(const_cast<keymaster0_device_t*>(dev));
+static inline TrustyKeymasterDevice* convert_device(const keymaster2_device_t* dev) {
+    return reinterpret_cast<TrustyKeymasterDevice*>(const_cast<keymaster2_device_t*>(dev));
 }
 
 /* static */
@@ -394,52 +652,111 @@
 }
 
 /* static */
-int TrustyKeymasterDevice::generate_keypair(const keymaster0_device_t* dev,
-                                            const keymaster_keypair_t key_type,
-                                            const void* key_params, uint8_t** keyBlob,
-                                            size_t* keyBlobLength) {
-    ALOGD("Generate keypair, sending to device: %p", convert_device(dev));
-    return convert_device(dev)->generate_keypair(key_type, key_params, keyBlob, keyBlobLength);
+keymaster_error_t TrustyKeymasterDevice::configure(const keymaster2_device_t* dev,
+                                                   const keymaster_key_param_set_t* params) {
+    return convert_device(dev)->configure(params);
 }
 
 /* static */
-int TrustyKeymasterDevice::import_keypair(const keymaster0_device_t* dev, const uint8_t* key,
-                                          const size_t key_length, uint8_t** key_blob,
-                                          size_t* key_blob_length) {
-    return convert_device(dev)->import_keypair(key, key_length, key_blob, key_blob_length);
+keymaster_error_t TrustyKeymasterDevice::add_rng_entropy(const keymaster2_device_t* dev,
+                                                         const uint8_t* data, size_t data_length) {
+    return convert_device(dev)->add_rng_entropy(data, data_length);
 }
 
 /* static */
-int TrustyKeymasterDevice::get_keypair_public(const keymaster0_device_t* dev,
-                                              const uint8_t* key_blob, const size_t key_blob_length,
-                                              uint8_t** x509_data, size_t* x509_data_length) {
-    return convert_device(dev)
-        ->get_keypair_public(key_blob, key_blob_length, x509_data, x509_data_length);
+keymaster_error_t TrustyKeymasterDevice::generate_key(
+    const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+    keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+    return convert_device(dev)->generate_key(params, key_blob, characteristics);
 }
 
 /* static */
-int TrustyKeymasterDevice::sign_data(const keymaster0_device_t* dev, const void* params,
-                                     const uint8_t* keyBlob, const size_t keyBlobLength,
-                                     const uint8_t* data, const size_t dataLength,
-                                     uint8_t** signedData, size_t* signedDataLength) {
-    return convert_device(dev)
-        ->sign_data(params, keyBlob, keyBlobLength, data, dataLength, signedData, signedDataLength);
+keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
+    const keymaster2_device_t* dev, const keymaster_key_blob_t* key_blob,
+    const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+    keymaster_key_characteristics_t* characteristics) {
+    return convert_device(dev)->get_key_characteristics(key_blob, client_id, app_data,
+                                                        characteristics);
 }
 
 /* static */
-int TrustyKeymasterDevice::verify_data(const keymaster0_device_t* dev, const void* params,
-                                       const uint8_t* keyBlob, const size_t keyBlobLength,
-                                       const uint8_t* signedData, const size_t signedDataLength,
-                                       const uint8_t* signature, const size_t signatureLength) {
-    return convert_device(dev)->verify_data(params, keyBlob, keyBlobLength, signedData,
-                                            signedDataLength, signature, signatureLength);
+keymaster_error_t TrustyKeymasterDevice::import_key(
+    const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+    keymaster_key_format_t key_format, const keymaster_blob_t* key_data,
+    keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+    return convert_device(dev)->import_key(params, key_format, key_data, key_blob, characteristics);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::export_key(const keymaster2_device_t* dev,
+                                                    keymaster_key_format_t export_format,
+                                                    const keymaster_key_blob_t* key_to_export,
+                                                    const keymaster_blob_t* client_id,
+                                                    const keymaster_blob_t* app_data,
+                                                    keymaster_blob_t* export_data) {
+    return convert_device(dev)->export_key(export_format, key_to_export, client_id, app_data,
+                                           export_data);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::attest_key(const keymaster2_device_t* dev,
+                                                    const keymaster_key_blob_t* key_to_attest,
+                                                    const keymaster_key_param_set_t* attest_params,
+                                                    keymaster_cert_chain_t* cert_chain) {
+    return convert_device(dev)->attest_key(key_to_attest, attest_params, cert_chain);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::upgrade_key(const keymaster2_device_t* dev,
+                                                     const keymaster_key_blob_t* key_to_upgrade,
+                                                     const keymaster_key_param_set_t* upgrade_params,
+                                                     keymaster_key_blob_t* upgraded_key) {
+    return convert_device(dev)->upgrade_key(key_to_upgrade, upgrade_params, upgraded_key);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::begin(const keymaster2_device_t* dev,
+                                               keymaster_purpose_t purpose,
+                                               const keymaster_key_blob_t* key,
+                                               const keymaster_key_param_set_t* in_params,
+                                               keymaster_key_param_set_t* out_params,
+                                               keymaster_operation_handle_t* operation_handle) {
+    return convert_device(dev)->begin(purpose, key, in_params, out_params, operation_handle);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::update(
+    const keymaster2_device_t* dev, keymaster_operation_handle_t operation_handle,
+    const keymaster_key_param_set_t* in_params, const keymaster_blob_t* input,
+    size_t* input_consumed, keymaster_key_param_set_t* out_params, keymaster_blob_t* output) {
+    return convert_device(dev)->update(operation_handle, in_params, input, input_consumed,
+                                       out_params, output);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::finish(const keymaster2_device_t* dev,
+                                                keymaster_operation_handle_t operation_handle,
+                                                const keymaster_key_param_set_t* in_params,
+                                                const keymaster_blob_t* input,
+                                                const keymaster_blob_t* signature,
+                                                keymaster_key_param_set_t* out_params,
+                                                keymaster_blob_t* output) {
+    return convert_device(dev)->finish(operation_handle, in_params, input, signature, out_params,
+                                       output);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::abort(const keymaster2_device_t* dev,
+                                               keymaster_operation_handle_t operation_handle) {
+    return convert_device(dev)->abort(operation_handle);
 }
 
 keymaster_error_t TrustyKeymasterDevice::Send(uint32_t command, const Serializable& req,
                                               KeymasterResponse* rsp) {
     uint32_t req_size = req.SerializedSize();
-    if (req_size > SEND_BUF_SIZE)
+    if (req_size > SEND_BUF_SIZE) {
         return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
     uint8_t send_buf[SEND_BUF_SIZE];
     Eraser send_buf_eraser(send_buf, SEND_BUF_SIZE);
     req.Serialize(send_buf, send_buf + req_size);
@@ -448,7 +765,7 @@
     uint8_t recv_buf[RECV_BUF_SIZE];
     Eraser recv_buf_eraser(recv_buf, RECV_BUF_SIZE);
     uint32_t rsp_size = RECV_BUF_SIZE;
-    printf("Sending %d byte request\n", (int)req.SerializedSize());
+    ALOGV("Sending %d byte request\n", (int)req.SerializedSize());
     int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size);
     if (rc < 0) {
         ALOGE("tipc error: %d\n", rc);
@@ -458,8 +775,8 @@
         ALOGV("Received %d byte response\n", rsp_size);
     }
 
-    const keymaster_message* msg = (keymaster_message *) recv_buf;
-    const uint8_t *p = msg->payload;
+    const keymaster_message* msg = (keymaster_message*)recv_buf;
+    const uint8_t* p = msg->payload;
     if (!rsp->Deserialize(&p, p + rsp_size)) {
         ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
         return KM_ERROR_UNKNOWN_ERROR;
@@ -470,65 +787,4 @@
     return rsp->error;
 }
 
-keymaster_error_t TrustyKeymasterDevice::StoreSigningParams(const void* signing_params,
-                                                            const uint8_t* key_blob,
-                                                            size_t key_blob_length,
-                                                            AuthorizationSet* auth_set) {
-    uint8_t* pub_key_data;
-    size_t pub_key_data_length;
-    int err = get_keypair_public(&device_, key_blob, key_blob_length, &pub_key_data,
-                                 &pub_key_data_length);
-    if (err < 0) {
-        ALOGE("Error %d extracting public key to determine algorithm", err);
-        return KM_ERROR_INVALID_KEY_BLOB;
-    }
-    UniquePtr<uint8_t, Malloc_Delete> pub_key(pub_key_data);
-
-    const uint8_t* p = pub_key_data;
-    UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(
-        d2i_PUBKEY(nullptr /* allocate new struct */, &p, pub_key_data_length));
-
-    switch (EVP_PKEY_type(pkey->type)) {
-    case EVP_PKEY_RSA: {
-        const keymaster_rsa_sign_params_t* rsa_params =
-            reinterpret_cast<const keymaster_rsa_sign_params_t*>(signing_params);
-        if (rsa_params->digest_type != DIGEST_NONE)
-            return KM_ERROR_UNSUPPORTED_DIGEST;
-        if (rsa_params->padding_type != PADDING_NONE)
-            return KM_ERROR_UNSUPPORTED_PADDING_MODE;
-        if (!auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE) ||
-            !auth_set->push_back(TAG_PADDING, KM_PAD_NONE))
-            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-    } break;
-    case EVP_PKEY_EC: {
-        const keymaster_ec_sign_params_t* ecdsa_params =
-            reinterpret_cast<const keymaster_ec_sign_params_t*>(signing_params);
-        if (ecdsa_params->digest_type != DIGEST_NONE)
-            return KM_ERROR_UNSUPPORTED_DIGEST;
-        if (!auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE))
-            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-    } break;
-    default:
-        return KM_ERROR_UNSUPPORTED_ALGORITHM;
-    }
-    return KM_ERROR_OK;
-}
-
-void TrustyKeymasterDevice::StoreNewKeyParams(AuthorizationSet* auth_set) {
-    auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_SIGN);
-    auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_VERIFY);
-    auth_set->push_back(TAG_ALL_USERS);
-    auth_set->push_back(TAG_NO_AUTH_REQUIRED);
-    uint64_t now = java_time(time(NULL));
-    auth_set->push_back(TAG_CREATION_DATETIME, now);
-    auth_set->push_back(TAG_ORIGINATION_EXPIRE_DATETIME, now + HUNDRED_YEARS);
-    if (message_version_ == 0) {
-        auth_set->push_back(TAG_DIGEST_OLD, KM_DIGEST_NONE);
-        auth_set->push_back(TAG_PADDING_OLD, KM_PAD_NONE);
-    } else {
-        auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE);
-        auth_set->push_back(TAG_PADDING, KM_PAD_NONE);
-    }
-}
-
 }  // namespace keymaster
diff --git a/trusty/keymaster/trusty_keymaster_device.h b/trusty/keymaster/trusty_keymaster_device.h
index 68cf40c..cfada1b 100644
--- a/trusty/keymaster/trusty_keymaster_device.h
+++ b/trusty/keymaster/trusty_keymaster_device.h
@@ -14,19 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
-#define EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+#ifndef TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+#define TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
 
-#include <hardware/keymaster0.h>
-
+#include <hardware/keymaster2.h>
 #include <keymaster/android_keymaster_messages.h>
 
-#include "keymaster_ipc.h"
-
 namespace keymaster {
 
 /**
- * Software OpenSSL-based Keymaster device.
+ * Trusty Keymaster device.
  *
  * IMPORTANT MAINTAINER NOTE: Pointers to instances of this class must be castable to hw_device_t
  * and keymaster_device. This means it must remain a standard layout class (no virtual functions and
@@ -46,79 +43,111 @@
 
     keymaster_error_t session_error() { return error_; }
 
-    int generate_keypair(const keymaster_keypair_t key_type, const void* key_params,
-                         uint8_t** key_blob, size_t* key_blob_length);
-    int import_keypair(const uint8_t* key, const size_t key_length, uint8_t** key_blob,
-                       size_t* key_blob_length);
-    int get_keypair_public(const uint8_t* key_blob, const size_t key_blob_length,
-                           uint8_t** x509_data, size_t* x509_data_length);
-    int sign_data(const void* signing_params, const uint8_t* key_blob, const size_t key_blob_length,
-                  const uint8_t* data, const size_t data_length, uint8_t** signed_data,
-                  size_t* signed_data_length);
-    int verify_data(const void* signing_params, const uint8_t* key_blob,
-                    const size_t key_blob_length, const uint8_t* signed_data,
-                    const size_t signed_data_length, const uint8_t* signature,
-                    const size_t signature_length);
+    keymaster_error_t configure(const keymaster_key_param_set_t* params);
+    keymaster_error_t add_rng_entropy(const uint8_t* data, size_t data_length);
+    keymaster_error_t generate_key(const keymaster_key_param_set_t* params,
+                                   keymaster_key_blob_t* key_blob,
+                                   keymaster_key_characteristics_t* characteristics);
+    keymaster_error_t get_key_characteristics(const keymaster_key_blob_t* key_blob,
+                                              const keymaster_blob_t* client_id,
+                                              const keymaster_blob_t* app_data,
+                                              keymaster_key_characteristics_t* character);
+    keymaster_error_t import_key(const keymaster_key_param_set_t* params,
+                                 keymaster_key_format_t key_format,
+                                 const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
+                                 keymaster_key_characteristics_t* characteristics);
+    keymaster_error_t export_key(keymaster_key_format_t export_format,
+                                 const keymaster_key_blob_t* key_to_export,
+                                 const keymaster_blob_t* client_id,
+                                 const keymaster_blob_t* app_data, keymaster_blob_t* export_data);
+    keymaster_error_t attest_key(const keymaster_key_blob_t* key_to_attest,
+                                 const keymaster_key_param_set_t* attest_params,
+                                 keymaster_cert_chain_t* cert_chain);
+    keymaster_error_t upgrade_key(const keymaster_key_blob_t* key_to_upgrade,
+                                  const keymaster_key_param_set_t* upgrade_params,
+                                  keymaster_key_blob_t* upgraded_key);
+    keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
+                            const keymaster_key_param_set_t* in_params,
+                            keymaster_key_param_set_t* out_params,
+                            keymaster_operation_handle_t* operation_handle);
+    keymaster_error_t update(keymaster_operation_handle_t operation_handle,
+                             const keymaster_key_param_set_t* in_params,
+                             const keymaster_blob_t* input, size_t* input_consumed,
+                             keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+    keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
+                             const keymaster_key_param_set_t* in_params,
+                             const keymaster_blob_t* input, const keymaster_blob_t* signature,
+                             keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+    keymaster_error_t abort(keymaster_operation_handle_t operation_handle);
 
   private:
     keymaster_error_t Send(uint32_t command, const Serializable& request,
                            KeymasterResponse* response);
-    keymaster_error_t Send(const GenerateKeyRequest& request, GenerateKeyResponse* response) {
-        return Send(KM_GENERATE_KEY, request, response);
-    }
-    keymaster_error_t Send(const BeginOperationRequest& request, BeginOperationResponse* response) {
-        return Send(KM_BEGIN_OPERATION, request, response);
-    }
-    keymaster_error_t Send(const UpdateOperationRequest& request,
-                           UpdateOperationResponse* response) {
-        return Send(KM_UPDATE_OPERATION, request, response);
-    }
-    keymaster_error_t Send(const FinishOperationRequest& request,
-                           FinishOperationResponse* response) {
-        return Send(KM_FINISH_OPERATION, request, response);
-    }
-    keymaster_error_t Send(const ImportKeyRequest& request, ImportKeyResponse* response) {
-        return Send(KM_IMPORT_KEY, request, response);
-    }
-    keymaster_error_t Send(const ExportKeyRequest& request, ExportKeyResponse* response) {
-        return Send(KM_EXPORT_KEY, request, response);
-    }
-    keymaster_error_t Send(const GetVersionRequest& request, GetVersionResponse* response) {
-        return Send(KM_GET_VERSION, request, response);
-    }
-
-    keymaster_error_t StoreSigningParams(const void* signing_params, const uint8_t* key_blob,
-                                         size_t key_blob_length, AuthorizationSet* auth_set);
-    void StoreNewKeyParams(AuthorizationSet* auth_set);
-    keymaster_error_t GetPkcs8KeyAlgorithm(const uint8_t* key, size_t key_length,
-                                           keymaster_algorithm_t* algorithm);
 
     /*
      * These static methods are the functions referenced through the function pointers in
      * keymaster_device.  They're all trivial wrappers.
      */
     static int close_device(hw_device_t* dev);
-    static int generate_keypair(const keymaster0_device_t* dev, const keymaster_keypair_t key_type,
-                                const void* key_params, uint8_t** keyBlob, size_t* keyBlobLength);
-    static int import_keypair(const keymaster0_device_t* dev, const uint8_t* key,
-                              const size_t key_length, uint8_t** key_blob, size_t* key_blob_length);
-    static int get_keypair_public(const keymaster0_device_t* dev, const uint8_t* key_blob,
-                                  const size_t key_blob_length, uint8_t** x509_data,
-                                  size_t* x509_data_length);
-    static int sign_data(const keymaster0_device_t* dev, const void* signing_params,
-                         const uint8_t* key_blob, const size_t key_blob_length, const uint8_t* data,
-                         const size_t data_length, uint8_t** signed_data,
-                         size_t* signed_data_length);
-    static int verify_data(const keymaster0_device_t* dev, const void* signing_params,
-                           const uint8_t* key_blob, const size_t key_blob_length,
-                           const uint8_t* signed_data, const size_t signed_data_length,
-                           const uint8_t* signature, const size_t signature_length);
+    static keymaster_error_t configure(const keymaster2_device_t* dev,
+                                       const keymaster_key_param_set_t* params);
+    static keymaster_error_t add_rng_entropy(const keymaster2_device_t* dev, const uint8_t* data,
+                                             size_t data_length);
+    static keymaster_error_t generate_key(const keymaster2_device_t* dev,
+                                          const keymaster_key_param_set_t* params,
+                                          keymaster_key_blob_t* key_blob,
+                                          keymaster_key_characteristics_t* characteristics);
+    static keymaster_error_t get_key_characteristics(const keymaster2_device_t* dev,
+                                                     const keymaster_key_blob_t* key_blob,
+                                                     const keymaster_blob_t* client_id,
+                                                     const keymaster_blob_t* app_data,
+                                                     keymaster_key_characteristics_t* character);
+    static keymaster_error_t import_key(const keymaster2_device_t* dev,
+                                        const keymaster_key_param_set_t* params,
+                                        keymaster_key_format_t key_format,
+                                        const keymaster_blob_t* key_data,
+                                        keymaster_key_blob_t* key_blob,
+                                        keymaster_key_characteristics_t* characteristics);
+    static keymaster_error_t export_key(const keymaster2_device_t* dev,
+                                        keymaster_key_format_t export_format,
+                                        const keymaster_key_blob_t* key_to_export,
+                                        const keymaster_blob_t* client_id,
+                                        const keymaster_blob_t* app_data,
+                                        keymaster_blob_t* export_data);
+    static keymaster_error_t attest_key(const keymaster2_device_t* dev,
+                                        const keymaster_key_blob_t* key_to_attest,
+                                        const keymaster_key_param_set_t* attest_params,
+                                        keymaster_cert_chain_t* cert_chain);
+    static keymaster_error_t upgrade_key(const keymaster2_device_t* dev,
+                                         const keymaster_key_blob_t* key_to_upgrade,
+                                         const keymaster_key_param_set_t* upgrade_params,
+                                         keymaster_key_blob_t* upgraded_key);
+    static keymaster_error_t delete_key(const keymaster2_device_t* dev,
+                                        const keymaster_key_blob_t* key);
+    static keymaster_error_t delete_all_keys(const keymaster2_device_t* dev);
+    static keymaster_error_t begin(const keymaster2_device_t* dev, keymaster_purpose_t purpose,
+                                   const keymaster_key_blob_t* key,
+                                   const keymaster_key_param_set_t* in_params,
+                                   keymaster_key_param_set_t* out_params,
+                                   keymaster_operation_handle_t* operation_handle);
+    static keymaster_error_t update(const keymaster2_device_t* dev,
+                                    keymaster_operation_handle_t operation_handle,
+                                    const keymaster_key_param_set_t* in_params,
+                                    const keymaster_blob_t* input, size_t* input_consumed,
+                                    keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+    static keymaster_error_t finish(const keymaster2_device_t* dev,
+                                    keymaster_operation_handle_t operation_handle,
+                                    const keymaster_key_param_set_t* in_params,
+                                    const keymaster_blob_t* input, const keymaster_blob_t* signature,
+                                    keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+    static keymaster_error_t abort(const keymaster2_device_t* dev,
+                                   keymaster_operation_handle_t operation_handle);
 
-    keymaster0_device_t device_;
+    keymaster2_device_t device_;
     keymaster_error_t error_;
     int32_t message_version_;
 };
 
 }  // namespace keymaster
 
-#endif  // EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+#endif  // TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
diff --git a/trusty/keymaster/trusty_keymaster_ipc.c b/trusty/keymaster/trusty_keymaster_ipc.cpp
similarity index 75%
rename from trusty/keymaster/trusty_keymaster_ipc.c
rename to trusty/keymaster/trusty_keymaster_ipc.cpp
index 88546af..cdc2778 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.c
+++ b/trusty/keymaster/trusty_keymaster_ipc.cpp
@@ -26,8 +26,8 @@
 #include <log/log.h>
 #include <trusty/tipc.h>
 
-#include "trusty_keymaster_ipc.h"
 #include "keymaster_ipc.h"
+#include "trusty_keymaster_ipc.h"
 
 #define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
 
@@ -43,15 +43,15 @@
     return 0;
 }
 
-int trusty_keymaster_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
-                          uint32_t *out_size)  {
+int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+                          uint32_t* out_size) {
     if (handle_ == 0) {
         ALOGE("not connected\n");
         return -EINVAL;
     }
 
     size_t msg_size = in_size + sizeof(struct keymaster_message);
-    struct keymaster_message *msg = malloc(msg_size);
+    struct keymaster_message* msg = reinterpret_cast<struct keymaster_message*>(malloc(msg_size));
     msg->cmd = cmd;
     memcpy(msg->payload, in, in_size);
 
@@ -59,31 +59,30 @@
     free(msg);
 
     if (rc < 0) {
-        ALOGE("failed to send cmd (%d) to %s: %s\n", cmd,
-                KEYMASTER_PORT, strerror(errno));
+        ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno));
         return -errno;
     }
 
     rc = read(handle_, out, *out_size);
     if (rc < 0) {
-        ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n",
-                cmd, KEYMASTER_PORT, strerror(errno));
+        ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
+              strerror(errno));
         return -errno;
     }
 
-    if ((size_t) rc < sizeof(struct keymaster_message)) {
-        ALOGE("invalid response size (%d)\n", (int) rc);
+    if ((size_t)rc < sizeof(struct keymaster_message)) {
+        ALOGE("invalid response size (%d)\n", (int)rc);
         return -EINVAL;
     }
 
-    msg = (struct keymaster_message *) out;
+    msg = (struct keymaster_message*)out;
 
     if ((cmd | KEYMASTER_RESP_BIT) != msg->cmd) {
         ALOGE("invalid command (%d)", msg->cmd);
         return -EINVAL;
     }
 
-    *out_size = ((size_t) rc) - sizeof(struct keymaster_message);
+    *out_size = ((size_t)rc) - sizeof(struct keymaster_message);
     return rc;
 }
 
@@ -92,4 +91,3 @@
         tipc_close(handle_);
     }
 }
-
diff --git a/trusty/keymaster/trusty_keymaster_ipc.h b/trusty/keymaster/trusty_keymaster_ipc.h
index 9785247..c15f7c1 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.h
+++ b/trusty/keymaster/trusty_keymaster_ipc.h
@@ -14,11 +14,16 @@
  * limitations under the License.
  */
 
+#ifndef TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
+#define TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
+
 __BEGIN_DECLS
 
 int trusty_keymaster_connect(void);
-int trusty_keymaster_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
-         uint32_t *out_size);
+int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+                          uint32_t* out_size);
 void trusty_keymaster_disconnect(void);
 
 __END_DECLS
+
+#endif  // TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
diff --git a/trusty/keymaster/trusty_keymaster_main.cpp b/trusty/keymaster/trusty_keymaster_main.cpp
index 7ed880e..9c2ae2d 100644
--- a/trusty/keymaster/trusty_keymaster_main.cpp
+++ b/trusty/keymaster/trusty_keymaster_main.cpp
@@ -14,7 +14,10 @@
  * limitations under the License.
  */
 
+#include <keymaster/keymaster_configuration.h>
+
 #include <stdio.h>
+#include <memory>
 
 #include <openssl/evp.h>
 #include <openssl/x509.h>
@@ -102,6 +105,28 @@
     0xd1, 0x1f, 0xd4, 0x49, 0x49, 0xe0, 0xb2, 0x18, 0x3b, 0xfe};
 unsigned int ec_privkey_pk8_der_len = 138;
 
+keymaster_key_param_t ec_params[] = {
+    keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_EC),
+    keymaster_param_long(KM_TAG_EC_CURVE, KM_EC_CURVE_P_521),
+    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
+    keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+    keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+};
+keymaster_key_param_set_t ec_param_set = {ec_params, sizeof(ec_params) / sizeof(*ec_params)};
+
+keymaster_key_param_t rsa_params[] = {
+    keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_RSA),
+    keymaster_param_int(KM_TAG_KEY_SIZE, 1024),
+    keymaster_param_long(KM_TAG_RSA_PUBLIC_EXPONENT, 65537),
+    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
+    keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+    keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+    keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+};
+keymaster_key_param_set_t rsa_param_set = {rsa_params, sizeof(rsa_params) / sizeof(*rsa_params)};
+
 struct EVP_PKEY_Delete {
     void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
 };
@@ -110,41 +135,70 @@
     void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
 };
 
+static bool do_operation(TrustyKeymasterDevice* device, keymaster_purpose_t purpose,
+                         keymaster_key_blob_t* key, keymaster_blob_t* input,
+                         keymaster_blob_t* signature, keymaster_blob_t* output) {
+    keymaster_key_param_t params[] = {
+        keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+        keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+    };
+    keymaster_key_param_set_t param_set = {params, sizeof(params) / sizeof(*params)};
+    keymaster_operation_handle_t op_handle;
+    keymaster_error_t error = device->begin(purpose, key, &param_set, nullptr, &op_handle);
+    if (error != KM_ERROR_OK) {
+        printf("Keymaster begin() failed: %d\n", error);
+        return false;
+    }
+    size_t input_consumed;
+    error = device->update(op_handle, nullptr, input, &input_consumed, nullptr, nullptr);
+    if (error != KM_ERROR_OK) {
+        printf("Keymaster update() failed: %d\n", error);
+        return false;
+    }
+    if (input_consumed != input->data_length) {
+        // This should never happen. If it does, it's a bug in the keymaster implementation.
+        printf("Keymaster update() did not consume all data.\n");
+        device->abort(op_handle);
+        return false;
+    }
+    error = device->finish(op_handle, nullptr, nullptr, signature, nullptr, output);
+    if (error != KM_ERROR_OK) {
+        printf("Keymaster finish() failed: %d\n", error);
+        return false;
+    }
+    return true;
+}
+
 static bool test_import_rsa(TrustyKeymasterDevice* device) {
     printf("===================\n");
     printf("= RSA Import Test =\n");
     printf("===================\n\n");
 
     printf("=== Importing RSA keypair === \n");
-    uint8_t* key;
-    size_t size;
-    int error = device->import_keypair(rsa_privkey_pk8_der, rsa_privkey_pk8_der_len, &key, &size);
+    keymaster_key_blob_t key;
+    keymaster_blob_t private_key = {rsa_privkey_pk8_der, rsa_privkey_pk8_der_len};
+    int error = device->import_key(&rsa_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
     if (error != KM_ERROR_OK) {
-        printf("Error importing key pair: %d\n\n", error);
+        printf("Error importing RSA key: %d\n\n", error);
         return false;
     }
-    UniquePtr<uint8_t[]> key_deleter(key);
+    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
 
     printf("=== Signing with imported RSA key ===\n");
-    keymaster_rsa_sign_params_t sign_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = 1024 / 8;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     memset(message.get(), 'a', message_len);
-    uint8_t* signature;
-    size_t signature_len;
-    error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
-                              &signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error signing data with imported RSA key: %d\n\n", error);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with imported RSA key\n\n");
         return false;
     }
-    UniquePtr<uint8_t[]> signature_deleter(signature);
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
 
     printf("=== Verifying with imported RSA key === \n");
-    error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
-                                signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error verifying data with imported RSA key: %d\n\n", error);
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with imported RSA key\n\n");
         return false;
     }
 
@@ -158,67 +212,58 @@
     printf("============\n\n");
 
     printf("=== Generating RSA key pair ===\n");
-    keymaster_rsa_keygen_params_t params;
-    params.public_exponent = 65537;
-    params.modulus_size = 2048;
-
-    uint8_t* key;
-    size_t size;
-    int error = device->generate_keypair(TYPE_RSA, &params, &key, &size);
+    keymaster_key_blob_t key;
+    int error = device->generate_key(&rsa_param_set, &key, nullptr);
     if (error != KM_ERROR_OK) {
         printf("Error generating RSA key pair: %d\n\n", error);
         return false;
     }
-    UniquePtr<uint8_t[]> deleter(key);
+    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
 
     printf("=== Signing with RSA key === \n");
-    keymaster_rsa_sign_params_t sign_params = {DIGEST_NONE, PADDING_NONE};
-    size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+    size_t message_len = 1024 / 8;
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     memset(message.get(), 'a', message_len);
-    uint8_t* signature;
-    size_t signature_len;
-    error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
-                              &signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error signing data with RSA key: %d\n\n", error);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with RSA key\n\n");
         return false;
     }
-    UniquePtr<uint8_t[]> signature_deleter(signature);
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
 
     printf("=== Verifying with RSA key === \n");
-    error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
-                                signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error verifying data with RSA key: %d\n\n", error);
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with RSA key\n\n");
         return false;
     }
 
     printf("=== Exporting RSA public key ===\n");
-    uint8_t* exported_key;
-    size_t exported_size;
-    error = device->get_keypair_public(key, size, &exported_key, &exported_size);
+    keymaster_blob_t exported_key;
+    error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
     if (error != KM_ERROR_OK) {
         printf("Error exporting RSA public key: %d\n\n", error);
         return false;
     }
 
     printf("=== Verifying with exported key ===\n");
-    const uint8_t* tmp = exported_key;
-    UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &tmp, exported_size));
-    UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+    const uint8_t* tmp = exported_key.data;
+    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+        d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
+    std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
     if (EVP_PKEY_verify_init(ctx.get()) != 1) {
-        printf("Error initializing openss EVP context\n");
+        printf("Error initializing openss EVP context\n\n");
         return false;
     }
     if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
-        printf("Exported key was the wrong type?!?\n");
+        printf("Exported key was the wrong type?!?\n\n");
         return false;
     }
 
     EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING);
-    if (EVP_PKEY_verify(ctx.get(), signature, signature_len, message.get(), message_len) != 1) {
-        printf("Verification with exported pubkey failed.\n");
+    if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
+                        message_len) != 1) {
+        printf("Verification with exported pubkey failed.\n\n");
         return false;
     } else {
         printf("Verification succeeded\n");
@@ -234,35 +279,31 @@
     printf("=====================\n\n");
 
     printf("=== Importing ECDSA keypair === \n");
-    uint8_t* key;
-    size_t size;
-    int error = device->import_keypair(ec_privkey_pk8_der, ec_privkey_pk8_der_len, &key, &size);
+    keymaster_key_blob_t key;
+    keymaster_blob_t private_key = {ec_privkey_pk8_der, ec_privkey_pk8_der_len};
+    int error = device->import_key(&ec_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
     if (error != KM_ERROR_OK) {
-        printf("Error importing key pair: %d\n\n", error);
+        printf("Error importing ECDSA key: %d\n\n", error);
         return false;
     }
-    UniquePtr<uint8_t[]> deleter(key);
+    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 */;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     memset(message.get(), 'a', message_len);
-    uint8_t* signature;
-    size_t signature_len;
-    error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
-                              &signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error signing data with imported ECDSA key: %d\n\n", error);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with imported ECDSA key\n\n");
         return false;
     }
-    UniquePtr<uint8_t[]> signature_deleter(signature);
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
 
     printf("=== Verifying with imported ECDSA key === \n");
-    error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
-                                signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error verifying data with imported ECDSA key: %d\n\n", error);
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with imported ECDSA key\n\n");
         return false;
     }
 
@@ -276,64 +317,57 @@
     printf("==============\n\n");
 
     printf("=== Generating ECDSA key pair ===\n");
-    keymaster_ec_keygen_params_t params;
-    params.field_size = 521;
-    uint8_t* key;
-    size_t size;
-    int error = device->generate_keypair(TYPE_EC, &params, &key, &size);
-    if (error != 0) {
+    keymaster_key_blob_t key;
+    int error = device->generate_key(&ec_param_set, &key, nullptr);
+    if (error != KM_ERROR_OK) {
         printf("Error generating ECDSA key pair: %d\n\n", error);
         return false;
     }
-    UniquePtr<uint8_t[]> deleter(key);
+    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
 
     printf("=== Signing with ECDSA key === \n");
-    keymaster_ec_sign_params_t sign_params = {DIGEST_NONE};
     size_t message_len = 30 /* arbitrary */;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     memset(message.get(), 'a', message_len);
-    uint8_t* signature;
-    size_t signature_len;
-    error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
-                              &signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error signing data with ECDSA key: %d\n\n", error);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with ECDSA key\n\n");
         return false;
     }
-    UniquePtr<uint8_t[]> signature_deleter(signature);
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
 
     printf("=== Verifying with ECDSA key === \n");
-    error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
-                                signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error verifying data with ECDSA key: %d\n\n", error);
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with ECDSA key\n\n");
         return false;
     }
 
     printf("=== Exporting ECDSA public key ===\n");
-    uint8_t* exported_key;
-    size_t exported_size;
-    error = device->get_keypair_public(key, size, &exported_key, &exported_size);
+    keymaster_blob_t exported_key;
+    error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
     if (error != KM_ERROR_OK) {
         printf("Error exporting ECDSA public key: %d\n\n", error);
         return false;
     }
 
     printf("=== Verifying with exported key ===\n");
-    const uint8_t* tmp = exported_key;
-    UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &tmp, exported_size));
-    UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+    const uint8_t* tmp = exported_key.data;
+    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+        d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
+    std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
     if (EVP_PKEY_verify_init(ctx.get()) != 1) {
-        printf("Error initializing openss EVP context\n");
+        printf("Error initializing openssl EVP context\n\n");
         return false;
     }
     if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) {
-        printf("Exported key was the wrong type?!?\n");
+        printf("Exported key was the wrong type?!?\n\n");
         return false;
     }
 
-    if (EVP_PKEY_verify(ctx.get(), signature, signature_len, message.get(), message_len) != 1) {
-        printf("Verification with exported pubkey failed.\n");
+    if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
+                        message_len) != 1) {
+        printf("Verification with exported pubkey failed.\n\n");
         return false;
     } else {
         printf("Verification succeeded\n");
@@ -344,8 +378,8 @@
 }
 
 int main(void) {
-
     TrustyKeymasterDevice device(NULL);
+    keymaster::ConfigureDevice(reinterpret_cast<keymaster2_device_t*>(&device));
     if (device.session_error() != KM_ERROR_OK) {
         printf("Failed to initialize Trusty session: %d\n", device.session_error());
         return 1;
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
new file mode 100644
index 0000000..f316da2
--- /dev/null
+++ b/trusty/libtrusty/Android.bp
@@ -0,0 +1,26 @@
+// 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.
+
+subdirs = [
+    "tipc-test",
+]
+
+cc_library {
+    name: "libtrusty",
+
+    srcs: ["trusty.c"],
+    export_include_dirs: ["include"],
+
+    shared_libs: ["liblog"],
+}
diff --git a/trusty/libtrusty/Android.mk b/trusty/libtrusty/Android.mk
deleted file mode 100644
index 45fc079..0000000
--- a/trusty/libtrusty/Android.mk
+++ /dev/null
@@ -1,36 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-# == libtrusty Static library ==
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libtrusty
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := trusty.c
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-
-include $(BUILD_STATIC_LIBRARY)
-
-# ==  libtrusty shared library ==
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libtrusty
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := trusty.c
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := liblog
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp
new file mode 100644
index 0000000..cb00fe7
--- /dev/null
+++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -0,0 +1,26 @@
+// 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.
+
+cc_test {
+    name: "tipc-test",
+    static_executable: true,
+
+    srcs: ["tipc_test.c"],
+    static_libs: [
+        "libc",
+        "libtrusty",
+        "liblog",
+    ],
+    gtest: false,
+}
diff --git a/trusty/libtrusty/tipc-test/Android.mk b/trusty/libtrusty/tipc-test/Android.mk
deleted file mode 100644
index 80030fe..0000000
--- a/trusty/libtrusty/tipc-test/Android.mk
+++ /dev/null
@@ -1,29 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := tipc-test
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := tipc_test.c
-LOCAL_STATIC_LIBRARIES := libc libtrusty liblog
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-include $(BUILD_EXECUTABLE)
diff --git a/trusty/nvram/Android.bp b/trusty/nvram/Android.bp
new file mode 100644
index 0000000..15e6c3e
--- /dev/null
+++ b/trusty/nvram/Android.bp
@@ -0,0 +1,61 @@
+//
+// 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.
+//
+
+// nvram.trusty is the Trusty NVRAM HAL module.
+cc_library_shared {
+    name: "nvram.trusty",
+    relative_install_path: "hw",
+    srcs: [
+        "module.c",
+        "trusty_nvram_device.cpp",
+        "trusty_nvram_implementation.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+        "-fvisibility=hidden",
+    ],
+    static_libs: ["libnvram-hal"],
+    shared_libs: [
+        "libtrusty",
+        "libnvram-messages",
+        "liblog",
+    ],
+}
+
+// nvram-wipe is a helper tool for clearing NVRAM state.
+cc_binary {
+    name: "nvram-wipe",
+    srcs: [
+        "nvram_wipe.cpp",
+        "trusty_nvram_implementation.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+        "-fvisibility=hidden",
+    ],
+    static_libs: ["libnvram-hal"],
+    shared_libs: [
+        "libtrusty",
+        "libnvram-messages",
+        "liblog",
+    ],
+}
diff --git a/trusty/nvram/Android.mk b/trusty/nvram/Android.mk
deleted file mode 100644
index 44e2212..0000000
--- a/trusty/nvram/Android.mk
+++ /dev/null
@@ -1,43 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# nvram.trusty is the Trusty NVRAM HAL module.
-include $(CLEAR_VARS)
-LOCAL_MODULE := nvram.trusty
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SRC_FILES := \
-	module.c \
-	trusty_nvram_device.cpp \
-	trusty_nvram_implementation.cpp
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := -Wall -Werror -Wextra -fvisibility=hidden
-LOCAL_STATIC_LIBRARIES := libnvram-hal
-LOCAL_SHARED_LIBRARIES := libtrusty libnvram-messages liblog
-include $(BUILD_SHARED_LIBRARY)
-
-# nvram-wipe is a helper tool for clearing NVRAM state.
-include $(CLEAR_VARS)
-LOCAL_MODULE := nvram-wipe
-LOCAL_SRC_FILES := \
-	nvram_wipe.cpp \
-	trusty_nvram_implementation.cpp
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := -Wall -Werror -Wextra -fvisibility=hidden
-LOCAL_STATIC_LIBRARIES := libnvram-hal
-LOCAL_SHARED_LIBRARIES := libtrusty libnvram-messages liblog
-include $(BUILD_EXECUTABLE)
diff --git a/trusty/storage/interface/Android.bp b/trusty/storage/interface/Android.bp
new file mode 100644
index 0000000..a551c37
--- /dev/null
+++ b/trusty/storage/interface/Android.bp
@@ -0,0 +1,20 @@
+//
+// 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.
+//
+
+cc_library_static {
+    name: "libtrustystorageinterface",
+    export_include_dirs: ["include"],
+}
diff --git a/trusty/storage/lib/Android.bp b/trusty/storage/lib/Android.bp
new file mode 100644
index 0000000..5eb3f07
--- /dev/null
+++ b/trusty/storage/lib/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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.
+//
+
+cc_library_static {
+    name: "libtrustystorage",
+
+    srcs: ["storage.c"],
+
+    export_include_dirs: ["include"],
+
+    static_libs: [
+        "liblog",
+        "libtrusty",
+        "libtrustystorageinterface",
+    ],
+
+    cflags: [
+        "-fvisibility=hidden",
+        "-Wall",
+        "-Werror",
+    ]
+}
diff --git a/trusty/storage/lib/Android.mk b/trusty/storage/lib/Android.mk
deleted file mode 100644
index 7e0fc9d..0000000
--- a/trusty/storage/lib/Android.mk
+++ /dev/null
@@ -1,37 +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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libtrustystorage
-
-LOCAL_SRC_FILES := \
-	storage.c \
-
-LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
-LOCAL_STATIC_LIBRARIES := \
-	liblog \
-	libtrusty \
-	libtrustystorageinterface
-
-include $(BUILD_STATIC_LIBRARY)
-
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
new file mode 100644
index 0000000..eb34df0
--- /dev/null
+++ b/trusty/storage/proxy/Android.bp
@@ -0,0 +1,38 @@
+//
+// 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.
+//
+
+cc_binary {
+    name: "storageproxyd",
+
+    srcs: [
+        "ipc.c",
+        "rpmb.c",
+        "storage.c",
+        "proxy.c",
+    ],
+
+    shared_libs: ["liblog"],
+
+    static_libs: [
+        "libtrustystorageinterface",
+        "libtrusty",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ]
+}
diff --git a/trusty/storage/proxy/Android.mk b/trusty/storage/proxy/Android.mk
deleted file mode 100644
index 745e302..0000000
--- a/trusty/storage/proxy/Android.mk
+++ /dev/null
@@ -1,41 +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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := storageproxyd
-
-LOCAL_C_INCLUDES += bionic/libc/kernel/uapi
-
-LOCAL_SRC_FILES := \
-	ipc.c \
-	rpmb.c \
-	storage.c \
-	proxy.c
-
-LOCAL_CLFAGS = -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
-	liblog \
-
-LOCAL_STATIC_LIBRARIES := \
-	libtrustystorageinterface \
-	libtrusty
-
-include $(BUILD_EXECUTABLE)
-
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index d645ac0..27e5891 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -229,7 +229,6 @@
 int main(int argc, char *argv[])
 {
     int rc;
-    uint retry_cnt;
 
     /* drop privileges */
     if (drop_privs() < 0)
diff --git a/trusty/storage/tests/Android.bp b/trusty/storage/tests/Android.bp
new file mode 100644
index 0000000..3eff3f2
--- /dev/null
+++ b/trusty/storage/tests/Android.bp
@@ -0,0 +1,36 @@
+//
+// 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.
+//
+
+cc_test {
+    name: "secure-storage-unit-test",
+
+    cflags: [
+        "-g",
+        "-Wall",
+        "-Werror",
+        "-std=gnu++11",
+        "-Wno-missing-field-initializers",
+    ],
+
+    static_libs: [
+        "libtrustystorageinterface",
+        "libtrustystorage",
+        "libtrusty",
+        "liblog",
+    ],
+
+    srcs: ["main.cpp"],
+}
diff --git a/trusty/storage/tests/Android.mk b/trusty/storage/tests/Android.mk
deleted file mode 100644
index 71c904d..0000000
--- a/trusty/storage/tests/Android.mk
+++ /dev/null
@@ -1,29 +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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := secure-storage-unit-test
-LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
-LOCAL_STATIC_LIBRARIES := \
-	libtrustystorageinterface \
-	libtrustystorage \
-	libtrusty \
-	liblog
-LOCAL_SRC_FILES := main.cpp
-include $(BUILD_NATIVE_TEST)
-